From patchwork Mon Oct 10 08:43:44 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Gonglei (Arei)" X-Patchwork-Id: 9369143 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 34682607FD for ; Mon, 10 Oct 2016 08:45:35 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 22D7228F83 for ; Mon, 10 Oct 2016 08:45:35 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 15CAF29385; Mon, 10 Oct 2016 08:45:35 +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 AEBA628F83 for ; Mon, 10 Oct 2016 08:45:33 +0000 (UTC) Received: from localhost ([::1]:48110 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1btWDE-0004xm-EQ for patchwork-qemu-devel@patchwork.kernel.org; Mon, 10 Oct 2016 04:45:32 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:45203) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1btWCd-0004v0-2t for qemu-devel@nongnu.org; Mon, 10 Oct 2016 04:44:56 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1btWCY-0007uy-Rg for qemu-devel@nongnu.org; Mon, 10 Oct 2016 04:44:54 -0400 Received: from szxga02-in.huawei.com ([119.145.14.65]:59080) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1btWCX-0007tX-Ql for qemu-devel@nongnu.org; Mon, 10 Oct 2016 04:44:50 -0400 Received: from 172.24.1.60 (EHLO szxeml430-hub.china.huawei.com) ([172.24.1.60]) by szxrg02-dlp.huawei.com (MOS 4.3.7-GA FastPath queued) with ESMTP id DOJ71790; Mon, 10 Oct 2016 16:44:08 +0800 (CST) Received: from localhost (10.177.18.62) by szxeml430-hub.china.huawei.com (10.82.67.185) with Microsoft SMTP Server id 14.3.235.1; Mon, 10 Oct 2016 16:44:00 +0800 From: Gonglei To: , Date: Mon, 10 Oct 2016 16:43:44 +0800 Message-ID: <1476089024-68684-13-git-send-email-arei.gonglei@huawei.com> X-Mailer: git-send-email 2.6.3.windows.1 In-Reply-To: <1476089024-68684-1-git-send-email-arei.gonglei@huawei.com> References: <1476089024-68684-1-git-send-email-arei.gonglei@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.177.18.62] X-CFilter-Loop: Reflected X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.4.x-2.6.x [generic] X-Received-From: 119.145.14.65 Subject: [Qemu-devel] [PATCH v6 12/12] virtio-crypto-test: add qtest case for virtio-crypto 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, claudio.fontana@huawei.com, mst@redhat.com, xin.zeng@intel.com, hanweidong@huawei.com, luonengjun@huawei.com, agraf@suse.de, nmorey@kalray.eu, mike.caraman@nxp.com, Gonglei , stefanha@redhat.com, jianjay.zhou@huawei.com, pbonzini@redhat.com, peter.huangpeng@huawei.com, vincent.jardin@6wind.com, wu.wubin@huawei.com, arei.gonglei@hotmail.com Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP We can simply test the functions of virtio crypto device, including session creation, session closing, cipher encryption and decryption. Quick usage: # make tests/virtio-crypto-test && ./tests/virtio-crypto-test CC tests/virtio-crypto-test.o LINK tests/virtio-crypto-test /x86_64/virtio/crypto/pci/basic: OK Signed-off-by: Gonglei --- tests/Makefile.include | 3 + tests/virtio-crypto-test.c | 427 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 430 insertions(+) create mode 100644 tests/virtio-crypto-test.c diff --git a/tests/Makefile.include b/tests/Makefile.include index a77777c..c79bbb1 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -152,6 +152,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 @@ -646,6 +648,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..57aa41f --- /dev/null +++ b/tests/virtio-crypto-test.c @@ -0,0 +1,427 @@ +/* + * 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 { + 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; +} VirtIOCryptoCipherTestData; + + +static VirtIOCryptoCipherTestData cipher_test_data[] = { + { /* From RFC 3602 */ + .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, + }, +}; + +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(&qvirtio_pci, &dev->vdev); + qvirtio_set_acknowledge(&qvirtio_pci, &dev->vdev); + qvirtio_set_driver(&qvirtio_pci, &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(const QVirtioBus *bus, QVirtioDevice *dev) +{ + /* Read configure space to get supported crypto services */ + + qvirtio_set_driver_ok(bus, dev); +} + +static uint64_t +virtio_crypto_create_session(const QVirtioBus *bus, 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(bus, dev, vq, free_head); + + qvirtio_wait_queue_isr(bus, 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(const QVirtioBus *bus, 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; + uint32_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(bus, dev, vq, free_head); + + qvirtio_wait_queue_isr(bus, 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(const QVirtioBus *bus, 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, src_addr, dst_addr; + uint64_t session_id; + char *output; + uint32_t src_len, dst_len, status; + QVRingIndirectDesc *indirect; + + /* Create a session */ + session_id = virtio_crypto_create_session(bus, 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); + } + + 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); + + indirect = qvring_indirect_desc_setup(dev, alloc, 5); + qvring_indirect_desc_add(indirect, req_addr, sizeof(req), false); + 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(bus, dev, vq, free_head); + + qvirtio_wait_queue_isr(bus, 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(bus, dev, alloc, ctrlq, session_id); +} + +static void virtio_crypto_pci_basic(void) +{ + QVirtioPCIDevice *dev; + QPCIBus *bus; + QGuestAllocator *alloc; + QVirtQueuePCI *dataq, *controlq; + size_t i; + uint32_t features; + + bus = virtio_crypto_test_start(); + dev = virtio_crypto_pci_init(bus, PCI_SLOT); + + alloc = pc_alloc_init(); + + features = qvirtio_get_features(&qvirtio_pci, &dev->vdev); + g_assert_cmphex(features & (1u << VIRTIO_RING_F_INDIRECT_DESC), !=, 0); + features = features & ~(QVIRTIO_F_BAD_FEATURE | + (1u << VIRTIO_RING_F_EVENT_IDX)); + qvirtio_set_features(&qvirtio_pci, &dev->vdev, features); + + dataq = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, + alloc, 0); + controlq = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, + alloc, 1); + + virtio_crypto_driver_init(&qvirtio_pci, &dev->vdev); + + for (i = 0; i < G_N_ELEMENTS(cipher_test_data); i++) { + /* Step 1: Encryption */ + virtio_crypto_test_cipher(&qvirtio_pci, &dev->vdev, alloc, + &controlq->vq, &dataq->vq, + &cipher_test_data[i], 1); + /* Step 2: Decryption */ + virtio_crypto_test_cipher(&qvirtio_pci, &dev->vdev, alloc, + &controlq->vq, &dataq->vq, + &cipher_test_data[i], 0); + } + + /* End test */ + qvirtqueue_cleanup(&qvirtio_pci, &dataq->vq, alloc); + qvirtqueue_cleanup(&qvirtio_pci, &controlq->vq, alloc); + 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 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) { + qtest_add_func("/virtio/crypto/pci/basic", virtio_crypto_pci_basic); + } + + return g_test_run(); +}