From patchwork Thu Sep 14 00:58:33 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Longpeng(Mike)" X-Patchwork-Id: 9952297 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 7A278602C9 for ; Thu, 14 Sep 2017 01:00:37 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 648FE289CC for ; Thu, 14 Sep 2017 01:00:37 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 59243289D2; Thu, 14 Sep 2017 01:00:37 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 697A5289CC for ; Thu, 14 Sep 2017 01:00:34 +0000 (UTC) Received: from localhost ([::1]:45245 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dsIW9-0000SH-45 for patchwork-qemu-devel@patchwork.kernel.org; Wed, 13 Sep 2017 21:00:33 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:43262) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dsIVE-0000Rp-SJ for qemu-devel@nongnu.org; Wed, 13 Sep 2017 20:59:40 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dsIVA-0007de-Sk for qemu-devel@nongnu.org; Wed, 13 Sep 2017 20:59:36 -0400 Received: from szxga04-in.huawei.com ([45.249.212.190]:2321) by eggs.gnu.org with esmtps (TLS1.0:RSA_ARCFOUR_SHA1:16) (Exim 4.71) (envelope-from ) id 1dsIV9-0007XX-DF for qemu-devel@nongnu.org; Wed, 13 Sep 2017 20:59:32 -0400 Received: from 172.30.72.60 (EHLO DGGEMS409-HUB.china.huawei.com) ([172.30.72.60]) by dggrg04-dlp.huawei.com (MOS 4.4.6-GA FastPath queued) with ESMTP id DHE33699; Thu, 14 Sep 2017 08:58:51 +0800 (CST) Received: from [127.0.0.1] (10.177.246.209) by DGGEMS409-HUB.china.huawei.com (10.3.19.209) with Microsoft SMTP Server id 14.3.301.0; Thu, 14 Sep 2017 08:58:40 +0800 Message-ID: <59B9D439.10807@huawei.com> Date: Thu, 14 Sep 2017 08:58:33 +0800 From: "Longpeng (Mike)" User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:11.0) Gecko/20120327 Thunderbird/11.0.1 MIME-Version: 1.0 To: Halil Pasic References: <1505092240-10864-1-git-send-email-longpeng2@huawei.com> <2d8ae3d3-438b-da84-4959-cf63f4f4ce99@linux.vnet.ibm.com> In-Reply-To: <2d8ae3d3-438b-da84-4959-cf63f4f4ce99@linux.vnet.ibm.com> X-Originating-IP: [10.177.246.209] X-CFilter-Loop: Reflected X-Mirapoint-Virus-RAPID-Raw: score=unknown(0), refid=str=0001.0A020203.59B9D44E.0071, ss=1, re=0.000, recu=0.000, reip=0.000, cl=1, cld=1, fgs=0, ip=0.0.0.0, so=2014-11-16 11:51:01, dmn=2013-03-21 17:37:32 X-Mirapoint-Loop-Id: 1b02311657db5d98431d5fb28175974e X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.4.x-2.6.x [generic] [fuzzy] X-Received-From: 45.249.212.190 Subject: Re: [Qemu-devel] [virtio-dev] Re: [RFC 0/8] virtio-crypto: add multiplexing mode support X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: weidong.huang@huawei.com, mst@redhat.com, jasowang@redhat.com, qemu-devel@nongnu.org, john.griffin@intel.com, Varun.Sethi@freescale.com, denglingli@chinamobile.com, arei.gonglei@hotmail.com, virtio-dev@lists.oasis-open.org, agraf@suse.de, arei.gonglei@huawei.com, vincent.jardin@6wind.com, Ola.Liljedahl@arm.com, luonengjun@huawei.com, xin.zeng@intel.com, liang.j.ma@intel.com, stefanha@redhat.com, Jani.Kokkonen@huawei.com, brian.a.keating@intel.com, wangxinxin.wang@huawei.com, cohuck@redhat.com, mike.caraman@nxp.com Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP On 2017/9/14 2:14, Halil Pasic wrote: > > > On 09/11/2017 03:10 AM, Longpeng(Mike) wrote: >> *NOTE* >> The code realization is based on the latest virtio crypto spec: >> [PATCH v19 0/2] virtio-crypto: virtio crypto device specification >> https://lists.nongnu.org/archive/html/qemu-devel/2017-08/msg05217.html >> >> In session mode, the process of create/close a session >> makes we have a least one full round-trip cost from guest to host to guest >> to be able to send any data for symmetric algorithms. It gets ourself into >> synchronization troubles in some scenarios like a web server handling lots >> of small requests whose algorithms and keys are different. >> >> We can support one-blob request (no sessions) as well for symmetric >> algorithms, including HASH, MAC services. The benefit is obvious for >> HASH service because it's usually a one-blob operation. >> > > Hi! > > I've just started looking at this. Patch #1 modifies linux/virtio_crypto.h > which if I compare with the (almost) latest linux master is different. Thus > I would expect a corresponding kernel patch set too, but I haven't received > one, nor did I find a reference in the cover letter. > > I think if I want to test the new features I need the kernel counter-part > too, or? > > Could you point me to the kernel counterpart? > Hi Halil, We haven't implemented the kernel frontend part yet, but there's a testcase based on qtest, you can use it. Please see the attachment. diff --git a/docs/specs/pci-ids.txt b/docs/specs/pci-ids.txt index bb99a02..61877b7 100755 --- a/docs/specs/pci-ids.txt +++ b/docs/specs/pci-ids.txt @@ -22,6 +22,7 @@ maintained as part of the virtio specification. 1af4:1004 SCSI host bus adapter device (legacy) 1af4:1005 entropy generator device (legacy) 1af4:1009 9p filesystem device (legacy) +1af4:1014 crypto device (legacy) 1af4:1041 network device (modern) 1af4:1042 block device (modern) @@ -32,6 +33,7 @@ maintained as part of the virtio specification. 1af4:1049 9p filesystem device (modern) 1af4:1050 virtio gpu device (modern) 1af4:1052 virtio input device (modern) +1af4:1054 crypto device (modern) 1af4:10f0 Available for experimental usage without registration. Must get to official ID when the code leaves the test lab (i.e. when seeking diff --git a/hw/virtio/virtio-crypto-pci.c b/hw/virtio/virtio-crypto-pci.c index bf64996..66a2966 100755 --- a/hw/virtio/virtio-crypto-pci.c +++ b/hw/virtio/virtio-crypto-pci.c @@ -37,7 +37,6 @@ static void virtio_crypto_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) } qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - virtio_pci_force_virtio_1(vpci_dev); object_property_set_bool(OBJECT(vdev), true, "realized", errp); object_property_set_link(OBJECT(vcrypto), OBJECT(vcrypto->vdev.conf.cryptodev), "cryptodev", @@ -53,6 +52,9 @@ static void virtio_crypto_pci_class_init(ObjectClass *klass, void *data) k->realize = virtio_crypto_pci_realize; set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->props = virtio_crypto_pci_properties; + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_CRYPTO; + pcidev_k->revision = 0; pcidev_k->class_id = PCI_CLASS_OTHERS; } diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index e598b09..ae9327c 100755 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -83,6 +83,8 @@ #define PCI_DEVICE_ID_VIRTIO_RNG 0x1005 #define PCI_DEVICE_ID_VIRTIO_9P 0x1009 #define PCI_DEVICE_ID_VIRTIO_VSOCK 0x1012 +#define PCI_DEVICE_ID_VIRTIO_CRYPTO 0x1014 + #define PCI_VENDOR_ID_REDHAT 0x1b36 #define PCI_DEVICE_ID_REDHAT_BRIDGE 0x0001 diff --git a/tests/Makefile.include b/tests/Makefile.include index 37c1bed..9b6c131 100755 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -192,6 +192,8 @@ check-qtest-virtio-y += tests/virtio-serial-test$(EXESUF) gcov-files-virtio-y += i386-softmmu/hw/char/virtio-serial-bus.c check-qtest-virtio-y += $(check-qtest-virtioserial-y) gcov-files-virtio-y += $(gcov-files-virtioserial-y) +check-qtest-virtio-y += tests/virtio-crypto-test$(EXESUF) +gcov-files-virtio-y += i386-softmmu/hw/virtio/virtio-crypto.c check-qtest-pci-y += tests/e1000-test$(EXESUF) gcov-files-pci-y += hw/net/e1000.c @@ -753,6 +755,7 @@ tests/virtio-scsi-test$(EXESUF): tests/virtio-scsi-test.o $(libqos-virtio-obj-y) tests/virtio-9p-test$(EXESUF): tests/virtio-9p-test.o $(libqos-virtio-obj-y) tests/virtio-serial-test$(EXESUF): tests/virtio-serial-test.o tests/virtio-console-test$(EXESUF): tests/virtio-console-test.o +tests/virtio-crypto-test$(EXESUF): tests/virtio-crypto-test.o $(libqos-virtio-obj-y) tests/tpci200-test$(EXESUF): tests/tpci200-test.o tests/display-vga-test$(EXESUF): tests/display-vga-test.o tests/ipoctal232-test$(EXESUF): tests/ipoctal232-test.o diff --git a/tests/virtio-crypto-test.c b/tests/virtio-crypto-test.c new file mode 100644 index 0000000..8825f1f --- /dev/null +++ b/tests/virtio-crypto-test.c @@ -0,0 +1,600 @@ +/* + * QTest testcase for VirtIO Crypto Device + * + * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. + * + * Authors: + * Gonglei + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/virtio.h" +#include "libqos/virtio-pci.h" +#include "libqos/virtio-mmio.h" +#include "libqos/pci-pc.h" +#include "libqos/malloc.h" +#include "libqos/malloc-pc.h" +#include "libqos/malloc-generic.h" +#include "qemu/bswap.h" +#include "standard-headers/linux/virtio_ids.h" +#include "standard-headers/linux/virtio_config.h" +#include "standard-headers/linux/virtio_ring.h" +#include "standard-headers/linux/virtio_crypto.h" +#include "standard-headers/linux/virtio_pci.h" + +#define QVIRTIO_CRYPTO_TIMEOUT_US (30 * 1000 * 1000) + +#define PCI_SLOT_HP 0x06 +#define PCI_SLOT 0x04 +#define PCI_FN 0x00 + +/* + * VirtIOCryptoCipherTestData: structure to describe a cipher test + * @key: A pointer to a key used by the test + * @key_len: The length of @key + * @iv: A pointer to the IV/Counter used by the test + * @iv_len: The length of @iv + * @input: A pointer to data used as input + * @ilen The length of data in @input + * @output: A pointer to what the test need to produce + * @olen: The length of data in @output + * @algo: The type of algorithm, refer to VIRTIO_CRYPTO_CIPHER_AES_* + */ +typedef struct VirtIOCryptoCipherTestData { + const char *path; + unsigned short algo; + const char *key; + const char *iv; + const char *input; + const char *output; + unsigned char key_len; + unsigned char iv_len; + unsigned short ilen; + unsigned short olen; + bool is_statelss_mode; +} VirtIOCryptoCipherTestData; + + +static VirtIOCryptoCipherTestData cipher_test_data[] = { + { /* From RFC 3602 */ + .path = "/virtio/crypto/cbc(aes-128-session-mode)", + .algo = VIRTIO_CRYPTO_CIPHER_AES_CBC, + .key = "\x06\xa9\x21\x40\x36\xb8\xa1\x5b" + "\x51\x2e\x03\xd5\x34\x12\x00\x06", + .key_len = 16, + .iv = "\x3d\xaf\xba\x42\x9d\x9e\xb4\x30" + "\xb4\x22\xda\x80\x2c\x9f\xac\x41", + .iv_len = 16, + .input = "Single block msg", + .ilen = 16, + .output = "\xe3\x53\x77\x9c\x10\x79\xae\xb8" + "\x27\x08\x94\x2d\xbe\x77\x18\x1a", + .olen = 16, + .is_statelss_mode = false, + }, + { /* From RFC 3602 */ + .path = "/virtio/crypto/cbc(aes-128-stateless-mode)", + .algo = VIRTIO_CRYPTO_CIPHER_AES_CBC, + .key = "\x06\xa9\x21\x40\x36\xb8\xa1\x5b" + "\x51\x2e\x03\xd5\x34\x12\x00\x06", + .key_len = 16, + .iv = "\x3d\xaf\xba\x42\x9d\x9e\xb4\x30" + "\xb4\x22\xda\x80\x2c\x9f\xac\x41", + .iv_len = 16, + .input = "Single block msg", + .ilen = 16, + .output = "\xe3\x53\x77\x9c\x10\x79\xae\xb8" + "\x27\x08\x94\x2d\xbe\x77\x18\x1a", + .olen = 16, + .is_statelss_mode = true, + }, +}; + +static QPCIBus *virtio_crypto_test_start(void) +{ + char *cmdline; + + cmdline = g_strdup_printf( + "-object cryptodev-backend-builtin,id=cryptodev0 " + "-device virtio-crypto-pci,id=crypto0," + "cryptodev=cryptodev0"); + + qtest_start(cmdline); + g_free(cmdline); + + return qpci_init_pc(NULL); +} + +static void test_end(void) +{ + qtest_end(); +} + +static QVirtioPCIDevice *virtio_crypto_pci_init(QPCIBus *bus, int slot) +{ + QVirtioPCIDevice *dev; + + dev = qvirtio_pci_device_find(bus, VIRTIO_ID_CRYPTO); + g_assert(dev != NULL); + g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_CRYPTO); + + qvirtio_pci_device_enable(dev); + qvirtio_reset(&dev->vdev); + qvirtio_set_acknowledge(&dev->vdev); + qvirtio_set_driver(&dev->vdev); + + return dev; +} + +static uint64_t +virtio_crypto_ctrl_request(QGuestAllocator *alloc, + struct virtio_crypto_op_ctrl_req *req) +{ + uint64_t addr; + + addr = guest_alloc(alloc, sizeof(*req)); + + memwrite(addr, req, sizeof(*req)); + + return addr; +} + +static uint64_t +virtio_crypto_data_request(QGuestAllocator *alloc, + struct virtio_crypto_op_data_req *req) +{ + uint64_t addr; + + addr = guest_alloc(alloc, sizeof(*req)); + + memwrite(addr, req, sizeof(*req)); + + return addr; +} + +static void +virtio_crypto_driver_init(QVirtioDevice *dev) +{ + /* Read configure space to get supported crypto services */ + + qvirtio_set_driver_ok(dev); +} + +static uint64_t +virtio_crypto_create_session(QVirtioDevice *dev, + QGuestAllocator *alloc, QVirtQueue *vq, + VirtIOCryptoCipherTestData *data, + int encrypt) +{ + uint32_t free_head; + struct virtio_crypto_op_ctrl_req ctrl; + struct virtio_crypto_session_input input; + uint32_t key_len = data->key_len; + uint64_t req_addr; + uint64_t key_addr, input_addr; /* cipher key guest physical address */ + uint64_t session_id; + QVRingIndirectDesc *indirect; + + /* Create an encryption session */ + ctrl.header.opcode = VIRTIO_CRYPTO_CIPHER_CREATE_SESSION; + ctrl.header.algo = data->algo; + /* Set the default dataqueue id to 0 */ + ctrl.header.queue_id = 0; + + /* Pad cipher's parameters */ + ctrl.u.sym_create_session.op_type = VIRTIO_CRYPTO_SYM_OP_CIPHER; + ctrl.u.sym_create_session.u.cipher.para.algo = ctrl.header.algo; + ctrl.u.sym_create_session.u.cipher.para.keylen = key_len; + if (encrypt) { + ctrl.u.sym_create_session.u.cipher.para.op = VIRTIO_CRYPTO_OP_ENCRYPT; + } else { + ctrl.u.sym_create_session.u.cipher.para.op = VIRTIO_CRYPTO_OP_DECRYPT; + } + + req_addr = virtio_crypto_ctrl_request(alloc, &ctrl); + + /* Pad cipher's output data */ + key_addr = guest_alloc(alloc, key_len); + memwrite(key_addr, data->key, key_len); + + input.status = VIRTIO_CRYPTO_ERR; + input_addr = guest_alloc(alloc, sizeof(input)); + memwrite(input_addr, &input, sizeof(input)); + + indirect = qvring_indirect_desc_setup(dev, alloc, 3); + qvring_indirect_desc_add(indirect, req_addr, sizeof(ctrl), false); + qvring_indirect_desc_add(indirect, key_addr, key_len, false); + qvring_indirect_desc_add(indirect, input_addr, sizeof(input), true); + free_head = qvirtqueue_add_indirect(vq, indirect); + + qvirtqueue_kick(dev, vq, free_head); + + qvirtio_wait_queue_isr(dev, vq, QVIRTIO_CRYPTO_TIMEOUT_US); + + /* calculate the offset of input data */ + + memread(input_addr, &input, sizeof(input)); + + /* Verify the result */ + g_assert_cmpint(input.status, ==, VIRTIO_CRYPTO_OK); + + session_id = input.session_id; + + g_free(indirect); + guest_free(alloc, input_addr); + guest_free(alloc, key_addr); + guest_free(alloc, req_addr); + + return session_id; +} + +static void +virtio_crypto_close_session(QVirtioDevice *dev, + QGuestAllocator *alloc, QVirtQueue *vq, + uint64_t session_id) +{ + uint32_t free_head; + struct virtio_crypto_op_ctrl_req ctrl; + uint64_t req_addr, status_addr; + uint8_t status; + QVRingIndirectDesc *indirect; + + /* Create an encryption session */ + ctrl.header.opcode = VIRTIO_CRYPTO_CIPHER_DESTROY_SESSION; + /* Set the default dataqueue id to 0 */ + ctrl.header.queue_id = 0; + + ctrl.u.destroy_session.session_id = session_id; + + req_addr = virtio_crypto_ctrl_request(alloc, &ctrl); + + status_addr = guest_alloc(alloc, sizeof(status)); + writel(status_addr, VIRTIO_CRYPTO_ERR); + + indirect = qvring_indirect_desc_setup(dev, alloc, 2); + qvring_indirect_desc_add(indirect, req_addr, sizeof(ctrl), false); + qvring_indirect_desc_add(indirect, status_addr, sizeof(status), true); + free_head = qvirtqueue_add_indirect(vq, indirect); + + qvirtqueue_kick(dev, vq, free_head); + + qvirtio_wait_queue_isr(dev, vq, QVIRTIO_CRYPTO_TIMEOUT_US); + + /* Verify the result */ + status = readl(status_addr); + g_assert_cmpint(status, ==, VIRTIO_CRYPTO_OK); + + g_free(indirect); + guest_free(alloc, req_addr); + guest_free(alloc, status_addr); +} + + +static void +virtio_crypto_test_cipher_session_mode(QVirtioDevice *dev, + QGuestAllocator *alloc, QVirtQueue *ctrlq, + QVirtQueue *vq, VirtIOCryptoCipherTestData *data, + int encrypt) +{ + uint32_t free_head; + struct virtio_crypto_op_data_req req; + uint64_t req_addr, status_addr; + uint64_t iv_addr = 0, src_addr, dst_addr; + uint64_t session_id; + char *output; + uint32_t src_len, dst_len; + uint8_t status; + QVRingIndirectDesc *indirect; + uint8_t entry_num; + + /* Create a session */ + session_id = virtio_crypto_create_session(dev, alloc, + ctrlq, data, encrypt); + + /* Head of operation */ + req.header.session_id = session_id; + if (encrypt) { + req.header.opcode = VIRTIO_CRYPTO_CIPHER_ENCRYPT; + } else { + req.header.opcode = VIRTIO_CRYPTO_CIPHER_DECRYPT; + } + + req.u.sym_req.op_type = VIRTIO_CRYPTO_SYM_OP_CIPHER; + req.u.sym_req.u.cipher.para.iv_len = data->iv_len; + req.u.sym_req.u.cipher.para.src_data_len = data->ilen; + req.u.sym_req.u.cipher.para.dst_data_len = data->olen; + + req_addr = virtio_crypto_data_request(alloc, &req); + + /* IV */ + if (data->iv_len > 0) { + iv_addr = guest_alloc(alloc, data->iv_len); + memwrite(iv_addr, data->iv, data->iv_len); + + /* header + iv + src + dst + status */ + entry_num = 5; + } else { + /* header + src + dst + status */ + entry_num = 4; + } + + if (encrypt) { + src_len = data->ilen; + dst_len = data->olen; + /* Source data is the input data which is a single buffer */ + src_addr = guest_alloc(alloc, src_len); + memwrite(src_addr, data->input, src_len); + } else { + src_len = data->olen; + dst_len = data->ilen; + /* Source data is the output data which is a single buffer */ + src_addr = guest_alloc(alloc, src_len); + memwrite(src_addr, data->output, src_len); + } + + dst_addr = guest_alloc(alloc, dst_len); + + status_addr = guest_alloc(alloc, sizeof(status)); + writel(status_addr, VIRTIO_CRYPTO_ERR); + + /* Allocate descripto table entries */ + indirect = qvring_indirect_desc_setup(dev, alloc, entry_num); + qvring_indirect_desc_add(indirect, req_addr, sizeof(req), false); + if (data->iv_len > 0) { + qvring_indirect_desc_add(indirect, iv_addr, data->iv_len, false); + } + qvring_indirect_desc_add(indirect, src_addr, src_len, false); + qvring_indirect_desc_add(indirect, dst_addr, dst_len, true); + qvring_indirect_desc_add(indirect, status_addr, sizeof(status), true); + free_head = qvirtqueue_add_indirect(vq, indirect); + + qvirtqueue_kick(dev, vq, free_head); + + qvirtio_wait_queue_isr(dev, vq, QVIRTIO_CRYPTO_TIMEOUT_US); + + /* Verify the result */ + status = readl(status_addr); + g_assert_cmpint(status, ==, VIRTIO_CRYPTO_OK); + + output = g_malloc0(dst_len); + memread(dst_addr, output, dst_len); + if (encrypt) { + g_assert_cmpstr(output, ==, data->output); + } else { + g_assert_cmpstr(output, ==, data->input); + } + g_free(output); + + g_free(indirect); + + if (data->iv_len > 0) { + guest_free(alloc, iv_addr); + } + guest_free(alloc, src_addr); + guest_free(alloc, dst_addr); + guest_free(alloc, req_addr); + guest_free(alloc, status_addr); + + /* Close the session */ + virtio_crypto_close_session(dev, alloc, ctrlq, session_id); +} + +static void +virtio_crypto_test_cipher_stateless_mode(QVirtioDevice *dev, + QGuestAllocator *alloc, + QVirtQueue *vq, VirtIOCryptoCipherTestData *data, + int encrypt) +{ + uint32_t free_head; + struct virtio_crypto_op_data_req_mux req; + struct virtio_crypto_sym_data_req_stateless para; + uint64_t req_addr, para_addr, status_addr; + uint64_t iv_addr = 0, src_addr, dst_addr, key_addr; + char *output; + uint32_t src_len, dst_len; + uint8_t status; + QVRingIndirectDesc *indirect; + uint8_t entry_num; + + /* Head of operation */ + req.header.flag = 0; + if (encrypt) { + req.header.opcode = VIRTIO_CRYPTO_CIPHER_ENCRYPT; + para.u.cipher.para.sess_para.op = VIRTIO_CRYPTO_OP_ENCRYPT; + } else { + req.header.opcode = VIRTIO_CRYPTO_CIPHER_DECRYPT; + para.u.cipher.para.sess_para.op = VIRTIO_CRYPTO_OP_DECRYPT; + } + + para.op_type = VIRTIO_CRYPTO_SYM_OP_CIPHER; + para.u.cipher.para.sess_para.algo = data->algo; + para.u.cipher.para.sess_para.keylen = data->key_len; + para.u.cipher.para.iv_len = data->iv_len; + para.u.cipher.para.src_data_len = data->ilen; + para.u.cipher.para.dst_data_len = data->olen; + + req_addr = guest_alloc(alloc, sizeof(req)); + memwrite(req_addr, &req, sizeof(req)); + + para_addr = guest_alloc(alloc, sizeof(para)); + memwrite(para_addr, ¶, sizeof(para)); + + g_assert(data->key_len > 0); + key_addr = guest_alloc(alloc, data->key_len); + memwrite(key_addr, data->key, data->key_len); + + /* IV */ + if (data->iv_len > 0) { + iv_addr = guest_alloc(alloc, data->iv_len); + memwrite(iv_addr, data->iv, data->iv_len); + + /* header + key + iv + src + dst + status */ + entry_num = 7; + } else { + /* header + key + src + dst + status */ + entry_num = 6; + } + + if (encrypt) { + src_len = data->ilen; + dst_len = data->olen; + /* Source data is the input data which is a single buffer */ + src_addr = guest_alloc(alloc, src_len); + memwrite(src_addr, data->input, src_len); + } else { + src_len = data->olen; + dst_len = data->ilen; + /* Source data is the output data which is a single buffer */ + src_addr = guest_alloc(alloc, src_len); + memwrite(src_addr, data->output, src_len); + } + + dst_addr = guest_alloc(alloc, dst_len); + + status_addr = guest_alloc(alloc, sizeof(status)); + writel(status_addr, VIRTIO_CRYPTO_ERR); + + /* Allocate desc table entries */ + indirect = qvring_indirect_desc_setup(dev, alloc, entry_num); + qvring_indirect_desc_add(indirect, req_addr, sizeof(req), false); + qvring_indirect_desc_add(indirect, para_addr, sizeof(para), false); + qvring_indirect_desc_add(indirect, key_addr, data->key_len, false); + if (data->iv_len > 0) { + qvring_indirect_desc_add(indirect, iv_addr, data->iv_len, false); + } + qvring_indirect_desc_add(indirect, src_addr, src_len, false); + qvring_indirect_desc_add(indirect, dst_addr, dst_len, true); + qvring_indirect_desc_add(indirect, status_addr, sizeof(status), true); + free_head = qvirtqueue_add_indirect(vq, indirect); + + qvirtqueue_kick(dev, vq, free_head); + + qvirtio_wait_queue_isr(dev, vq, QVIRTIO_CRYPTO_TIMEOUT_US); + + /* Verify the result */ + status = readl(status_addr); + g_assert_cmpint(status, ==, VIRTIO_CRYPTO_OK); + + output = g_malloc0(dst_len); + memread(dst_addr, output, dst_len); + if (encrypt) { + g_assert_cmpstr(output, ==, data->output); + } else { + g_assert_cmpstr(output, ==, data->input); + } + g_free(output); + + g_free(indirect); + guest_free(alloc, key_addr); + if (data->iv_len > 0) { + guest_free(alloc, iv_addr); + } + guest_free(alloc, src_addr); + guest_free(alloc, dst_addr); + guest_free(alloc, para_addr); + guest_free(alloc, req_addr); + guest_free(alloc, status_addr); +} + +static void +virtio_crypto_test_cipher(QVirtioDevice *dev, + QGuestAllocator *alloc, QVirtQueue *ctrlq, + QVirtQueue *dataq, VirtIOCryptoCipherTestData *data, + int encrypt) +{ + if (!data->is_statelss_mode) { + virtio_crypto_test_cipher_session_mode(dev, alloc, + ctrlq, dataq, data, encrypt); + } else { + virtio_crypto_test_cipher_stateless_mode(dev, alloc, + dataq, data, encrypt); + } +} + +static void virtio_crypto_pci_basic(void *opaque) +{ + VirtIOCryptoCipherTestData *test_data = opaque; + QVirtioPCIDevice *dev; + QPCIBus *bus; + QGuestAllocator *alloc; + QVirtQueuePCI *dataq, *controlq; + uint32_t features; + + bus = virtio_crypto_test_start(); + dev = virtio_crypto_pci_init(bus, PCI_SLOT); + + alloc = pc_alloc_init(); + + features = qvirtio_get_features(&dev->vdev); + g_assert_cmphex(features & (1u << VIRTIO_RING_F_INDIRECT_DESC), !=, 0); + + if (!test_data->is_statelss_mode) { + features = features & ~(QVIRTIO_F_BAD_FEATURE | + (1u << VIRTIO_RING_F_EVENT_IDX | + 1u << VIRTIO_CRYPTO_F_MUX_MODE | + 1u << VIRTIO_CRYPTO_F_CIPHER_STATELESS_MODE)); + } else { + features = features & ~(QVIRTIO_F_BAD_FEATURE | + (1u << VIRTIO_RING_F_EVENT_IDX)); + } + qvirtio_set_features(&dev->vdev, features); + + dataq = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, + alloc, 0); + controlq = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, + alloc, 1); + + virtio_crypto_driver_init(&dev->vdev); + + /* Step 1: Encryption */ + virtio_crypto_test_cipher(&dev->vdev, alloc, + &controlq->vq, &dataq->vq, + test_data, 1); + /* Step 2: Decryption */ + virtio_crypto_test_cipher(&dev->vdev, alloc, + &controlq->vq, &dataq->vq, + test_data, 0); + + /* End test */ + guest_free(alloc, dataq->vq.desc); + guest_free(alloc, controlq->vq.desc); + pc_alloc_uninit(alloc); + qvirtio_pci_device_disable(dev); + g_free(dev); + qpci_free_pc(bus); + test_end(); +} + +int main(int argc, char **argv) +{ + const char *qemu; + const char *arch; + int i, ret; + + qemu = getenv("QTEST_QEMU_BINARY"); + if (qemu == NULL) { + ret = setenv("QTEST_QEMU_BINARY", + "x86_64-softmmu/qemu-system-x86_64", 0); + g_assert(ret == 0); + } + + arch = qtest_get_arch(); + + g_test_init(&argc, &argv, NULL); + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + for (i = 0; i < G_N_ELEMENTS(cipher_test_data); i++) { + g_test_add_data_func(cipher_test_data[i].path, + (void *)&cipher_test_data[i], + (GTestDataFunc)virtio_crypto_pci_basic); + } + } + + return g_test_run(); +}