From patchwork Wed Sep 28 08:25:51 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Gonglei (Arei)" X-Patchwork-Id: 9353329 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 A50216086A for ; Wed, 28 Sep 2016 08:42:22 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 93D0229454 for ; Wed, 28 Sep 2016 08:42:22 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 87CCB29456; Wed, 28 Sep 2016 08:42:22 +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 79C9F29455 for ; Wed, 28 Sep 2016 08:42:21 +0000 (UTC) Received: from localhost ([::1]:56823 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bpARY-00068x-Lq for patchwork-qemu-devel@patchwork.kernel.org; Wed, 28 Sep 2016 04:42:20 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:37662) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bpAFo-00030X-4D for qemu-devel@nongnu.org; Wed, 28 Sep 2016 04:30:14 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1bpAFj-0006CI-Qu for qemu-devel@nongnu.org; Wed, 28 Sep 2016 04:30:11 -0400 Received: from szxga03-in.huawei.com ([119.145.14.66]:14101) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bpAFi-000685-N4 for qemu-devel@nongnu.org; Wed, 28 Sep 2016 04:30:07 -0400 Received: from 172.24.1.136 (EHLO SZXEML423-HUB.china.huawei.com) ([172.24.1.136]) by szxrg03-dlp.huawei.com (MOS 4.4.3-GA FastPath queued) with ESMTP id CIM80048; Wed, 28 Sep 2016 16:26:19 +0800 (CST) Received: from localhost (10.177.18.62) by SZXEML423-HUB.china.huawei.com (10.82.67.154) with Microsoft SMTP Server id 14.3.235.1; Wed, 28 Sep 2016 16:26:09 +0800 From: Gonglei To: , Date: Wed, 28 Sep 2016 16:25:51 +0800 Message-ID: <1475051152-400276-13-git-send-email-arei.gonglei@huawei.com> X-Mailer: git-send-email 2.6.3.windows.1 In-Reply-To: <1475051152-400276-1-git-send-email-arei.gonglei@huawei.com> References: <1475051152-400276-1-git-send-email-arei.gonglei@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.177.18.62] X-CFilter-Loop: Reflected X-Mirapoint-Virus-RAPID-Raw: score=unknown(0), refid=str=0001.0A020206.57EB7EAB.0177, ss=1, re=0.000, recu=0.000, reip=0.000, cl=1, cld=1, fgs=0, ip=0.0.0.0, so=2013-05-26 15:14:31, dmn=2013-03-21 17:37:32 X-Mirapoint-Loop-Id: 65cd5e6cb47d549b2e8ea424dc52d567 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.4.x-2.6.x [generic] X-Received-From: 119.145.14.66 Subject: [Qemu-devel] [PATCH v4 12/13] 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 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 | 412 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 415 insertions(+) create mode 100644 tests/virtio-crypto-test.c diff --git a/tests/Makefile.include b/tests/Makefile.include index 8162f6f..b1baa5c 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 @@ -638,6 +640,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..bbc4089 --- /dev/null +++ b/tests/virtio-crypto-test.c @@ -0,0 +1,412 @@ +/* + * 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(); +} + +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; /* cipher key guest physical address */ + uint64_t session_id; + size_t input_offset; + + /* 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; + + ctrl.u.sym_create_session.u.cipher.input.status = VIRTIO_CRYPTO_ERR; + /* 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; + } + /* Pad cipher's output data */ + key_addr = guest_alloc(alloc, key_len); + memwrite(key_addr, data->key, key_len); + ctrl.u.sym_create_session.u.cipher.out.key_addr = key_addr; + + req_addr = virtio_crypto_ctrl_request(alloc, &ctrl); + + free_head = qvirtqueue_add(vq, req_addr, sizeof(ctrl), true, false); + + qvirtqueue_kick(bus, dev, vq, free_head); + + qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_CRYPTO_TIMEOUT_US); + + /* calculate the offset of input data */ + input_offset = offsetof(struct virtio_crypto_op_ctrl_req, + u.sym_create_session.u.cipher.input); + input = g_new(struct virtio_crypto_session_input, 1); + memread(req_addr + input_offset, (void *)input, sizeof(*input)); + + /* Verify the result */ + g_assert_cmpint(input->status, ==, VIRTIO_CRYPTO_OK); + + session_id = input->session_id; + + g_free(input); + 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; + size_t status_offset; + uint32_t status; + + /* 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; + ctrl.u.destroy_session.status = VIRTIO_CRYPTO_ERR; + + req_addr = virtio_crypto_ctrl_request(alloc, &ctrl); + + free_head = qvirtqueue_add(vq, req_addr, sizeof(ctrl), true, false); + + qvirtqueue_kick(bus, dev, vq, free_head); + + qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_CRYPTO_TIMEOUT_US); + + /* calculate the offset of input data */ + status_offset = offsetof(struct virtio_crypto_op_ctrl_req, + u.destroy_session.status); + memread(req_addr + status_offset, (void *)&status, sizeof(status)); + + /* Verify the result */ + g_assert_cmpint(status, ==, VIRTIO_CRYPTO_OK); + + guest_free(alloc, req_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; + struct virtio_crypto_sym_input *idata; + uint64_t req_addr; + uint64_t iv_addr, src_addr, dst_addr; + uint64_t session_id; + char *output; + size_t idata_offset; + uint32_t src_len, dst_len; + + /* 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; + /* IV */ + if (data->iv_len > 0) { + iv_addr = guest_alloc(alloc, data->iv_len); + memwrite(iv_addr, data->iv, data->iv_len); + req.u.sym_req.u.cipher.odata.iv_addr = iv_addr; + } + + if (encrypt) { + src_len = data->ilen; + dst_len = data->olen; + /* Source data is the input data which is a single s/g */ + 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 s/g */ + src_addr = guest_alloc(alloc, src_len); + memwrite(src_addr, data->output, src_len); + } + req.u.sym_req.u.cipher.odata.src_data.addr = src_addr; + req.u.sym_req.u.cipher.odata.src_data.len = src_len; + req.u.sym_req.u.cipher.odata.src_data.flags = ~VIRTIO_CRYPTO_IOVEC_F_NEXT; + + /* Destination data, a single s/g */ + dst_addr = guest_alloc(alloc, dst_len); + req.u.sym_req.u.cipher.idata.input.dst_data.addr = dst_addr; + req.u.sym_req.u.cipher.idata.input.dst_data.len = dst_len; + req.u.sym_req.u.cipher.idata.input.dst_data.flags = + ~VIRTIO_CRYPTO_IOVEC_F_NEXT; + + req_addr = virtio_crypto_data_request(alloc, &req); + + free_head = qvirtqueue_add(vq, req_addr, sizeof(req), true, false); + + qvirtqueue_kick(bus, dev, vq, free_head); + + qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_CRYPTO_TIMEOUT_US); + + /* Calculate the offset of input data */ + idata_offset = offsetof(struct virtio_crypto_op_data_req, + u.sym_req.u.cipher.idata.input); + idata = g_new(struct virtio_crypto_sym_input, 1); + memread(req_addr + idata_offset, (void *)idata, sizeof(*idata)); + + /* Verify the result */ + g_assert_cmpint(idata->status, ==, VIRTIO_CRYPTO_OK); + g_free(idata); + + output = g_malloc(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); + + 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); + + /* 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; + + bus = virtio_crypto_test_start(); + dev = virtio_crypto_pci_init(bus, PCI_SLOT); + + alloc = pc_alloc_init(); + 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(); +}