From 5b774fe5505dc7daad908f9f4b95e9e735c45c14 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 20 Feb 2019 15:33:27 +0100 Subject: [PATCH 01/57] chardev-socket: do not blindly reset handlers when switching GMainContext MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the socket is connecting or connected, tcp_chr_update_read_handler will be called but it should not set the NetListener's callbacks again. Otherwise, tcp_chr_accept is invoked while the socket is in connected state and you get an assertion failure. Reviewed-by: Daniel P. Berrangé Reviewed-by: Marc-André Lureau Signed-off-by: Paolo Bonzini --- chardev/char-socket.c | 2 +- tests/test-char.c | 102 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 2 deletions(-) diff --git a/chardev/char-socket.c b/chardev/char-socket.c index 4fcdd8aedd..6d287babfb 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -632,7 +632,7 @@ static void tcp_chr_update_read_handler(Chardev *chr) { SocketChardev *s = SOCKET_CHARDEV(chr); - if (s->listener) { + if (s->listener && s->state == TCP_CHARDEV_STATE_DISCONNECTED) { /* * It's possible that chardev context is changed in * qemu_chr_be_update_read_handlers(). Reset it for QIO net diff --git a/tests/test-char.c b/tests/test-char.c index 63b4d3289d..de328380c1 100644 --- a/tests/test-char.c +++ b/tests/test-char.c @@ -1003,6 +1003,103 @@ static void char_socket_client_test(gconstpointer opaque) g_free(optstr); } +static void +count_closed_event(void *opaque, int event) +{ + int *count = opaque; + if (event == CHR_EVENT_CLOSED) { + (*count)++; + } +} + +static void +char_socket_discard_read(void *opaque, const uint8_t *buf, int size) +{ +} + +static void char_socket_server_two_clients_test(gconstpointer opaque) +{ + SocketAddress *incoming_addr = (gpointer) opaque; + Chardev *chr; + CharBackend be = {0}; + QObject *qaddr; + SocketAddress *addr; + Visitor *v; + char *optstr; + QemuOpts *opts; + QIOChannelSocket *ioc1, *ioc2; + int closed = 0; + + g_setenv("QTEST_SILENT_ERRORS", "1", 1); + /* + * We rely on addr containing "nowait", otherwise + * qemu_chr_new() will block until a client connects. We + * can't spawn our client thread though, because until + * qemu_chr_new() returns we don't know what TCP port was + * allocated by the OS + */ + optstr = char_socket_addr_to_opt_str(incoming_addr, + false, + NULL, + true); + opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), + optstr, true); + g_assert_nonnull(opts); + chr = qemu_chr_new_from_opts(opts, NULL, &error_abort); + qemu_opts_del(opts); + g_assert_nonnull(chr); + g_assert(!object_property_get_bool(OBJECT(chr), "connected", &error_abort)); + + qaddr = object_property_get_qobject(OBJECT(chr), "addr", &error_abort); + g_assert_nonnull(qaddr); + + v = qobject_input_visitor_new(qaddr); + visit_type_SocketAddress(v, "addr", &addr, &error_abort); + visit_free(v); + qobject_unref(qaddr); + + qemu_chr_fe_init(&be, chr, &error_abort); + + qemu_chr_fe_set_handlers(&be, char_socket_can_read, char_socket_discard_read, + count_closed_event, NULL, + &closed, NULL, true); + + ioc1 = qio_channel_socket_new(); + qio_channel_socket_connect_sync(ioc1, addr, &error_abort); + qemu_chr_wait_connected(chr, &error_abort); + + /* switch the chardev to another context */ + GMainContext *ctx = g_main_context_new(); + qemu_chr_fe_set_handlers(&be, char_socket_can_read, char_socket_discard_read, + count_closed_event, NULL, + &closed, ctx, true); + + /* Start a second connection while the first is still connected. + * It will be placed in the listen() backlog, and connect() will + * succeed immediately. + */ + ioc2 = qio_channel_socket_new(); + qio_channel_socket_connect_sync(ioc2, addr, &error_abort); + + object_unref(OBJECT(ioc1)); + /* The two connections should now be processed serially. */ + while (g_main_context_iteration(ctx, TRUE)) { + if (closed == 1 && ioc2) { + object_unref(OBJECT(ioc2)); + ioc2 = NULL; + } + if (closed == 2) { + break; + } + } + + qapi_free_SocketAddress(addr); + object_unparent(OBJECT(chr)); + g_main_context_unref(ctx); + g_free(optstr); + g_unsetenv("QTEST_SILENT_ERRORS"); +} + #ifdef HAVE_CHARDEV_SERIAL static void char_serial_test(void) @@ -1342,12 +1439,15 @@ int main(int argc, char **argv) SOCKET_SERVER_TEST(tcp, &tcpaddr); SOCKET_CLIENT_TEST(tcp, &tcpaddr); + g_test_add_data_func("/char/socket/server/two-clients/tcp", &tcpaddr, + char_socket_server_two_clients_test); #ifndef WIN32 SOCKET_SERVER_TEST(unix, &unixaddr); SOCKET_CLIENT_TEST(unix, &unixaddr); + g_test_add_data_func("/char/socket/server/two-clients/unix", &unixaddr, + char_socket_server_two_clients_test); #endif - g_test_add_func("/char/udp", char_udp_test); #ifdef HAVE_CHARDEV_SERIAL g_test_add_func("/char/serial", char_serial_test); From 34c977489dd85e3528b0e5c3a02157c834560efa Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Wed, 25 Jul 2018 16:25:43 +0200 Subject: [PATCH 02/57] tests/libqos: introduce virtio_start_device This function is intended to group all the qvirtio_* functions that start the qvirtio devices. Applied in all tests using this combination of functions. Signed-off-by: Emanuele Giuseppe Esposito Reviewed-by: Laurent Vivier Reviewed-by: Thomas Huth Signed-off-by: Paolo Bonzini --- tests/libqos/virtio.c | 7 +++++++ tests/libqos/virtio.h | 1 + tests/vhost-user-test.c | 4 +--- tests/virtio-9p-test.c | 4 +--- tests/virtio-blk-test.c | 9 ++------- tests/virtio-net-test.c | 4 +--- tests/virtio-scsi-test.c | 4 +--- 7 files changed, 14 insertions(+), 19 deletions(-) diff --git a/tests/libqos/virtio.c b/tests/libqos/virtio.c index 0dad5c19ac..c1ff0206d5 100644 --- a/tests/libqos/virtio.c +++ b/tests/libqos/virtio.c @@ -365,3 +365,10 @@ const char *qvirtio_get_dev_type(void) return "pci"; } } + +void qvirtio_start_device(QVirtioDevice *vdev) +{ + qvirtio_reset(vdev); + qvirtio_set_acknowledge(vdev); + qvirtio_set_driver(vdev); +} diff --git a/tests/libqos/virtio.h b/tests/libqos/virtio.h index 69b5b13840..2c68668de0 100644 --- a/tests/libqos/virtio.h +++ b/tests/libqos/virtio.h @@ -146,5 +146,6 @@ bool qvirtqueue_get_buf(QVirtQueue *vq, uint32_t *desc_idx, uint32_t *len); void qvirtqueue_set_used_event(QVirtQueue *vq, uint16_t idx); const char *qvirtio_get_dev_type(void); +void qvirtio_start_device(QVirtioDevice *vdev); #endif diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c index 4cd0a97f13..770e1e6484 100644 --- a/tests/vhost-user-test.c +++ b/tests/vhost-user-test.c @@ -204,9 +204,7 @@ static void init_virtio_dev(QTestState *qts, TestServer *s, uint32_t features_ma g_assert_nonnull(s->dev); qvirtio_pci_device_enable(s->dev); - qvirtio_reset(&s->dev->vdev); - qvirtio_set_acknowledge(&s->dev->vdev); - qvirtio_set_driver(&s->dev->vdev); + qvirtio_start_device(&s->dev->vdev); s->alloc = pc_alloc_init(qts); diff --git a/tests/virtio-9p-test.c b/tests/virtio-9p-test.c index a2b31085f6..d275c74106 100644 --- a/tests/virtio-9p-test.c +++ b/tests/virtio-9p-test.c @@ -65,9 +65,7 @@ static QVirtIO9P *qvirtio_9p_pci_start(void) v9p->dev = (QVirtioDevice *) dev; qvirtio_pci_device_enable(dev); - qvirtio_reset(v9p->dev); - qvirtio_set_acknowledge(v9p->dev); - qvirtio_set_driver(v9p->dev); + qvirtio_start_device(v9p->dev); v9p->vq = qvirtqueue_setup(v9p->dev, v9p->qs->alloc, 0); diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c index 8d2fc9c710..0680bd040e 100644 --- a/tests/virtio-blk-test.c +++ b/tests/virtio-blk-test.c @@ -122,10 +122,7 @@ static QVirtioPCIDevice *virtio_blk_pci_init(QPCIBus *bus, int slot) g_assert_cmphex(dev->pdev->devfn, ==, ((slot << 3) | PCI_FN)); qvirtio_pci_device_enable(dev); - qvirtio_reset(&dev->vdev); - qvirtio_set_acknowledge(&dev->vdev); - qvirtio_set_driver(&dev->vdev); - + qvirtio_start_device(&dev->vdev); return dev; } @@ -833,9 +830,7 @@ static void mmio_basic(void) g_assert(dev != NULL); g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK); - qvirtio_reset(&dev->vdev); - qvirtio_set_acknowledge(&dev->vdev); - qvirtio_set_driver(&dev->vdev); + qvirtio_start_device(&dev->vdev); alloc = generic_alloc_init(MMIO_RAM_ADDR, MMIO_RAM_SIZE, MMIO_PAGE_SIZE); vq = qvirtqueue_setup(&dev->vdev, alloc, 0); diff --git a/tests/virtio-net-test.c b/tests/virtio-net-test.c index e9783e6707..d4a32a4c44 100644 --- a/tests/virtio-net-test.c +++ b/tests/virtio-net-test.c @@ -44,9 +44,7 @@ static QVirtioPCIDevice *virtio_net_pci_init(QPCIBus *bus, int slot) g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_NET); qvirtio_pci_device_enable(dev); - qvirtio_reset(&dev->vdev); - qvirtio_set_acknowledge(&dev->vdev); - qvirtio_set_driver(&dev->vdev); + qvirtio_start_device(&dev->vdev); return dev; } diff --git a/tests/virtio-scsi-test.c b/tests/virtio-scsi-test.c index 0d4f25d369..961925cec1 100644 --- a/tests/virtio-scsi-test.c +++ b/tests/virtio-scsi-test.c @@ -159,9 +159,7 @@ static QVirtIOSCSI *qvirtio_scsi_pci_init(int slot) g_assert_cmphex(vs->dev->device_type, ==, VIRTIO_ID_SCSI); qvirtio_pci_device_enable(dev); - qvirtio_reset(vs->dev); - qvirtio_set_acknowledge(vs->dev); - qvirtio_set_driver(vs->dev); + qvirtio_start_device(vs->dev); vs->num_queues = qvirtio_config_readl(vs->dev, 0); From 143e6db6fa4ecd2a85de740cc3754aeb86d1e802 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Thu, 19 Jul 2018 13:50:27 +0200 Subject: [PATCH 03/57] tests/libqos: rename qpci_init_pc and qpci_init_spapr functions Rename qpci_init_pc in qpci_pc_new and qpci_init_spapr in qpci_spapr_new, since these function actually allocate a new pci struct and initialize it (compare to object_new and object_initialize). Changed QOSOps field name from qpci_init to qpci_new. Signed-off-by: Emanuele Giuseppe Esposito Reviewed-by: Laurent Vivier Reviewed-by: Thomas Huth Signed-off-by: Paolo Bonzini --- tests/e1000e-test.c | 2 +- tests/i440fx-test.c | 2 +- tests/ide-test.c | 2 +- tests/libqos/ahci.c | 2 +- tests/libqos/libqos-pc.c | 2 +- tests/libqos/libqos-spapr.c | 2 +- tests/libqos/libqos.c | 2 +- tests/libqos/libqos.h | 2 +- tests/libqos/pci-pc.c | 2 +- tests/libqos/pci-pc.h | 12 +++++++++++- tests/libqos/pci-spapr.c | 2 +- tests/libqos/pci-spapr.h | 2 +- tests/q35-test.c | 4 ++-- tests/rtl8139-test.c | 2 +- tests/sdhci-test.c | 2 +- tests/tco-test.c | 2 +- tests/usb-hcd-ehci-test.c | 2 +- tests/vhost-user-test.c | 2 +- 18 files changed, 29 insertions(+), 19 deletions(-) diff --git a/tests/e1000e-test.c b/tests/e1000e-test.c index c9408a5d1f..5525589e5b 100644 --- a/tests/e1000e-test.c +++ b/tests/e1000e-test.c @@ -395,7 +395,7 @@ static void data_test_init(e1000e_device *d) test_alloc = pc_alloc_init(global_qtest); g_assert_nonnull(test_alloc); - test_bus = qpci_init_pc(global_qtest, test_alloc); + test_bus = qpci_new_pc(global_qtest, test_alloc); g_assert_nonnull(test_bus); e1000e_device_init(test_bus, d); diff --git a/tests/i440fx-test.c b/tests/i440fx-test.c index 4390e5591e..69205b58a8 100644 --- a/tests/i440fx-test.c +++ b/tests/i440fx-test.c @@ -38,7 +38,7 @@ static QPCIBus *test_start_get_bus(const TestData *s) cmdline = g_strdup_printf("-smp %d", s->num_cpus); qtest_start(cmdline); g_free(cmdline); - return qpci_init_pc(global_qtest, NULL); + return qpci_new_pc(global_qtest, NULL); } static void test_i440fx_defaults(gconstpointer opaque) diff --git a/tests/ide-test.c b/tests/ide-test.c index 300d64e77d..46898d96bf 100644 --- a/tests/ide-test.c +++ b/tests/ide-test.c @@ -157,7 +157,7 @@ static QPCIDevice *get_pci_device(QPCIBar *bmdma_bar, QPCIBar *ide_bar) uint16_t vendor_id, device_id; if (!pcibus) { - pcibus = qpci_init_pc(global_qtest, NULL); + pcibus = qpci_new_pc(global_qtest, NULL); } /* Find PCI device and verify it's the right one */ diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c index 63fbc9e3c9..cc1b08eabe 100644 --- a/tests/libqos/ahci.c +++ b/tests/libqos/ahci.c @@ -130,7 +130,7 @@ QPCIDevice *get_ahci_device(QTestState *qts, uint32_t *fingerprint) uint32_t ahci_fingerprint; QPCIBus *pcibus; - pcibus = qpci_init_pc(qts, NULL); + pcibus = qpci_new_pc(qts, NULL); /* Find the AHCI PCI device and verify it's the right one. */ ahci = qpci_device_find(pcibus, QPCI_DEVFN(0x1F, 0x02)); diff --git a/tests/libqos/libqos-pc.c b/tests/libqos/libqos-pc.c index a9c1aceaa7..293f9b6fe6 100644 --- a/tests/libqos/libqos-pc.c +++ b/tests/libqos/libqos-pc.c @@ -6,7 +6,7 @@ static QOSOps qos_ops = { .init_allocator = pc_alloc_init_flags, .uninit_allocator = pc_alloc_uninit, - .qpci_init = qpci_init_pc, + .qpci_new = qpci_new_pc, .qpci_free = qpci_free_pc, .shutdown = qtest_pc_shutdown, }; diff --git a/tests/libqos/libqos-spapr.c b/tests/libqos/libqos-spapr.c index a37791e5d0..64addfe577 100644 --- a/tests/libqos/libqos-spapr.c +++ b/tests/libqos/libqos-spapr.c @@ -6,7 +6,7 @@ static QOSOps qos_ops = { .init_allocator = spapr_alloc_init_flags, .uninit_allocator = spapr_alloc_uninit, - .qpci_init = qpci_init_spapr, + .qpci_new = qpci_new_spapr, .qpci_free = qpci_free_spapr, .shutdown = qtest_spapr_shutdown, }; diff --git a/tests/libqos/libqos.c b/tests/libqos/libqos.c index c514187344..6c9137165d 100644 --- a/tests/libqos/libqos.c +++ b/tests/libqos/libqos.c @@ -25,7 +25,7 @@ QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap) qs->ops = ops; if (ops) { qs->alloc = ops->init_allocator(qs->qts, ALLOC_NO_FLAGS); - qs->pcibus = ops->qpci_init(qs->qts, qs->alloc); + qs->pcibus = ops->qpci_new(qs->qts, qs->alloc); } g_free(cmdline); diff --git a/tests/libqos/libqos.h b/tests/libqos/libqos.h index 07d4b93d1d..1af6035db4 100644 --- a/tests/libqos/libqos.h +++ b/tests/libqos/libqos.h @@ -10,7 +10,7 @@ typedef struct QOSState QOSState; typedef struct QOSOps { QGuestAllocator *(*init_allocator)(QTestState *qts, QAllocOpts); void (*uninit_allocator)(QGuestAllocator *); - QPCIBus *(*qpci_init)(QTestState *qts, QGuestAllocator *alloc); + QPCIBus *(*qpci_new)(QTestState *qts, QGuestAllocator *alloc); void (*qpci_free)(QPCIBus *bus); void (*shutdown)(QOSState *); } QOSOps; diff --git a/tests/libqos/pci-pc.c b/tests/libqos/pci-pc.c index a4fc02b5d8..43d7082516 100644 --- a/tests/libqos/pci-pc.c +++ b/tests/libqos/pci-pc.c @@ -116,7 +116,7 @@ static void qpci_pc_config_writel(QPCIBus *bus, int devfn, uint8_t offset, uint3 qtest_outl(bus->qts, 0xcfc, value); } -QPCIBus *qpci_init_pc(QTestState *qts, QGuestAllocator *alloc) +QPCIBus *qpci_new_pc(QTestState *qts, QGuestAllocator *alloc) { QPCIBusPC *ret = g_new0(QPCIBusPC, 1); diff --git a/tests/libqos/pci-pc.h b/tests/libqos/pci-pc.h index 491eeac756..5be68ba7de 100644 --- a/tests/libqos/pci-pc.h +++ b/tests/libqos/pci-pc.h @@ -16,7 +16,17 @@ #include "libqos/pci.h" #include "libqos/malloc.h" -QPCIBus *qpci_init_pc(QTestState *qts, QGuestAllocator *alloc); +/* qpci_pc_new(): + * @qts: The %QTestState for this PC machine + * @alloc: A previously initialized @alloc providing memory for @qts + * + * This function creates a new QPCIBusPC object, + * and properly initialize its fields. + * + * Returns a newly allocated QPCIBus. + */ +QPCIBus *qpci_new_pc(QTestState *qts, QGuestAllocator *alloc); + void qpci_free_pc(QPCIBus *bus); #endif diff --git a/tests/libqos/pci-spapr.c b/tests/libqos/pci-spapr.c index 4c29889b0b..59679f6a92 100644 --- a/tests/libqos/pci-spapr.c +++ b/tests/libqos/pci-spapr.c @@ -160,7 +160,7 @@ static void qpci_spapr_config_writel(QPCIBus *bus, int devfn, uint8_t offset, #define SPAPR_PCI_MMIO32_WIN_SIZE 0x80000000 /* 2 GiB */ #define SPAPR_PCI_IO_WIN_SIZE 0x10000 -QPCIBus *qpci_init_spapr(QTestState *qts, QGuestAllocator *alloc) +QPCIBus *qpci_new_spapr(QTestState *qts, QGuestAllocator *alloc) { QPCIBusSPAPR *ret = g_new0(QPCIBusSPAPR, 1); diff --git a/tests/libqos/pci-spapr.h b/tests/libqos/pci-spapr.h index 387686dfc8..177e8c002c 100644 --- a/tests/libqos/pci-spapr.h +++ b/tests/libqos/pci-spapr.h @@ -11,7 +11,7 @@ #include "libqos/malloc.h" #include "libqos/pci.h" -QPCIBus *qpci_init_spapr(QTestState *qts, QGuestAllocator *alloc); +QPCIBus *qpci_new_spapr(QTestState *qts, QGuestAllocator *alloc); void qpci_free_spapr(QPCIBus *bus); #endif diff --git a/tests/q35-test.c b/tests/q35-test.c index 7ea7acc9d8..34b34bc2b9 100644 --- a/tests/q35-test.c +++ b/tests/q35-test.c @@ -87,7 +87,7 @@ static void test_smram_lock(void) qtest_start("-M q35"); - pcibus = qpci_init_pc(global_qtest, NULL); + pcibus = qpci_new_pc(global_qtest, NULL); g_assert(pcibus != NULL); pcidev = qpci_device_find(pcibus, 0); @@ -146,7 +146,7 @@ static void test_tseg_size(const void *data) g_free(cmdline); /* locate the DRAM controller */ - pcibus = qpci_init_pc(global_qtest, NULL); + pcibus = qpci_new_pc(global_qtest, NULL); g_assert(pcibus != NULL); pcidev = qpci_device_find(pcibus, 0); g_assert(pcidev != NULL); diff --git a/tests/rtl8139-test.c b/tests/rtl8139-test.c index 68bfc42178..a85c91f197 100644 --- a/tests/rtl8139-test.c +++ b/tests/rtl8139-test.c @@ -35,7 +35,7 @@ static QPCIDevice *get_device(void) { QPCIDevice *dev; - pcibus = qpci_init_pc(global_qtest, NULL); + pcibus = qpci_new_pc(global_qtest, NULL); qpci_device_foreach(pcibus, 0x10ec, 0x8139, save_fn, &dev); g_assert(dev != NULL); diff --git a/tests/sdhci-test.c b/tests/sdhci-test.c index 982f5ebbb2..28d481b62f 100644 --- a/tests/sdhci-test.c +++ b/tests/sdhci-test.c @@ -187,7 +187,7 @@ static QSDHCI *machine_start(const struct sdhci_t *test) global_qtest = qtest_initf("-machine %s -device sdhci-pci", test->machine); - s->pci.bus = qpci_init_pc(global_qtest, NULL); + s->pci.bus = qpci_new_pc(global_qtest, NULL); /* Find PCI device and verify it's the right one */ s->pci.dev = qpci_device_find(s->pci.bus, QPCI_DEVFN(4, 0)); diff --git a/tests/tco-test.c b/tests/tco-test.c index 6bee9a37d3..f89a42cdcc 100644 --- a/tests/tco-test.c +++ b/tests/tco-test.c @@ -64,7 +64,7 @@ static void test_init(TestData *d) global_qtest = qs; qtest_irq_intercept_in(qs, "ioapic"); - d->bus = qpci_init_pc(qs, NULL); + d->bus = qpci_new_pc(qs, NULL); d->dev = qpci_device_find(d->bus, QPCI_DEVFN(0x1f, 0x00)); g_assert(d->dev != NULL); diff --git a/tests/usb-hcd-ehci-test.c b/tests/usb-hcd-ehci-test.c index f28ea27f37..8bc3e44189 100644 --- a/tests/usb-hcd-ehci-test.c +++ b/tests/usb-hcd-ehci-test.c @@ -52,7 +52,7 @@ static void ehci_port_test(struct qhc *hc, int port, uint32_t expect) static void test_init(void) { - pcibus = qpci_init_pc(global_qtest, NULL); + pcibus = qpci_new_pc(global_qtest, NULL); g_assert(pcibus != NULL); qusb_pci_init_one(pcibus, &uhci1, QPCI_DEVFN(0x1d, 0), 4); diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c index 770e1e6484..611d3a79d8 100644 --- a/tests/vhost-user-test.c +++ b/tests/vhost-user-test.c @@ -197,7 +197,7 @@ static void init_virtio_dev(QTestState *qts, TestServer *s, uint32_t features_ma uint32_t features; int i; - s->bus = qpci_init_pc(qts, NULL); + s->bus = qpci_new_pc(qts, NULL); g_assert_nonnull(s->bus); s->dev = qvirtio_pci_device_find(s->bus, VIRTIO_ID_NET); From eb5937bad691ed18a401079a0604aa11fea0ecdd Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 29 Nov 2018 12:37:04 +0100 Subject: [PATCH 04/57] tests/libqos: embed allocators instead of malloc-ing them separately qgraph will embed these objects instead of allocating them in a separate object. Expose a new API "generic_alloc_init" and "generic_alloc_destroy" for that, and rename the existing API with s/init/new/ and s/uninit/free/. Signed-off-by: Paolo Bonzini --- tests/ahci-test.c | 6 +-- tests/e1000e-test.c | 22 +++++------ tests/ide-test.c | 17 ++++----- tests/libqos/libqos-pc.c | 3 +- tests/libqos/libqos-spapr.c | 3 +- tests/libqos/libqos.c | 13 +++---- tests/libqos/libqos.h | 11 +++--- tests/libqos/malloc-generic.c | 25 ++----------- tests/libqos/malloc-generic.h | 7 +--- tests/libqos/malloc-pc.c | 22 +---------- tests/libqos/malloc-pc.h | 4 +- tests/libqos/malloc-spapr.c | 19 +--------- tests/libqos/malloc-spapr.h | 4 +- tests/libqos/malloc.c | 42 +++------------------ tests/libqos/malloc.h | 21 ++++++++--- tests/rtas-test.c | 2 +- tests/vhost-user-test.c | 10 ++--- tests/virtio-9p-test.c | 12 +++--- tests/virtio-blk-test.c | 69 ++++++++++++++++++----------------- tests/virtio-net-test.c | 10 ++--- tests/virtio-scsi-test.c | 14 +++---- 21 files changed, 124 insertions(+), 212 deletions(-) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 5dd380e5ea..9f07e6f2ce 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -162,7 +162,7 @@ static AHCIQState *ahci_vboot(const char *cli, va_list ap) s = g_new0(AHCIQState, 1); s->parent = qtest_pc_vboot(cli, ap); global_qtest = s->parent->qts; - alloc_set_flags(s->parent->alloc, ALLOC_LEAK_ASSERT); + alloc_set_flags(&s->parent->alloc, ALLOC_LEAK_ASSERT); /* Verify that we have an AHCI device present. */ s->dev = get_ahci_device(s->parent->qts, &s->fingerprint); @@ -1039,7 +1039,7 @@ static void test_dma_fragmented(void) generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE); /* Create a DMA buffer in guest memory, and write our pattern to it. */ - ptr = guest_alloc(ahci->parent->alloc, bufsize); + ptr = guest_alloc(&ahci->parent->alloc, bufsize); g_assert(ptr); bufwrite(ptr, tx, bufsize); @@ -1059,7 +1059,7 @@ static void test_dma_fragmented(void) /* Read back the guest's receive buffer into local memory */ bufread(ptr, rx, bufsize); - guest_free(ahci->parent->alloc, ptr); + guest_free(&ahci->parent->alloc, ptr); g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0); diff --git a/tests/e1000e-test.c b/tests/e1000e-test.c index 5525589e5b..17a12b8ca0 100644 --- a/tests/e1000e-test.c +++ b/tests/e1000e-test.c @@ -94,7 +94,7 @@ typedef struct e1000e_device { } e1000e_device; static int test_sockets[2]; -static QGuestAllocator *test_alloc; +static QGuestAllocator test_alloc; static QPCIBus *test_bus; static void e1000e_pci_foreach_callback(QPCIDevice *dev, int devfn, void *data) @@ -165,7 +165,7 @@ static void e1000e_device_init(QPCIBus *bus, e1000e_device *d) val | E1000E_CTRL_EXT_DRV_LOAD | E1000E_CTRL_EXT_TXLSFLOW); /* Allocate and setup TX ring */ - d->tx_ring = guest_alloc(test_alloc, E1000E_RING_LEN); + d->tx_ring = guest_alloc(&test_alloc, E1000E_RING_LEN); g_assert(d->tx_ring != 0); e1000e_macreg_write(d, E1000E_TDBAL, (uint32_t) d->tx_ring); @@ -178,7 +178,7 @@ static void e1000e_device_init(QPCIBus *bus, e1000e_device *d) e1000e_macreg_write(d, E1000E_TCTL, E1000E_TCTL_EN); /* Allocate and setup RX ring */ - d->rx_ring = guest_alloc(test_alloc, E1000E_RING_LEN); + d->rx_ring = guest_alloc(&test_alloc, E1000E_RING_LEN); g_assert(d->rx_ring != 0); e1000e_macreg_write(d, E1000E_RDBAL, (uint32_t)d->rx_ring); @@ -268,7 +268,7 @@ static void e1000e_send_verify(e1000e_device *d) uint32_t recv_len; /* Prepare test data buffer */ - uint64_t data = guest_alloc(test_alloc, data_len); + uint64_t data = guest_alloc(&test_alloc, data_len); memwrite(data, "TEST", 5); /* Prepare TX descriptor */ @@ -296,7 +296,7 @@ static void e1000e_send_verify(e1000e_device *d) g_assert_cmpstr(buffer, == , "TEST"); /* Free test data buffer */ - guest_free(test_alloc, data); + guest_free(&test_alloc, data); } static void e1000e_receive_verify(e1000e_device *d) @@ -348,7 +348,7 @@ static void e1000e_receive_verify(e1000e_device *d) g_assert_cmpint(ret, == , sizeof(test) + sizeof(len)); /* Prepare test data buffer */ - uint64_t data = guest_alloc(test_alloc, data_len); + uint64_t data = guest_alloc(&test_alloc, data_len); /* Prepare RX descriptor */ memset(&descr, 0, sizeof(descr)); @@ -369,7 +369,7 @@ static void e1000e_receive_verify(e1000e_device *d) g_assert_cmpstr(buffer, == , "TEST"); /* Free test data buffer */ - guest_free(test_alloc, data); + guest_free(&test_alloc, data); } static void e1000e_device_clear(QPCIBus *bus, e1000e_device *d) @@ -392,10 +392,8 @@ static void data_test_init(e1000e_device *d) qtest_start(cmdline); g_free(cmdline); - test_alloc = pc_alloc_init(global_qtest); - g_assert_nonnull(test_alloc); - - test_bus = qpci_new_pc(global_qtest, test_alloc); + pc_alloc_init(&test_alloc, global_qtest, 0); + test_bus = qpci_new_pc(global_qtest, &test_alloc); g_assert_nonnull(test_bus); e1000e_device_init(test_bus, d); @@ -405,7 +403,7 @@ static void data_test_clear(e1000e_device *d) { e1000e_device_clear(test_bus, d); close(test_sockets[0]); - pc_alloc_uninit(test_alloc); + alloc_destroy(&test_alloc); g_free(d->pci_dev); qpci_free_pc(test_bus); qtest_end(); diff --git a/tests/ide-test.c b/tests/ide-test.c index 46898d96bf..d863a99f7f 100644 --- a/tests/ide-test.c +++ b/tests/ide-test.c @@ -120,7 +120,7 @@ enum { #define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0) static QPCIBus *pcibus = NULL; -static QGuestAllocator *guest_malloc; +static QGuestAllocator guest_malloc; static char tmp_path[] = "/tmp/qtest.XXXXXX"; static char debug_path[] = "/tmp/qtest-blkdebug.XXXXXX"; @@ -135,7 +135,7 @@ static void ide_test_start(const char *cmdline_fmt, ...) va_end(ap); qtest_start(cmdline); - guest_malloc = pc_alloc_init(global_qtest); + pc_alloc_init(&guest_malloc, global_qtest, 0); g_free(cmdline); } @@ -146,8 +146,7 @@ static void ide_test_quit(void) qpci_free_pc(pcibus); pcibus = NULL; } - pc_alloc_uninit(guest_malloc); - guest_malloc = NULL; + alloc_destroy(&guest_malloc); qtest_end(); } @@ -246,7 +245,7 @@ static int send_dma_request(int cmd, uint64_t sector, int nb_sectors, /* Setup PRDT */ len = sizeof(*prdt) * prdt_entries; - guest_prdt = guest_alloc(guest_malloc, len); + guest_prdt = guest_alloc(&guest_malloc, len); memwrite(guest_prdt, prdt, len); qpci_io_writel(dev, bmdma_bar, bmreg_prdt, guest_prdt); @@ -311,7 +310,7 @@ static void test_bmdma_simple_rw(void) uint8_t *buf; uint8_t *cmpbuf; size_t len = 512; - uintptr_t guest_buf = guest_alloc(guest_malloc, len); + uintptr_t guest_buf = guest_alloc(&guest_malloc, len); PrdtEntry prdt[] = { { @@ -381,7 +380,7 @@ static void test_bmdma_trim(void) const uint64_t bad_range = trim_range_le(TEST_IMAGE_SIZE / 512 - 1, 2); size_t len = 512; uint8_t *buf; - uintptr_t guest_buf = guest_alloc(guest_malloc, len); + uintptr_t guest_buf = guest_alloc(&guest_malloc, len); PrdtEntry prdt[] = { { @@ -625,7 +624,7 @@ static void make_dirty(uint8_t device) dev = get_pci_device(&bmdma_bar, &ide_bar); - guest_buf = guest_alloc(guest_malloc, len); + guest_buf = guest_alloc(&guest_malloc, len); buf = g_malloc(len); memset(buf, rand() % 255 + 1, len); g_assert(guest_buf); @@ -986,7 +985,7 @@ static void test_cdrom_dma(void) "-device ide-cd,drive=sr0,bus=ide.0", tmp_path); qtest_irq_intercept_in(global_qtest, "ioapic"); - guest_buf = guest_alloc(guest_malloc, len); + guest_buf = guest_alloc(&guest_malloc, len); prdt[0].addr = cpu_to_le32(guest_buf); prdt[0].size = cpu_to_le32(len | PRDT_EOT); diff --git a/tests/libqos/libqos-pc.c b/tests/libqos/libqos-pc.c index 293f9b6fe6..d04abc548b 100644 --- a/tests/libqos/libqos-pc.c +++ b/tests/libqos/libqos-pc.c @@ -4,8 +4,7 @@ #include "libqos/pci-pc.h" static QOSOps qos_ops = { - .init_allocator = pc_alloc_init_flags, - .uninit_allocator = pc_alloc_uninit, + .alloc_init = pc_alloc_init, .qpci_new = qpci_new_pc, .qpci_free = qpci_free_pc, .shutdown = qtest_pc_shutdown, diff --git a/tests/libqos/libqos-spapr.c b/tests/libqos/libqos-spapr.c index 64addfe577..8766d543ce 100644 --- a/tests/libqos/libqos-spapr.c +++ b/tests/libqos/libqos-spapr.c @@ -4,8 +4,7 @@ #include "libqos/pci-spapr.h" static QOSOps qos_ops = { - .init_allocator = spapr_alloc_init_flags, - .uninit_allocator = spapr_alloc_uninit, + .alloc_init = spapr_alloc_init, .qpci_new = qpci_new_spapr, .qpci_free = qpci_free_spapr, .shutdown = qtest_spapr_shutdown, diff --git a/tests/libqos/libqos.c b/tests/libqos/libqos.c index 6c9137165d..636a111a6f 100644 --- a/tests/libqos/libqos.c +++ b/tests/libqos/libqos.c @@ -24,8 +24,8 @@ QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap) qs->qts = qtest_init(cmdline); qs->ops = ops; if (ops) { - qs->alloc = ops->init_allocator(qs->qts, ALLOC_NO_FLAGS); - qs->pcibus = ops->qpci_new(qs->qts, qs->alloc); + ops->alloc_init(&qs->alloc, qs->qts, ALLOC_NO_FLAGS); + qs->pcibus = ops->qpci_new(qs->qts, &qs->alloc); } g_free(cmdline); @@ -58,11 +58,8 @@ void qtest_common_shutdown(QOSState *qs) qs->ops->qpci_free(qs->pcibus); qs->pcibus = NULL; } - if (qs->alloc && qs->ops->uninit_allocator) { - qs->ops->uninit_allocator(qs->alloc); - qs->alloc = NULL; - } } + alloc_destroy(&qs->alloc); qtest_quit(qs->qts); g_free(qs); } @@ -116,7 +113,7 @@ void migrate(QOSState *from, QOSState *to, const char *uri) /* If we were running, we can wait for an event. */ if (running) { - migrate_allocator(from->alloc, to->alloc); + migrate_allocator(&from->alloc, &to->alloc); set_context(to); qtest_qmp_eventwait(to->qts, "RESUME"); return; @@ -146,7 +143,7 @@ void migrate(QOSState *from, QOSState *to, const char *uri) g_assert_not_reached(); } - migrate_allocator(from->alloc, to->alloc); + migrate_allocator(&from->alloc, &to->alloc); set_context(to); } diff --git a/tests/libqos/libqos.h b/tests/libqos/libqos.h index 1af6035db4..149b0be8bc 100644 --- a/tests/libqos/libqos.h +++ b/tests/libqos/libqos.h @@ -3,13 +3,12 @@ #include "libqtest.h" #include "libqos/pci.h" -#include "libqos/malloc-pc.h" +#include "libqos/malloc.h" typedef struct QOSState QOSState; typedef struct QOSOps { - QGuestAllocator *(*init_allocator)(QTestState *qts, QAllocOpts); - void (*uninit_allocator)(QGuestAllocator *); + void (*alloc_init)(QGuestAllocator *, QTestState *, QAllocOpts); QPCIBus *(*qpci_new)(QTestState *qts, QGuestAllocator *alloc); void (*qpci_free)(QPCIBus *bus); void (*shutdown)(QOSState *); @@ -17,7 +16,7 @@ typedef struct QOSOps { struct QOSState { QTestState *qts; - QGuestAllocator *alloc; + QGuestAllocator alloc; QPCIBus *pcibus; QOSOps *ops; }; @@ -36,12 +35,12 @@ void generate_pattern(void *buffer, size_t len, size_t cycle_len); static inline uint64_t qmalloc(QOSState *q, size_t bytes) { - return guest_alloc(q->alloc, bytes); + return guest_alloc(&q->alloc, bytes); } static inline void qfree(QOSState *q, uint64_t addr) { - guest_free(q->alloc, addr); + guest_free(&q->alloc, addr); } #endif diff --git a/tests/libqos/malloc-generic.c b/tests/libqos/malloc-generic.c index 33ce90b925..94290daffc 100644 --- a/tests/libqos/malloc-generic.c +++ b/tests/libqos/malloc-generic.c @@ -11,29 +11,10 @@ #include "libqos/malloc-generic.h" #include "libqos/malloc.h" -/* - * Mostly for valgrind happiness, but it does offer - * a chokepoint for debugging guest memory leaks, too. - */ -void generic_alloc_uninit(QGuestAllocator *allocator) +void generic_alloc_init(QGuestAllocator *s, uint64_t base_addr, + uint64_t size, uint32_t page_size) { - alloc_uninit(allocator); -} - -QGuestAllocator *generic_alloc_init_flags(uint64_t base_addr, uint64_t size, - uint32_t page_size, QAllocOpts flags) -{ - QGuestAllocator *s; uint64_t start = base_addr + (1 << 20); /* Start at 1MB */ - s = alloc_init_flags(flags, start, start + size); - alloc_set_page_size(s, page_size); - - return s; -} - -inline QGuestAllocator *generic_alloc_init(uint64_t base_addr, uint64_t size, - uint32_t page_size) -{ - return generic_alloc_init_flags(base_addr, size, page_size, ALLOC_NO_FLAGS); + alloc_init(s, 0, start, start + size, page_size); } diff --git a/tests/libqos/malloc-generic.h b/tests/libqos/malloc-generic.h index 90104ecec9..40ea058b2d 100644 --- a/tests/libqos/malloc-generic.h +++ b/tests/libqos/malloc-generic.h @@ -12,10 +12,7 @@ #include "libqos/malloc.h" -QGuestAllocator *generic_alloc_init(uint64_t base_addr, uint64_t size, - uint32_t page_size); -QGuestAllocator *generic_alloc_init_flags(uint64_t base_addr, uint64_t size, - uint32_t page_size, QAllocOpts flags); -void generic_alloc_uninit(QGuestAllocator *allocator); +void generic_alloc_init(QGuestAllocator *s, uint64_t base_addr, uint64_t size, + uint32_t page_size); #endif diff --git a/tests/libqos/malloc-pc.c b/tests/libqos/malloc-pc.c index b83cb8f0af..949a99361d 100644 --- a/tests/libqos/malloc-pc.c +++ b/tests/libqos/malloc-pc.c @@ -20,32 +20,14 @@ #define PAGE_SIZE (4096) -/* - * Mostly for valgrind happiness, but it does offer - * a chokepoint for debugging guest memory leaks, too. - */ -void pc_alloc_uninit(QGuestAllocator *allocator) +void pc_alloc_init(QGuestAllocator *s, QTestState *qts, QAllocOpts flags) { - alloc_uninit(allocator); -} - -QGuestAllocator *pc_alloc_init_flags(QTestState *qts, QAllocOpts flags) -{ - QGuestAllocator *s; uint64_t ram_size; QFWCFG *fw_cfg = pc_fw_cfg_init(qts); ram_size = qfw_cfg_get_u64(fw_cfg, FW_CFG_RAM_SIZE); - s = alloc_init_flags(flags, 1 << 20, MIN(ram_size, 0xE0000000)); - alloc_set_page_size(s, PAGE_SIZE); + alloc_init(s, flags, 1 << 20, MIN(ram_size, 0xE0000000), PAGE_SIZE); /* clean-up */ g_free(fw_cfg); - - return s; -} - -inline QGuestAllocator *pc_alloc_init(QTestState *qts) -{ - return pc_alloc_init_flags(qts, ALLOC_NO_FLAGS); } diff --git a/tests/libqos/malloc-pc.h b/tests/libqos/malloc-pc.h index 10f3da6cf2..21e75ae004 100644 --- a/tests/libqos/malloc-pc.h +++ b/tests/libqos/malloc-pc.h @@ -15,8 +15,6 @@ #include "libqos/malloc.h" -QGuestAllocator *pc_alloc_init(QTestState *qts); -QGuestAllocator *pc_alloc_init_flags(QTestState *qts, QAllocOpts flags); -void pc_alloc_uninit(QGuestAllocator *allocator); +void pc_alloc_init(QGuestAllocator *s, QTestState *qts, QAllocOpts flags); #endif diff --git a/tests/libqos/malloc-spapr.c b/tests/libqos/malloc-spapr.c index 1c359cea6c..2a6b7e3776 100644 --- a/tests/libqos/malloc-spapr.c +++ b/tests/libqos/malloc-spapr.c @@ -17,22 +17,7 @@ */ #define SPAPR_MIN_SIZE 0x10000000 -void spapr_alloc_uninit(QGuestAllocator *allocator) +void spapr_alloc_init(QGuestAllocator *s, QTestState *qts, QAllocOpts flags) { - alloc_uninit(allocator); -} - -QGuestAllocator *spapr_alloc_init_flags(QTestState *qts, QAllocOpts flags) -{ - QGuestAllocator *s; - - s = alloc_init_flags(flags, 1 << 20, SPAPR_MIN_SIZE); - alloc_set_page_size(s, PAGE_SIZE); - - return s; -} - -QGuestAllocator *spapr_alloc_init(void) -{ - return spapr_alloc_init_flags(NULL, ALLOC_NO_FLAGS); + alloc_init(s, flags, 1 << 20, SPAPR_MIN_SIZE, PAGE_SIZE); } diff --git a/tests/libqos/malloc-spapr.h b/tests/libqos/malloc-spapr.h index 52a9346a26..e5fe9bfc4b 100644 --- a/tests/libqos/malloc-spapr.h +++ b/tests/libqos/malloc-spapr.h @@ -10,8 +10,6 @@ #include "libqos/malloc.h" -QGuestAllocator *spapr_alloc_init(void); -QGuestAllocator *spapr_alloc_init_flags(QTestState *qts, QAllocOpts flags); -void spapr_alloc_uninit(QGuestAllocator *allocator); +void spapr_alloc_init(QGuestAllocator *s, QTestState *qts, QAllocOpts flags); #endif diff --git a/tests/libqos/malloc.c b/tests/libqos/malloc.c index f7bae47a08..615422a5c4 100644 --- a/tests/libqos/malloc.c +++ b/tests/libqos/malloc.c @@ -15,24 +15,12 @@ #include "qemu-common.h" #include "qemu/host-utils.h" -typedef QTAILQ_HEAD(MemList, MemBlock) MemList; - typedef struct MemBlock { QTAILQ_ENTRY(MemBlock) MLIST_ENTNAME; uint64_t size; uint64_t addr; } MemBlock; -struct QGuestAllocator { - QAllocOpts opts; - uint64_t start; - uint64_t end; - uint32_t page_size; - - MemList *used; - MemList *free; -}; - #define DEFAULT_PAGE_SIZE 4096 static void mlist_delete(MemList *list, MemBlock *node) @@ -225,7 +213,7 @@ static void mlist_free(QGuestAllocator *s, uint64_t addr) * Mostly for valgrind happiness, but it does offer * a chokepoint for debugging guest memory leaks, too. */ -void alloc_uninit(QGuestAllocator *allocator) +void alloc_destroy(QGuestAllocator *allocator) { MemBlock *node; MemBlock *tmp; @@ -261,7 +249,6 @@ void alloc_uninit(QGuestAllocator *allocator) g_free(allocator->used); g_free(allocator->free); - g_free(allocator); } uint64_t guest_alloc(QGuestAllocator *allocator, size_t size) @@ -297,11 +284,13 @@ void guest_free(QGuestAllocator *allocator, uint64_t addr) } } -QGuestAllocator *alloc_init(uint64_t start, uint64_t end) +void alloc_init(QGuestAllocator *s, QAllocOpts opts, + uint64_t start, uint64_t end, + size_t page_size) { - QGuestAllocator *s = g_malloc0(sizeof(*s)); MemBlock *node; + s->opts = opts; s->start = start; s->end = end; @@ -313,26 +302,7 @@ QGuestAllocator *alloc_init(uint64_t start, uint64_t end) node = mlist_new(s->start, s->end - s->start); QTAILQ_INSERT_HEAD(s->free, node, MLIST_ENTNAME); - s->page_size = DEFAULT_PAGE_SIZE; - - return s; -} - -QGuestAllocator *alloc_init_flags(QAllocOpts opts, - uint64_t start, uint64_t end) -{ - QGuestAllocator *s = alloc_init(start, end); - s->opts = opts; - return s; -} - -void alloc_set_page_size(QGuestAllocator *allocator, size_t page_size) -{ - /* Can't alter the page_size for an allocator in-use */ - g_assert(QTAILQ_EMPTY(allocator->used)); - - g_assert(is_power_of_2(page_size)); - allocator->page_size = page_size; + s->page_size = page_size; } void alloc_set_flags(QGuestAllocator *allocator, QAllocOpts opts) diff --git a/tests/libqos/malloc.h b/tests/libqos/malloc.h index 828fddabdb..4d1a2e2bef 100644 --- a/tests/libqos/malloc.h +++ b/tests/libqos/malloc.h @@ -23,19 +23,28 @@ typedef enum { ALLOC_PARANOID = 0x04 } QAllocOpts; -typedef struct QGuestAllocator QGuestAllocator; +typedef QTAILQ_HEAD(MemList, MemBlock) MemList; -void alloc_uninit(QGuestAllocator *allocator); +typedef struct QGuestAllocator { + QAllocOpts opts; + uint64_t start; + uint64_t end; + uint32_t page_size; + + MemList *used; + MemList *free; +} QGuestAllocator; /* Always returns page aligned values */ uint64_t guest_alloc(QGuestAllocator *allocator, size_t size); void guest_free(QGuestAllocator *allocator, uint64_t addr); void migrate_allocator(QGuestAllocator *src, QGuestAllocator *dst); -QGuestAllocator *alloc_init(uint64_t start, uint64_t end); -QGuestAllocator *alloc_init_flags(QAllocOpts flags, - uint64_t start, uint64_t end); -void alloc_set_page_size(QGuestAllocator *allocator, size_t page_size); void alloc_set_flags(QGuestAllocator *allocator, QAllocOpts opts); +void alloc_init(QGuestAllocator *alloc, QAllocOpts flags, + uint64_t start, uint64_t end, + size_t page_size); +void alloc_destroy(QGuestAllocator *allocator); + #endif diff --git a/tests/rtas-test.c b/tests/rtas-test.c index 009bda6d23..ee888676ed 100644 --- a/tests/rtas-test.c +++ b/tests/rtas-test.c @@ -17,7 +17,7 @@ static void test_rtas_get_time_of_day(void) global_qtest = qs->qts; t1 = time(NULL); - ret = qrtas_get_time_of_day(qs->qts, qs->alloc, &tm, &ns); + ret = qrtas_get_time_of_day(qs->qts, &qs->alloc, &tm, &ns); g_assert_cmpint(ret, ==, 0); t2 = mktimegm(&tm); g_assert(t2 - t1 < 5); /* 5 sec max to run the test */ diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c index 611d3a79d8..b1414e2b44 100644 --- a/tests/vhost-user-test.c +++ b/tests/vhost-user-test.c @@ -158,7 +158,7 @@ typedef struct TestServer { bool test_fail; int test_flags; int queues; - QGuestAllocator *alloc; + QGuestAllocator alloc; } TestServer; static TestServer *test_server_new(const gchar *name); @@ -206,10 +206,10 @@ static void init_virtio_dev(QTestState *qts, TestServer *s, uint32_t features_ma qvirtio_pci_device_enable(s->dev); qvirtio_start_device(&s->dev->vdev); - s->alloc = pc_alloc_init(qts); + pc_alloc_init(&s->alloc, qts, 0); for (i = 0; i < s->queues * 2; i++) { - s->vq[i] = qvirtqueue_setup(&s->dev->vdev, s->alloc, i); + s->vq[i] = qvirtqueue_setup(&s->dev->vdev, &s->alloc, i); } features = qvirtio_get_features(&s->dev->vdev); @@ -224,9 +224,9 @@ static void uninit_virtio_dev(TestServer *s) int i; for (i = 0; i < s->queues * 2; i++) { - qvirtqueue_cleanup(s->dev->vdev.bus, s->vq[i], s->alloc); + qvirtqueue_cleanup(s->dev->vdev.bus, s->vq[i], &s->alloc); } - pc_alloc_uninit(s->alloc); + alloc_destroy(&s->alloc); qvirtio_pci_device_free(s->dev); } diff --git a/tests/virtio-9p-test.c b/tests/virtio-9p-test.c index d275c74106..8fd74f6ace 100644 --- a/tests/virtio-9p-test.c +++ b/tests/virtio-9p-test.c @@ -67,7 +67,7 @@ static QVirtIO9P *qvirtio_9p_pci_start(void) qvirtio_pci_device_enable(dev); qvirtio_start_device(v9p->dev); - v9p->vq = qvirtqueue_setup(v9p->dev, v9p->qs->alloc, 0); + v9p->vq = qvirtqueue_setup(v9p->dev, &v9p->qs->alloc, 0); qvirtio_set_driver_ok(v9p->dev); @@ -76,7 +76,7 @@ static QVirtIO9P *qvirtio_9p_pci_start(void) static void qvirtio_9p_pci_stop(QVirtIO9P *v9p) { - qvirtqueue_cleanup(v9p->dev->bus, v9p->vq, v9p->qs->alloc); + qvirtqueue_cleanup(v9p->dev->bus, v9p->vq, &v9p->qs->alloc); qvirtio_pci_device_disable(container_of(v9p->dev, QVirtioPCIDevice, vdev)); qvirtio_pci_device_free((QVirtioPCIDevice *)v9p->dev); qvirtio_9p_stop(v9p); @@ -222,7 +222,7 @@ static P9Req *v9fs_req_init(QVirtIO9P *v9p, uint32_t size, uint8_t id, req->v9p = v9p; req->t_size = total_size; - req->t_msg = guest_alloc(v9p->qs->alloc, req->t_size); + req->t_msg = guest_alloc(&v9p->qs->alloc, req->t_size); v9fs_memwrite(req, &hdr, 7); req->tag = tag; return req; @@ -232,7 +232,7 @@ static void v9fs_req_send(P9Req *req) { QVirtIO9P *v9p = req->v9p; - req->r_msg = guest_alloc(v9p->qs->alloc, P9_MAX_SIZE); + req->r_msg = guest_alloc(&v9p->qs->alloc, P9_MAX_SIZE); req->free_head = qvirtqueue_add(v9p->vq, req->t_msg, req->t_size, false, true); qvirtqueue_add(v9p->vq, req->r_msg, P9_MAX_SIZE, true, false); @@ -290,8 +290,8 @@ static void v9fs_req_free(P9Req *req) { QVirtIO9P *v9p = req->v9p; - guest_free(v9p->qs->alloc, req->t_msg); - guest_free(v9p->qs->alloc, req->r_msg); + guest_free(&v9p->qs->alloc, req->t_msg); + guest_free(&v9p->qs->alloc, req->r_msg); g_free(req); } diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c index 0680bd040e..b449970d01 100644 --- a/tests/virtio-blk-test.c +++ b/tests/virtio-blk-test.c @@ -15,6 +15,7 @@ #include "libqos/virtio.h" #include "libqos/virtio-pci.h" #include "libqos/virtio-mmio.h" +#include "libqos/malloc.h" #include "libqos/malloc-generic.h" #include "qapi/qmp/qdict.h" #include "qemu/bswap.h" @@ -403,12 +404,12 @@ static void pci_basic(void) qs = pci_test_start(); dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); - vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); + vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, &qs->alloc, 0); - test_basic(&dev->vdev, qs->alloc, &vqpci->vq); + test_basic(&dev->vdev, &qs->alloc, &vqpci->vq); /* End test */ - qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); + qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, &qs->alloc); qvirtio_pci_device_disable(dev); qvirtio_pci_device_free(dev); qtest_shutdown(qs); @@ -442,7 +443,7 @@ static void pci_indirect(void) (1u << VIRTIO_BLK_F_SCSI)); qvirtio_set_features(&dev->vdev, features); - vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); + vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, &qs->alloc, 0); qvirtio_set_driver_ok(&dev->vdev); /* Write request */ @@ -452,11 +453,11 @@ static void pci_indirect(void) req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); + req_addr = virtio_blk_request(&qs->alloc, &dev->vdev, &req, 512); g_free(req.data); - indirect = qvring_indirect_desc_setup(&dev->vdev, qs->alloc, 2); + indirect = qvring_indirect_desc_setup(&dev->vdev, &qs->alloc, 2); qvring_indirect_desc_add(indirect, req_addr, 528, false); qvring_indirect_desc_add(indirect, req_addr + 528, 1, true); free_head = qvirtqueue_add_indirect(&vqpci->vq, indirect); @@ -468,7 +469,7 @@ static void pci_indirect(void) g_assert_cmpint(status, ==, 0); g_free(indirect); - guest_free(qs->alloc, req_addr); + guest_free(&qs->alloc, req_addr); /* Read request */ req.type = VIRTIO_BLK_T_IN; @@ -477,11 +478,11 @@ static void pci_indirect(void) req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); + req_addr = virtio_blk_request(&qs->alloc, &dev->vdev, &req, 512); g_free(req.data); - indirect = qvring_indirect_desc_setup(&dev->vdev, qs->alloc, 2); + indirect = qvring_indirect_desc_setup(&dev->vdev, &qs->alloc, 2); qvring_indirect_desc_add(indirect, req_addr, 16, false); qvring_indirect_desc_add(indirect, req_addr + 16, 513, true); free_head = qvirtqueue_add_indirect(&vqpci->vq, indirect); @@ -498,10 +499,10 @@ static void pci_indirect(void) g_free(data); g_free(indirect); - guest_free(qs->alloc, req_addr); + guest_free(&qs->alloc, req_addr); /* End test */ - qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); + qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, &qs->alloc); qvirtio_pci_device_disable(dev); qvirtio_pci_device_free(dev); qtest_shutdown(qs); @@ -556,7 +557,7 @@ static void pci_msix(void) dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); qpci_msix_enable(dev->pdev); - qvirtio_pci_set_msix_configuration_vector(dev, qs->alloc, 0); + qvirtio_pci_set_msix_configuration_vector(dev, &qs->alloc, 0); capacity = qvirtio_config_readq(&dev->vdev, 0); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); @@ -568,8 +569,8 @@ static void pci_msix(void) (1u << VIRTIO_BLK_F_SCSI)); qvirtio_set_features(&dev->vdev, features); - vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); - qvirtqueue_pci_msix_setup(dev, vqpci, qs->alloc, 1); + vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, &qs->alloc, 0); + qvirtqueue_pci_msix_setup(dev, vqpci, &qs->alloc, 1); qvirtio_set_driver_ok(&dev->vdev); @@ -589,7 +590,7 @@ static void pci_msix(void) req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); + req_addr = virtio_blk_request(&qs->alloc, &dev->vdev, &req, 512); g_free(req.data); @@ -604,7 +605,7 @@ static void pci_msix(void) status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); - guest_free(qs->alloc, req_addr); + guest_free(&qs->alloc, req_addr); /* Read request */ req.type = VIRTIO_BLK_T_IN; @@ -612,7 +613,7 @@ static void pci_msix(void) req.sector = 0; req.data = g_malloc0(512); - req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); + req_addr = virtio_blk_request(&qs->alloc, &dev->vdev, &req, 512); g_free(req.data); @@ -634,10 +635,10 @@ static void pci_msix(void) g_assert_cmpstr(data, ==, "TEST"); g_free(data); - guest_free(qs->alloc, req_addr); + guest_free(&qs->alloc, req_addr); /* End test */ - qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); + qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, &qs->alloc); qpci_msix_disable(dev->pdev); qvirtio_pci_device_disable(dev); qvirtio_pci_device_free(dev); @@ -664,7 +665,7 @@ static void pci_idx(void) dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); qpci_msix_enable(dev->pdev); - qvirtio_pci_set_msix_configuration_vector(dev, qs->alloc, 0); + qvirtio_pci_set_msix_configuration_vector(dev, &qs->alloc, 0); capacity = qvirtio_config_readq(&dev->vdev, 0); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); @@ -676,8 +677,8 @@ static void pci_idx(void) (1u << VIRTIO_BLK_F_SCSI)); qvirtio_set_features(&dev->vdev, features); - vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); - qvirtqueue_pci_msix_setup(dev, vqpci, qs->alloc, 1); + vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, &qs->alloc, 0); + qvirtqueue_pci_msix_setup(dev, vqpci, &qs->alloc, 1); qvirtio_set_driver_ok(&dev->vdev); @@ -688,7 +689,7 @@ static void pci_idx(void) req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); + req_addr = virtio_blk_request(&qs->alloc, &dev->vdev, &req, 512); g_free(req.data); @@ -707,7 +708,7 @@ static void pci_idx(void) req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); + req_addr = virtio_blk_request(&qs->alloc, &dev->vdev, &req, 512); g_free(req.data); @@ -725,7 +726,7 @@ static void pci_idx(void) QVIRTIO_BLK_TIMEOUT_US); g_assert_cmpint(status, ==, 0); - guest_free(qs->alloc, req_addr); + guest_free(&qs->alloc, req_addr); /* Read request */ req.type = VIRTIO_BLK_T_IN; @@ -733,7 +734,7 @@ static void pci_idx(void) req.sector = 1; req.data = g_malloc0(512); - req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); + req_addr = virtio_blk_request(&qs->alloc, &dev->vdev, &req, 512); g_free(req.data); @@ -757,10 +758,10 @@ static void pci_idx(void) g_assert_cmpstr(data, ==, "TEST"); g_free(data); - guest_free(qs->alloc, req_addr); + guest_free(&qs->alloc, req_addr); /* End test */ - qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); + qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, &qs->alloc); qpci_msix_disable(dev->pdev); qvirtio_pci_device_disable(dev); qvirtio_pci_device_free(dev); @@ -820,7 +821,7 @@ static void mmio_basic(void) { QVirtioMMIODevice *dev; QVirtQueue *vq; - QGuestAllocator *alloc; + QGuestAllocator alloc; int n_size = TEST_IMAGE_SIZE / 2; uint64_t capacity; @@ -832,10 +833,10 @@ static void mmio_basic(void) qvirtio_start_device(&dev->vdev); - alloc = generic_alloc_init(MMIO_RAM_ADDR, MMIO_RAM_SIZE, MMIO_PAGE_SIZE); - vq = qvirtqueue_setup(&dev->vdev, alloc, 0); + generic_alloc_init(&alloc, MMIO_RAM_ADDR, MMIO_RAM_SIZE, MMIO_PAGE_SIZE); + vq = qvirtqueue_setup(&dev->vdev, &alloc, 0); - test_basic(&dev->vdev, alloc, vq); + test_basic(&dev->vdev, &alloc, vq); qmp_discard_response("{ 'execute': 'block_resize', " " 'arguments': { 'device': 'drive0', " @@ -847,9 +848,9 @@ static void mmio_basic(void) g_assert_cmpint(capacity, ==, n_size / 512); /* End test */ - qvirtqueue_cleanup(dev->vdev.bus, vq, alloc); + qvirtqueue_cleanup(dev->vdev.bus, vq, &alloc); g_free(dev); - generic_alloc_uninit(alloc); + alloc_destroy(&alloc); test_end(); } diff --git a/tests/virtio-net-test.c b/tests/virtio-net-test.c index d4a32a4c44..653148eeab 100644 --- a/tests/virtio-net-test.c +++ b/tests/virtio-net-test.c @@ -228,16 +228,16 @@ static void pci_basic(gconstpointer data) "virtio-net-pci,netdev=hs0", sv[1]); dev = virtio_net_pci_init(qs->pcibus, PCI_SLOT); - rx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); - tx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 1); + rx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, &qs->alloc, 0); + tx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, &qs->alloc, 1); driver_init(&dev->vdev); - func(&dev->vdev, qs->alloc, &rx->vq, &tx->vq, sv[0]); + func(&dev->vdev, &qs->alloc, &rx->vq, &tx->vq, sv[0]); /* End test */ close(sv[0]); - qvirtqueue_cleanup(dev->vdev.bus, &tx->vq, qs->alloc); - qvirtqueue_cleanup(dev->vdev.bus, &rx->vq, qs->alloc); + qvirtqueue_cleanup(dev->vdev.bus, &tx->vq, &qs->alloc); + qvirtqueue_cleanup(dev->vdev.bus, &rx->vq, &qs->alloc); qvirtio_pci_device_disable(dev); g_free(dev->pdev); g_free(dev); diff --git a/tests/virtio-scsi-test.c b/tests/virtio-scsi-test.c index 961925cec1..9298aa8877 100644 --- a/tests/virtio-scsi-test.c +++ b/tests/virtio-scsi-test.c @@ -62,7 +62,7 @@ static void qvirtio_scsi_pci_free(QVirtIOSCSI *vs) int i; for (i = 0; i < vs->num_queues + 2; i++) { - qvirtqueue_cleanup(vs->dev->bus, vs->vq[i], vs->qs->alloc); + qvirtqueue_cleanup(vs->dev->bus, vs->vq[i], &vs->qs->alloc); } qvirtio_pci_device_disable(container_of(vs->dev, QVirtioPCIDevice, vdev)); qvirtio_pci_device_free((QVirtioPCIDevice *)vs->dev); @@ -75,7 +75,7 @@ static uint64_t qvirtio_scsi_alloc(QVirtIOSCSI *vs, size_t alloc_size, { uint64_t addr; - addr = guest_alloc(vs->qs->alloc, alloc_size); + addr = guest_alloc(&vs->qs->alloc, alloc_size); if (data) { memwrite(addr, data, alloc_size); } @@ -133,10 +133,10 @@ static uint8_t virtio_scsi_do_command(QVirtIOSCSI *vs, const uint8_t *cdb, memread(resp_addr, resp_out, sizeof(*resp_out)); } - guest_free(vs->qs->alloc, req_addr); - guest_free(vs->qs->alloc, resp_addr); - guest_free(vs->qs->alloc, data_in_addr); - guest_free(vs->qs->alloc, data_out_addr); + guest_free(&vs->qs->alloc, req_addr); + guest_free(&vs->qs->alloc, resp_addr); + guest_free(&vs->qs->alloc, data_in_addr); + guest_free(&vs->qs->alloc, data_out_addr); return response; } @@ -166,7 +166,7 @@ static QVirtIOSCSI *qvirtio_scsi_pci_init(int slot) g_assert_cmpint(vs->num_queues, <, MAX_NUM_QUEUES); for (i = 0; i < vs->num_queues + 2; i++) { - vs->vq[i] = qvirtqueue_setup(vs->dev, vs->qs->alloc, i); + vs->vq[i] = qvirtqueue_setup(vs->dev, &vs->qs->alloc, i); } /* Clear the POWER ON OCCURRED unit attention */ From fc281c802022cb3a73a53386d761daa32dce5cf9 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Wed, 13 Jun 2018 17:07:21 +0200 Subject: [PATCH 05/57] tests: qgraph API for the qtest driver framework Add qgraph API that allows to add/remove nodes and edges from the graph, implementation of Depth First Search to discover the paths and basic unit test to check correctness of the API. Included also a main executable that takes care of starting the framework, create the nodes, set the available drivers/machines, discover the path and run tests. graph.h provides the public API to manage the graph nodes/edges graph_extra.h provides a more private API used successively by the gtest integration part qos-test.c provides the main executable Signed-off-by: Emanuele Giuseppe Esposito [Paolo's changes compared to the Google Summer of Code submission: * added subprocess to test options * refactored object creation to support live migration tests * removed driver .before callback (unused) * removed test .after callbacks (replaced by GTest destruction queue)] Signed-off-by: Paolo Bonzini --- configure | 2 +- include/qemu/module.h | 2 + tests/Makefile.include | 13 +- tests/libqos/qgraph.c | 753 +++++++++++++++++++++++++++++++++ tests/libqos/qgraph.h | 575 +++++++++++++++++++++++++ tests/libqos/qgraph_internal.h | 257 +++++++++++ tests/libqtest.h | 3 + tests/qos-test.c | 445 +++++++++++++++++++ tests/test-qgraph.c | 434 +++++++++++++++++++ 9 files changed, 2481 insertions(+), 3 deletions(-) create mode 100644 tests/libqos/qgraph.c create mode 100644 tests/libqos/qgraph.h create mode 100644 tests/libqos/qgraph_internal.h create mode 100644 tests/qos-test.c create mode 100644 tests/test-qgraph.c diff --git a/configure b/configure index cefeb8fcce..f7b8c2ea94 100755 --- a/configure +++ b/configure @@ -7647,7 +7647,7 @@ fi # tests might fail. Prefer to keep the relevant files in their own # directory and symlink the directory instead. DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests tests/vm" -DIRS="$DIRS tests/fp" +DIRS="$DIRS tests/fp tests/qgraph" DIRS="$DIRS docs docs/interop fsdev scsi" DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw" DIRS="$DIRS roms/seabios roms/vgabios" diff --git a/include/qemu/module.h b/include/qemu/module.h index 55dd2beea8..db3065381d 100644 --- a/include/qemu/module.h +++ b/include/qemu/module.h @@ -45,6 +45,7 @@ typedef enum { MODULE_INIT_QOM, MODULE_INIT_TRACE, MODULE_INIT_XEN_BACKEND, + MODULE_INIT_LIBQOS, MODULE_INIT_MAX } module_init_type; @@ -54,6 +55,7 @@ typedef enum { #define trace_init(function) module_init(function, MODULE_INIT_TRACE) #define xen_backend_init(function) module_init(function, \ MODULE_INIT_XEN_BACKEND) +#define libqos_init(function) module_init(function, MODULE_INIT_LIBQOS) #define block_module_load_one(lib) module_load_one("block-", lib) #define ui_module_load_one(lib) module_load_one("ui-", lib) diff --git a/tests/Makefile.include b/tests/Makefile.include index 97e1cb90a3..94ace7b675 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -732,7 +732,10 @@ tests/test-crypto-ivgen$(EXESUF): tests/test-crypto-ivgen.o $(test-crypto-obj-y) tests/test-crypto-afsplit$(EXESUF): tests/test-crypto-afsplit.o $(test-crypto-obj-y) tests/test-crypto-block$(EXESUF): tests/test-crypto-block.o $(test-crypto-obj-y) -libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o +libqgraph-obj-y = tests/libqos/qgraph.o + +libqos-obj-y = $(libqgraph-obj-y) tests/libqos/pci.o tests/libqos/fw_cfg.o +libqos-obj-y += tests/libqos/malloc.o tests/libqos/malloc-generic.o libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o libqos-spapr-obj-y = $(libqos-obj-y) tests/libqos/malloc-spapr.o libqos-spapr-obj-y += tests/libqos/libqos-spapr.o @@ -744,7 +747,13 @@ libqos-pc-obj-y += tests/libqos/ahci.o libqos-omap-obj-y = $(libqos-obj-y) tests/libqos/i2c-omap.o libqos-imx-obj-y = $(libqos-obj-y) tests/libqos/i2c-imx.o libqos-usb-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/usb.o -libqos-virtio-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o tests/libqos/malloc-generic.o +libqos-virtio-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o + +check-unit-y += tests/test-qgraph$(EXESUF) +tests/test-qgraph$(EXESUF): tests/test-qgraph.o $(libqgraph-obj-y) + +check-qtest-generic-y += tests/qos-test$(EXESUF) +tests/qos-test$(EXESUF): tests/qos-test.o $(libqgraph-obj-y) tests/qmp-test$(EXESUF): tests/qmp-test.o tests/qmp-cmd-test$(EXESUF): tests/qmp-cmd-test.o diff --git a/tests/libqos/qgraph.c b/tests/libqos/qgraph.c new file mode 100644 index 0000000000..122efc1b7b --- /dev/null +++ b/tests/libqos/qgraph.c @@ -0,0 +1,753 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/queue.h" +#include "libqos/qgraph_internal.h" +#include "libqos/qgraph.h" + +#define QGRAPH_PRINT_DEBUG 0 +#define QOS_ROOT "" +typedef struct QOSStackElement QOSStackElement; + +/* Graph Edge.*/ +struct QOSGraphEdge { + QOSEdgeType type; + char *dest; + void *arg; /* just for QEDGE_CONTAINS + * and QEDGE_CONSUMED_BY */ + char *extra_device_opts; /* added to -device option, "," is + * automatically added + */ + char *before_cmd_line; /* added before node cmd_line */ + char *after_cmd_line; /* added after -device options */ + char *edge_name; /* used by QEDGE_CONTAINS */ + QSLIST_ENTRY(QOSGraphEdge) edge_list; +}; + +typedef QSLIST_HEAD(, QOSGraphEdge) QOSGraphEdgeList; + +/** + * Stack used to keep track of the discovered path when using + * the DFS algorithm + */ +struct QOSStackElement { + QOSGraphNode *node; + QOSStackElement *parent; + QOSGraphEdge *parent_edge; + int length; +}; + +/* Each enty in these hash table will consist of pair. */ +static GHashTable *edge_table; +static GHashTable *node_table; + +/* stack used by the DFS algorithm to store the path from machine to test */ +static QOSStackElement qos_node_stack[QOS_PATH_MAX_ELEMENT_SIZE]; +static int qos_node_tos; + +/** + * add_edge(): creates an edge of type @type + * from @source to @dest node, and inserts it in the + * edges hash table + * + * Nodes @source and @dest do not necessarily need to exist. + * Possibility to add also options (see #QOSGraphEdgeOptions) + * edge->edge_name is used as identifier for get_device relationships, + * so by default is equal to @dest. + */ +static void add_edge(const char *source, const char *dest, + QOSEdgeType type, QOSGraphEdgeOptions *opts) +{ + char *key; + QOSGraphEdgeList *list = g_hash_table_lookup(edge_table, source); + + if (!list) { + list = g_new0(QOSGraphEdgeList, 1); + key = g_strdup(source); + g_hash_table_insert(edge_table, key, list); + } + + if (!opts) { + opts = &(QOSGraphEdgeOptions) { }; + } + + QOSGraphEdge *edge = g_new0(QOSGraphEdge, 1); + edge->type = type; + edge->dest = g_strdup(dest); + edge->edge_name = g_strdup(opts->edge_name ?: dest); + edge->arg = g_memdup(opts->arg, opts->size_arg); + + edge->before_cmd_line = + opts->before_cmd_line ? g_strconcat(" ", opts->before_cmd_line, NULL) : NULL; + edge->extra_device_opts = + opts->extra_device_opts ? g_strconcat(",", opts->extra_device_opts, NULL) : NULL; + edge->after_cmd_line = + opts->after_cmd_line ? g_strconcat(" ", opts->after_cmd_line, NULL) : NULL; + + QSLIST_INSERT_HEAD(list, edge, edge_list); +} + +/* destroy_edges(): frees all edges inside a given @list */ +static void destroy_edges(void *list) +{ + QOSGraphEdge *temp; + QOSGraphEdgeList *elist = list; + + while (!QSLIST_EMPTY(elist)) { + temp = QSLIST_FIRST(elist); + QSLIST_REMOVE_HEAD(elist, edge_list); + g_free(temp->dest); + g_free(temp->before_cmd_line); + g_free(temp->after_cmd_line); + g_free(temp->extra_device_opts); + g_free(temp->edge_name); + g_free(temp->arg); + g_free(temp); + } + g_free(elist); +} + +/** + * create_node(): creates a node @name of type @type + * and inserts it to the nodes hash table. + * By default, node is not available. + */ +static QOSGraphNode *create_node(const char *name, QOSNodeType type) +{ + if (g_hash_table_lookup(node_table, name)) { + g_printerr("Node %s already created\n", name); + abort(); + } + + QOSGraphNode *node = g_new0(QOSGraphNode, 1); + node->type = type; + node->available = false; + node->name = g_strdup(name); + g_hash_table_insert(node_table, node->name, node); + return node; +} + +/** + * destroy_node(): frees a node @val from the nodes hash table. + * Note that node->name is not free'd since it will represent the + * hash table key + */ +static void destroy_node(void *val) +{ + QOSGraphNode *node = val; + g_free(node->command_line); + g_free(node); +} + +/** + * destroy_string(): frees @key from the nodes hash table. + * Actually frees the node->name + */ +static void destroy_string(void *key) +{ + g_free(key); +} + +/** + * search_node(): search for a node @key in the nodes hash table + * Returns the QOSGraphNode if found, #NULL otherwise + */ +static QOSGraphNode *search_node(const char *key) +{ + return g_hash_table_lookup(node_table, key); +} + +/** + * get_edgelist(): returns the edge list (value) assigned to + * the @key in the edge hash table. + * This list will contain all edges with source equal to @key + * + * Returns: on success: the %QOSGraphEdgeList + * otherwise: abort() + */ +static QOSGraphEdgeList *get_edgelist(const char *key) +{ + return g_hash_table_lookup(edge_table, key); +} + +/** + * search_list_edges(): search for an edge with destination @dest + * in the given @edgelist. + * + * Returns: on success: the %QOSGraphEdge + * otherwise: #NULL + */ +static QOSGraphEdge *search_list_edges(QOSGraphEdgeList *edgelist, + const char *dest) +{ + QOSGraphEdge *tmp, *next; + if (!edgelist) { + return NULL; + } + QSLIST_FOREACH_SAFE(tmp, edgelist, edge_list, next) { + if (g_strcmp0(tmp->dest, dest) == 0) { + break; + } + } + return tmp; +} + +/** + * search_machine(): search for a machine @name in the node hash + * table. A machine is the child of the root node. + * This function forces the research in the childs of the root, + * to check the node is a proper machine + * + * Returns: on success: the %QOSGraphNode + * otherwise: #NULL + */ +static QOSGraphNode *search_machine(const char *name) +{ + QOSGraphNode *n; + QOSGraphEdgeList *root_list = get_edgelist(QOS_ROOT); + QOSGraphEdge *e = search_list_edges(root_list, name); + if (!e) { + return NULL; + } + n = search_node(e->dest); + if (n->type == QNODE_MACHINE) { + return n; + } + return NULL; +} + +/** + * create_interface(): checks if there is already + * a node @node in the node hash table, if not + * creates a node @node of type #QNODE_INTERFACE + * and inserts it. If there is one, check it's + * a #QNODE_INTERFACE and abort() if it's not. + */ +static void create_interface(const char *node) +{ + QOSGraphNode *interface; + interface = search_node(node); + if (!interface) { + create_node(node, QNODE_INTERFACE); + } else if (interface->type != QNODE_INTERFACE) { + fprintf(stderr, "Error: Node %s is not an interface\n", node); + abort(); + } +} + +/** + * build_machine_cmd_line(): builds the command line for the machine + * @node. The node name must be a valid qemu identifier, since it + * will be used to build the command line. + * + * It is also possible to pass an optional @args that will be + * concatenated to the command line. + * + * For machines, prepend -M to the machine name. ", @rgs" is added + * after the -M command. + */ +static void build_machine_cmd_line(QOSGraphNode *node, const char *args) +{ + char *machine = qos_get_machine_type(node->name); + if (args) { + node->command_line = g_strconcat("-M ", machine, ",", args, NULL); + } else { + node->command_line = g_strconcat("-M ", machine, " ", NULL); + } +} + +/** + * build_driver_cmd_line(): builds the command line for the driver + * @node. The node name must be a valid qemu identifier, since it + * will be used to build the command line. + * + * Driver do not need additional command line, since it will be + * provided by the edge options. + * + * For drivers, prepend -device to the node name. + */ +static void build_driver_cmd_line(QOSGraphNode *node) +{ + node->command_line = g_strconcat(" -device ", node->name, NULL); +} + +/* qos_print_cb(): callback prints all path found by the DFS algorithm. */ +static void qos_print_cb(QOSGraphNode *path, int length) +{ + #if QGRAPH_PRINT_DEBUG + printf("%d elements\n", length); + + if (!path) { + return; + } + + while (path->path_edge) { + printf("%s ", path->name); + switch (path->path_edge->type) { + case QEDGE_PRODUCES: + printf("--PRODUCES--> "); + break; + case QEDGE_CONSUMED_BY: + printf("--CONSUMED_BY--> "); + break; + case QEDGE_CONTAINS: + printf("--CONTAINS--> "); + break; + } + path = search_node(path->path_edge->dest); + } + + printf("%s\n\n", path->name); + #endif +} + +/* qos_push(): push a node @el and edge @e in the qos_node_stack */ +static void qos_push(QOSGraphNode *el, QOSStackElement *parent, + QOSGraphEdge *e) +{ + int len = 0; /* root is not counted */ + if (qos_node_tos == QOS_PATH_MAX_ELEMENT_SIZE) { + g_printerr("QOSStack: full stack, cannot push"); + abort(); + } + + if (parent) { + len = parent->length + 1; + } + qos_node_stack[qos_node_tos++] = (QOSStackElement) { + .node = el, + .parent = parent, + .parent_edge = e, + .length = len, + }; +} + +/* qos_tos(): returns the top of stack, without popping */ +static QOSStackElement *qos_tos(void) +{ + return &qos_node_stack[qos_node_tos - 1]; +} + +/* qos_pop(): pops an element from the tos, setting it unvisited*/ +static QOSStackElement *qos_pop(void) +{ + if (qos_node_tos == 0) { + g_printerr("QOSStack: empty stack, cannot pop"); + abort(); + } + QOSStackElement *e = qos_tos(); + e->node->visited = false; + qos_node_tos--; + return e; +} + +/** + * qos_reverse_path(): reverses the found path, going from + * test-to-machine to machine-to-test + */ +static QOSGraphNode *qos_reverse_path(QOSStackElement *el) +{ + if (!el) { + return NULL; + } + + el->node->path_edge = NULL; + + while (el->parent) { + el->parent->node->path_edge = el->parent_edge; + el = el->parent; + } + + return el->node; +} + +/** + * qos_traverse_graph(): graph-walking algorithm, using Depth First Search it + * starts from the root @machine and walks all possible path until it + * reaches a test node. + * At that point, it reverses the path found and invokes the @callback. + * + * Being Depth First Search, time complexity is O(|V| + |E|), while + * space is O(|V|). In this case, the maximum stack size is set by + * QOS_PATH_MAX_ELEMENT_SIZE. + */ +static void qos_traverse_graph(QOSGraphNode *root, QOSTestCallback callback) +{ + QOSGraphNode *v, *dest_node, *path; + QOSStackElement *s_el; + QOSGraphEdge *e, *next; + QOSGraphEdgeList *list; + + qos_push(root, NULL, NULL); + + while (qos_node_tos > 0) { + s_el = qos_tos(); + v = s_el->node; + if (v->visited) { + qos_pop(); + continue; + } + v->visited = true; + list = get_edgelist(v->name); + if (!list) { + qos_pop(); + if (v->type == QNODE_TEST) { + v->visited = false; + path = qos_reverse_path(s_el); + callback(path, s_el->length); + } + } else { + QSLIST_FOREACH_SAFE(e, list, edge_list, next) { + dest_node = search_node(e->dest); + + if (!dest_node) { + fprintf(stderr, "node %s in %s -> %s does not exist\n", + e->dest, v->name, e->dest); + abort(); + } + + if (!dest_node->visited && dest_node->available) { + qos_push(dest_node, s_el, e); + } + } + } + } +} + +/* QGRAPH API*/ + +QOSGraphNode *qos_graph_get_node(const char *key) +{ + return search_node(key); +} + +bool qos_graph_has_node(const char *node) +{ + QOSGraphNode *n = search_node(node); + return n != NULL; +} + +QOSNodeType qos_graph_get_node_type(const char *node) +{ + QOSGraphNode *n = search_node(node); + if (n) { + return n->type; + } + return -1; +} + +bool qos_graph_get_node_availability(const char *node) +{ + QOSGraphNode *n = search_node(node); + if (n) { + return n->available; + } + return false; +} + +QOSGraphEdge *qos_graph_get_edge(const char *node, const char *dest) +{ + QOSGraphEdgeList *list = get_edgelist(node); + return search_list_edges(list, dest); +} + +QOSEdgeType qos_graph_edge_get_type(QOSGraphEdge *edge) +{ + if (!edge) { + return -1; + } + return edge->type;; +} + +char *qos_graph_edge_get_dest(QOSGraphEdge *edge) +{ + if (!edge) { + return NULL; + } + return edge->dest; +} + +void *qos_graph_edge_get_arg(QOSGraphEdge *edge) +{ + if (!edge) { + return NULL; + } + return edge->arg; +} + +char *qos_graph_edge_get_after_cmd_line(QOSGraphEdge *edge) +{ + if (!edge) { + return NULL; + } + return edge->after_cmd_line; +} + +char *qos_graph_edge_get_before_cmd_line(QOSGraphEdge *edge) +{ + if (!edge) { + return NULL; + } + return edge->before_cmd_line; +} + +char *qos_graph_edge_get_extra_device_opts(QOSGraphEdge *edge) +{ + if (!edge) { + return NULL; + } + return edge->extra_device_opts; +} + +char *qos_graph_edge_get_name(QOSGraphEdge *edge) +{ + if (!edge) { + return NULL; + } + return edge->edge_name; +} + +bool qos_graph_has_edge(const char *start, const char *dest) +{ + QOSGraphEdgeList *list = get_edgelist(start); + QOSGraphEdge *e = search_list_edges(list, dest); + return e != NULL; +} + +QOSGraphNode *qos_graph_get_machine(const char *node) +{ + return search_machine(node); +} + +bool qos_graph_has_machine(const char *node) +{ + QOSGraphNode *m = search_machine(node); + return m != NULL; +} + +void qos_print_graph(void) +{ + qos_graph_foreach_test_path(qos_print_cb); +} + +void qos_graph_init(void) +{ + if (!node_table) { + node_table = g_hash_table_new_full(g_str_hash, g_str_equal, + destroy_string, destroy_node); + create_node(QOS_ROOT, QNODE_DRIVER); + } + + if (!edge_table) { + edge_table = g_hash_table_new_full(g_str_hash, g_str_equal, + destroy_string, destroy_edges); + } +} + +void qos_graph_destroy(void) +{ + if (node_table) { + g_hash_table_destroy(node_table); + } + + if (edge_table) { + g_hash_table_destroy(edge_table); + } + + node_table = NULL; + edge_table = NULL; +} + +void qos_node_destroy(void *key) +{ + g_hash_table_remove(node_table, key); +} + +void qos_edge_destroy(void *key) +{ + g_hash_table_remove(edge_table, key); +} + +void qos_add_test(const char *name, const char *interface, + QOSTestFunc test_func, QOSGraphTestOptions *opts) +{ + QOSGraphNode *node; + char *test_name = g_strdup_printf("%s-tests/%s", interface, name);; + + if (!opts) { + opts = &(QOSGraphTestOptions) { }; + } + node = create_node(test_name, QNODE_TEST); + node->u.test.function = test_func; + node->u.test.arg = opts->arg; + assert(!opts->edge.arg); + assert(!opts->edge.size_arg); + + node->u.test.before = opts->before; + node->u.test.subprocess = opts->subprocess; + node->available = true; + add_edge(interface, test_name, QEDGE_CONSUMED_BY, &opts->edge); + g_free(test_name); +} + +void qos_node_create_machine(const char *name, QOSCreateMachineFunc function) +{ + qos_node_create_machine_args(name, function, NULL); +} + +void qos_node_create_machine_args(const char *name, + QOSCreateMachineFunc function, + const char *opts) +{ + QOSGraphNode *node = create_node(name, QNODE_MACHINE); + build_machine_cmd_line(node, opts); + node->u.machine.constructor = function; + add_edge(QOS_ROOT, name, QEDGE_CONTAINS, NULL); +} + +void qos_node_create_driver(const char *name, QOSCreateDriverFunc function) +{ + QOSGraphNode *node = create_node(name, QNODE_DRIVER); + build_driver_cmd_line(node); + node->u.driver.constructor = function; +} + +void qos_node_contains(const char *container, const char *contained, + ...) +{ + va_list va; + va_start(va, contained); + QOSGraphEdgeOptions *opts; + + do { + opts = va_arg(va, QOSGraphEdgeOptions *); + add_edge(container, contained, QEDGE_CONTAINS, opts); + } while (opts != NULL); + + va_end(va); +} + +void qos_node_produces(const char *producer, const char *interface) +{ + create_interface(interface); + add_edge(producer, interface, QEDGE_PRODUCES, NULL); +} + +void qos_node_consumes(const char *consumer, const char *interface, + QOSGraphEdgeOptions *opts) +{ + create_interface(interface); + add_edge(interface, consumer, QEDGE_CONSUMED_BY, opts); +} + +void qos_graph_node_set_availability(const char *node, bool av) +{ + QOSGraphEdgeList *elist; + QOSGraphNode *n = search_node(node); + QOSGraphEdge *e, *next; + if (!n) { + return; + } + n->available = av; + elist = get_edgelist(node); + if (!elist) { + return; + } + QSLIST_FOREACH_SAFE(e, elist, edge_list, next) { + if (e->type == QEDGE_CONTAINS || e->type == QEDGE_PRODUCES) { + qos_graph_node_set_availability(e->dest, av); + } + } +} + +void qos_graph_foreach_test_path(QOSTestCallback fn) +{ + QOSGraphNode *root = qos_graph_get_node(QOS_ROOT); + qos_traverse_graph(root, fn); +} + +QOSGraphObject *qos_machine_new(QOSGraphNode *node, QTestState *qts) +{ + QOSGraphObject *obj; + + g_assert(node->type == QNODE_MACHINE); + obj = node->u.machine.constructor(qts); + obj->free = g_free; + return obj; +} + +QOSGraphObject *qos_driver_new(QOSGraphNode *node, QOSGraphObject *parent, + QGuestAllocator *alloc, void *arg) +{ + QOSGraphObject *obj; + + g_assert(node->type == QNODE_DRIVER); + obj = node->u.driver.constructor(parent, alloc, arg); + obj->free = g_free; + return obj; +} + +void qos_object_destroy(QOSGraphObject *obj) +{ + if (!obj) { + return; + } + if (obj->destructor) { + obj->destructor(obj); + } + if (obj->free) { + obj->free(obj); + } +} + +void qos_object_queue_destroy(QOSGraphObject *obj) +{ + g_test_queue_destroy((GDestroyNotify) qos_object_destroy, obj); +} + +void qos_object_start_hw(QOSGraphObject *obj) +{ + if (obj->start_hw) { + obj->start_hw(obj); + } +} + +char *qos_get_machine_type(char *name) +{ + while (*name != '\0' && *name != '/') { + name++; + } + + if (!*name || !name[1]) { + fprintf(stderr, "Machine name has to be of the form /\n"); + abort(); + } + + return name + 1; +} + +void qos_delete_cmd_line(const char *name) +{ + QOSGraphNode *node = search_node(name); + if (node) { + g_free(node->command_line); + node->command_line = NULL; + } +} diff --git a/tests/libqos/qgraph.h b/tests/libqos/qgraph.h new file mode 100644 index 0000000000..ef0c73837a --- /dev/null +++ b/tests/libqos/qgraph.h @@ -0,0 +1,575 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#ifndef QGRAPH_H +#define QGRAPH_H + +#include +#include +#include +#include +#include +#include "qemu/module.h" +#include "malloc.h" + +/* maximum path length */ +#define QOS_PATH_MAX_ELEMENT_SIZE 50 + +typedef struct QOSGraphObject QOSGraphObject; +typedef struct QOSGraphNode QOSGraphNode; +typedef struct QOSGraphEdge QOSGraphEdge; +typedef struct QOSGraphNodeOptions QOSGraphNodeOptions; +typedef struct QOSGraphEdgeOptions QOSGraphEdgeOptions; +typedef struct QOSGraphTestOptions QOSGraphTestOptions; + +/* Constructor for drivers, machines and test */ +typedef void *(*QOSCreateDriverFunc) (void *parent, QGuestAllocator *alloc, + void *addr); +typedef void *(*QOSCreateMachineFunc) (QTestState *qts); +typedef void (*QOSTestFunc) (void *parent, void *arg, QGuestAllocator *alloc); + +/* QOSGraphObject functions */ +typedef void *(*QOSGetDriver) (void *object, const char *interface); +typedef QOSGraphObject *(*QOSGetDevice) (void *object, const char *name); +typedef void (*QOSDestructorFunc) (QOSGraphObject *object); +typedef void (*QOSStartFunct) (QOSGraphObject *object); + +/* Test options functions */ +typedef void *(*QOSBeforeTest) (GString *cmd_line, void *arg); + +/** + * SECTION: qgraph.h + * @title: Qtest Driver Framework + * @short_description: interfaces to organize drivers and tests + * as nodes in a graph + * + * This Qgraph API provides all basic functions to create a graph + * and instantiate nodes representing machines, drivers and tests + * representing their relations with CONSUMES, PRODUCES, and CONTAINS + * edges. + * + * The idea is to have a framework where each test asks for a specific + * driver, and the framework takes care of allocating the proper devices + * required and passing the correct command line arguments to QEMU. + * + * A node can be of four types: + * - QNODE_MACHINE: for example "arm/raspi2" + * - QNODE_DRIVER: for example "generic-sdhci" + * - QNODE_INTERFACE: for example "sdhci" (interface for all "-sdhci" drivers) + * an interface is not explicitly created, it will be auto- + * matically instantiated when a node consumes or produces + * it. + * - QNODE_TEST: for example "sdhci-test", consumes an interface and tests + * the functions provided + * + * Notes for the nodes: + * - QNODE_MACHINE: each machine struct must have a QGuestAllocator and + * implement get_driver to return the allocator passing + * "memory". The function can also return NULL if the + * allocator is not set. + * - QNODE_DRIVER: driver names must be unique, and machines and nodes + * planned to be "consumed" by other nodes must match QEMU + * drivers name, otherwise they won't be discovered + * + * An edge relation between two nodes (drivers or machines) X and Y can be: + * - X CONSUMES Y: Y can be plugged into X + * - X PRODUCES Y: X provides the interface Y + * - X CONTAINS Y: Y is part of X component + * + * Basic framework steps are the following: + * - All nodes and edges are created in their respective + * machine/driver/test files + * - The framework starts QEMU and asks for a list of available devices + * and machines (note that only machines and "consumed" nodes are mapped + * 1:1 with QEMU devices) + * - The framework walks the graph starting from the available machines and + * performs a Depth First Search for tests + * - Once a test is found, the path is walked again and all drivers are + * allocated accordingly and the final interface is passed to the test + * - The test is executed + * - Unused objects are cleaned and the path discovery is continued + * + * Depending on the QEMU binary used, only some drivers/machines will be + * available and only test that are reached by them will be executed. + * + * + * Creating new driver an its interface + * + #include "libqos/qgraph.h" + + struct My_driver { + QOSGraphObject obj; + Node_produced prod; + Node_contained cont; + } + + static void my_destructor(QOSGraphObject *obj) + { + g_free(obj); + } + + static void my_get_driver(void *object, const char *interface) { + My_driver *dev = object; + if (!g_strcmp0(interface, "my_interface")) { + return &dev->prod; + } + abort(); + } + + static void my_get_device(void *object, const char *device) { + My_driver *dev = object; + if (!g_strcmp0(device, "my_driver_contained")) { + return &dev->cont; + } + abort(); + } + + static void *my_driver_constructor(void *node_consumed, + QOSGraphObject *alloc) + { + My_driver dev = g_new(My_driver, 1); + // get the node pointed by the produce edge + dev->obj.get_driver = my_get_driver; + // get the node pointed by the contains + dev->obj.get_device = my_get_device; + // free the object + dev->obj.destructor = my_destructor; + do_something_with_node_consumed(node_consumed); + // set all fields of contained device + init_contained_device(&dev->cont); + return &dev->obj; + } + + static void register_my_driver(void) + { + qos_node_create_driver("my_driver", my_driver_constructor); + // contained drivers don't need a constructor, + // they will be init by the parent. + qos_node_create_driver("my_driver_contained", NULL); + + // For the sake of this example, assume machine x86_64/pc contains + // "other_node". + // This relation, along with the machine and "other_node" creation, + // should be defined in the x86_64_pc-machine.c file. + // "my_driver" will then consume "other_node" + qos_node_contains("my_driver", "my_driver_contained"); + qos_node_produces("my_driver", "my_interface"); + qos_node_consumes("my_driver", "other_node"); + } + * + * + * + * In the above example, all possible types of relations are created: + * node "my_driver" consumes, contains and produces other nodes. + * more specifically: + * x86_64/pc -->contains--> other_node <--consumes-- my_driver + * | + * my_driver_contained <--contains--+ + * | + * my_interface <--produces--+ + * + * or inverting the consumes edge in consumed_by: + * + * x86_64/pc -->contains--> other_node --consumed_by--> my_driver + * | + * my_driver_contained <--contains--+ + * | + * my_interface <--produces--+ + * + * + * Creating new test + * + * #include "libqos/qgraph.h" + * + * static void my_test_function(void *obj, void *data) + * { + * Node_produced *interface_to_test = obj; + * // test interface_to_test + * } + * + * static void register_my_test(void) + * { + * qos_add_test("my_interface", "my_test", my_test_function); + * } + * + * libqos_init(register_my_test); + * + * + * + * + * Here a new test is created, consuming "my_interface" node + * and creating a valid path from a machine to a test. + * Final graph will be like this: + * x86_64/pc -->contains--> other_node <--consumes-- my_driver + * | + * my_driver_contained <--contains--+ + * | + * my_test --consumes--> my_interface <--produces--+ + * + * or inverting the consumes edge in consumed_by: + * + * x86_64/pc -->contains--> other_node --consumed_by--> my_driver + * | + * my_driver_contained <--contains--+ + * | + * my_test <--consumed_by-- my_interface <--produces--+ + * + * Assuming there the binary is + * QTEST_QEMU_BINARY=x86_64-softmmu/qemu-system-x86_64 + * a valid test path will be: + * "/x86_64/pc/other_node/my_driver/my_interface/my_test". + * + * Additional examples are also in libqos/test-qgraph.c + * + * Command line: + * Command line is built by using node names and optional arguments + * passed by the user when building the edges. + * + * There are three types of command line arguments: + * - in node : created from the node name. For example, machines will + * have "-M " to its command line, while devices + * "-device ". It is automatically done by the + * framework. + * - after node : added as additional argument to the node name. + * This argument is added optionally when creating edges, + * by setting the parameter @after_cmd_line and + * @extra_edge_opts in #QOSGraphEdgeOptions. + * The framework automatically adds + * a comma before @extra_edge_opts, + * because it is going to add attributes + * after the destination node pointed by + * the edge containing these options, and automatically + * adds a space before @after_cmd_line, because it + * adds an additional device, not an attribute. + * - before node : added as additional argument to the node name. + * This argument is added optionally when creating edges, + * by setting the parameter @before_cmd_line in + * #QOSGraphEdgeOptions. This attribute + * is going to add attributes before the destination node + * pointed by the edge containing these options. It is + * helpful to commands that are not node-representable, + * such as "-fdsev" or "-netdev". + * + * While adding command line in edges is always used, not all nodes names are + * used in every path walk: this is because the contained or produced ones + * are already added by QEMU, so only nodes that "consumes" will be used to + * build the command line. Also, nodes that will have { "abstract" : true } + * as QMP attribute will loose their command line, since they are not proper + * devices to be added in QEMU. + * + * Example: + * + QOSGraphEdgeOptions opts = { + .arg = NULL, + .size_arg = 0, + .after_cmd_line = "-device other", + .before_cmd_line = "-netdev something", + .extra_edge_opts = "addr=04.0", + }; + QOSGraphNode * node = qos_node_create_driver("my_node", constructor); + qos_node_consumes_args("my_node", "interface", &opts); + * + * Will produce the following command line: + * "-netdev something -device my_node,addr=04.0 -device other" + */ + +/** + * Edge options to be passed to the contains/consumes *_args function. + */ +struct QOSGraphEdgeOptions { + void *arg; /* + * optional arg that will be used by + * dest edge + */ + uint32_t size_arg; /* + * optional arg size that will be used by + * dest edge + */ + const char *extra_device_opts;/* + *optional additional command line for dest + * edge, used to add additional attributes + * *after* the node command line, the + * framework automatically prepends "," + * to this argument. + */ + const char *before_cmd_line; /* + * optional additional command line for dest + * edge, used to add additional attributes + * *before* the node command line, usually + * other non-node represented commands, + * like "-fdsev synt" + */ + const char *after_cmd_line; /* + * optional extra command line to be added + * after the device command. This option + * is used to add other devices + * command line that depend on current node. + * Automatically prepends " " to this + * argument + */ + const char *edge_name; /* + * optional edge to differentiate multiple + * devices with same node name + */ +}; + +/** + * Test options to be passed to the test functions. + */ +struct QOSGraphTestOptions { + QOSGraphEdgeOptions edge; /* edge arguments that will be used by test. + * Note that test *does not* use edge_name, + * and uses instead arg and size_arg as + * data arg for its test function. + */ + void *arg; /* passed to the .before function, or to the + * test function if there is no .before + * function + */ + QOSBeforeTest before; /* executed before the test. Can add + * additional parameters to the command line + * and modify the argument to the test function. + */ + bool subprocess; /* run the test in a subprocess */ +}; + +/** + * Each driver, test or machine of this framework will have a + * QOSGraphObject as first field. + * + * This set of functions offered by QOSGraphObject are executed + * in different stages of the framework: + * - get_driver / get_device : Once a machine-to-test path has been + * found, the framework traverses it again and allocates all the + * nodes, using the provided constructor. To satisfy their relations, + * i.e. for produces or contains, where a struct constructor needs + * an external parameter represented by the previous node, + * the framework will call get_device (for contains) or + * get_driver (for produces), depending on the edge type, passing + * them the name of the next node to be taken and getting from them + * the corresponding pointer to the actual structure of the next node to + * be used in the path. + * + * - start_hw: This function is executed after all the path objects + * have been allocated, but before the test is run. It starts the hw, setting + * the initial configurations (*_device_enable) and making it ready for the + * test. + * + * - destructor: Opposite to the node constructor, destroys the object. + * This function is called after the test has been executed, and performs + * a complete cleanup of each node allocated field. In case no constructor + * is provided, no destructor will be called. + * + */ +struct QOSGraphObject { + /* for produces edges, returns void * */ + QOSGetDriver get_driver; + /* for contains edges, returns a QOSGraphObject * */ + QOSGetDevice get_device; + /* start the hw, get ready for the test */ + QOSStartFunct start_hw; + /* destroy this QOSGraphObject */ + QOSDestructorFunc destructor; + /* free the memory associated to the QOSGraphObject and its contained + * children */ + GDestroyNotify free; +}; + +/** + * qos_graph_init(): initialize the framework, creates two hash + * tables: one for the nodes and another for the edges. + */ +void qos_graph_init(void); + +/** + * qos_graph_destroy(): deallocates all the hash tables, + * freeing all nodes and edges. + */ +void qos_graph_destroy(void); + +/** + * qos_node_destroy(): removes and frees a node from the, + * nodes hash table. + */ +void qos_node_destroy(void *key); + +/** + * qos_edge_destroy(): removes and frees an edge from the, + * edges hash table. + */ +void qos_edge_destroy(void *key); + +/** + * qos_add_test(): adds a test node @name to the nodes hash table. + * + * The test will consume a @interface node, and once the + * graph walking algorithm has found it, the @test_func will be + * executed. It also has the possibility to + * add an optional @opts (see %QOSGraphNodeOptions). + * + * For tests, opts->edge.arg and size_arg represent the arg to pass + * to @test_func + */ +void qos_add_test(const char *name, const char *interface, + QOSTestFunc test_func, + QOSGraphTestOptions *opts); + +/** + * qos_node_create_machine(): creates the machine @name and + * adds it to the node hash table. + * + * This node will be of type QNODE_MACHINE and have @function + * as constructor + */ +void qos_node_create_machine(const char *name, QOSCreateMachineFunc function); + +/** + * qos_node_create_machine_args(): same as qos_node_create_machine, + * but with the possibility to add an optional ", @opts" after -M machine + * command line. + */ +void qos_node_create_machine_args(const char *name, + QOSCreateMachineFunc function, + const char *opts); + +/** + * qos_node_create_driver(): creates the driver @name and + * adds it to the node hash table. + * + * This node will be of type QNODE_DRIVER and have @function + * as constructor + */ +void qos_node_create_driver(const char *name, QOSCreateDriverFunc function); + +/** + * qos_node_contains(): creates an edge of type QEDGE_CONTAINS and + * adds it to the edge list mapped to @container in the + * edge hash table. + * + * This edge will have @container as source and @contained as destination. + * + * It also has the possibility to add optional NULL-terminated + * @opts parameters (see %QOSGraphEdgeOptions) + * + * This function can be useful when there are multiple devices + * with the same node name contained in a machine/other node + * + * For example, if "arm/raspi2" contains 2 "generic-sdhci" + * devices, the right commands will be: + * qos_node_create_machine("arm/raspi2"); + * qos_node_create_driver("generic-sdhci", constructor); + * //assume rest of the fields are set NULL + * QOSGraphEdgeOptions op1 = { .edge_name = "emmc" }; + * QOSGraphEdgeOptions op2 = { .edge_name = "sdcard" }; + * qos_node_contains("arm/raspi2", "generic-sdhci", &op1, &op2, NULL); + * + * Of course this also requires that the @container's get_device function + * should implement a case for "emmc" and "sdcard". + * + * For contains, op1.arg and op1.size_arg represent the arg to pass + * to @contained constructor to properly initialize it. + */ +void qos_node_contains(const char *container, const char *contained, ...); + +/** + * qos_node_produces(): creates an edge of type QEDGE_PRODUCES and + * adds it to the edge list mapped to @producer in the + * edge hash table. + * + * This edge will have @producer as source and @interface as destination. + */ +void qos_node_produces(const char *producer, const char *interface); + +/** + * qos_node_consumes(): creates an edge of type QEDGE_CONSUMED_BY and + * adds it to the edge list mapped to @interface in the + * edge hash table. + * + * This edge will have @interface as source and @consumer as destination. + * It also has the possibility to add an optional @opts + * (see %QOSGraphEdgeOptions) + */ +void qos_node_consumes(const char *consumer, const char *interface, + QOSGraphEdgeOptions *opts); + +/** + * qos_invalidate_command_line(): invalidates current command line, so that + * qgraph framework cannot try to cache the current command line and + * forces QEMU to restart. + */ +void qos_invalidate_command_line(void); + +/** + * qos_get_current_command_line(): return the command line required by the + * machine and driver objects. This is the same string that was passed to + * the test's "before" callback, if any. + */ +const char *qos_get_current_command_line(void); + +/** + * qos_allocate_objects(): + * @qts: The #QTestState that will be referred to by the machine object. + * @alloc: Where to store the allocator for the machine object, or %NULL. + * + * Allocate driver objects for the current test + * path, but relative to the QTestState @qts. + * + * Returns a test object just like the one that was passed to + * the test function, but relative to @qts. + */ +void *qos_allocate_objects(QTestState *qts, QGuestAllocator **p_alloc); + +/** + * qos_object_destroy(): calls the destructor for @obj + */ +void qos_object_destroy(QOSGraphObject *obj); + +/** + * qos_object_queue_destroy(): queue the destructor for @obj so that it is + * called at the end of the test + */ +void qos_object_queue_destroy(QOSGraphObject *obj); + +/** + * qos_object_start_hw(): calls the start_hw function for @obj + */ +void qos_object_start_hw(QOSGraphObject *obj); + +/** + * qos_machine_new(): instantiate a new machine node + * @node: A machine node to be instantiated + * @qts: The #QTestState that will be referred to by the machine object. + * + * Returns a machine object. + */ +QOSGraphObject *qos_machine_new(QOSGraphNode *node, QTestState *qts); + +/** + * qos_machine_new(): instantiate a new driver node + * @node: A driver node to be instantiated + * @parent: A #QOSGraphObject to be consumed by the new driver node + * @alloc: An allocator to be used by the new driver node. + * @arg: The argument for the consumed-by edge to @node. + * + * Calls the constructor for the driver object. + */ +QOSGraphObject *qos_driver_new(QOSGraphNode *node, QOSGraphObject *parent, + QGuestAllocator *alloc, void *arg); + + +#endif diff --git a/tests/libqos/qgraph_internal.h b/tests/libqos/qgraph_internal.h new file mode 100644 index 0000000000..2ef748baf6 --- /dev/null +++ b/tests/libqos/qgraph_internal.h @@ -0,0 +1,257 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#ifndef QGRAPH_EXTRA_H +#define QGRAPH_EXTRA_H + +/* This header is declaring additional helper functions defined in + * libqos/qgraph.c + * It should not be included in tests + */ + +#include "libqos/qgraph.h" + +typedef struct QOSGraphMachine QOSGraphMachine; +typedef enum QOSEdgeType QOSEdgeType; +typedef enum QOSNodeType QOSNodeType; + +/* callback called when the walk path algorithm found a + * valid path + */ +typedef void (*QOSTestCallback) (QOSGraphNode *path, int len); + +/* edge types*/ +enum QOSEdgeType { + QEDGE_CONTAINS, + QEDGE_PRODUCES, + QEDGE_CONSUMED_BY +}; + +/* node types*/ +enum QOSNodeType { + QNODE_MACHINE, + QNODE_DRIVER, + QNODE_INTERFACE, + QNODE_TEST +}; + +/* Graph Node */ +struct QOSGraphNode { + QOSNodeType type; + bool available; /* set by QEMU via QMP, used during graph walk */ + bool visited; /* used during graph walk */ + char *name; /* used to identify the node */ + char *command_line; /* used to start QEMU at test execution */ + union { + struct { + QOSCreateDriverFunc constructor; + } driver; + struct { + QOSCreateMachineFunc constructor; + } machine; + struct { + QOSTestFunc function; + void *arg; + QOSBeforeTest before; + bool subprocess; + } test; + } u; + + /** + * only used when traversing the path, never rely on that except in the + * qos_traverse_graph callback function + */ + QOSGraphEdge *path_edge; +}; + +/** + * qos_graph_get_node(): returns the node mapped to that @key. + * It performs an hash map search O(1) + * + * Returns: on success: the %QOSGraphNode + * otherwise: #NULL + */ +QOSGraphNode *qos_graph_get_node(const char *key); + +/** + * qos_graph_has_node(): returns #TRUE if the node + * has map has a node mapped to that @key. + */ +bool qos_graph_has_node(const char *node); + +/** + * qos_graph_get_node_type(): returns the %QOSNodeType + * of the node @node. + * It performs an hash map search O(1) + * Returns: on success: the %QOSNodeType + * otherwise: #-1 + */ +QOSNodeType qos_graph_get_node_type(const char *node); + +/** + * qos_graph_get_node_availability(): returns the availability (boolean) + * of the node @node. + */ +bool qos_graph_get_node_availability(const char *node); + +/** + * qos_graph_get_edge(): returns the edge + * linking of the node @node with @dest. + * + * Returns: on success: the %QOSGraphEdge + * otherwise: #NULL + */ +QOSGraphEdge *qos_graph_get_edge(const char *node, const char *dest); + +/** + * qos_graph_edge_get_type(): returns the edge type + * of the edge @edge. + * + * Returns: on success: the %QOSEdgeType + * otherwise: #-1 + */ +QOSEdgeType qos_graph_edge_get_type(QOSGraphEdge *edge); + +/** + * qos_graph_edge_get_dest(): returns the name of the node + * pointed as destination of edge @edge. + * + * Returns: on success: the destination + * otherwise: #NULL + */ +char *qos_graph_edge_get_dest(QOSGraphEdge *edge); + +/** + * qos_graph_has_edge(): returns #TRUE if there + * exists an edge from @start to @dest. + */ +bool qos_graph_has_edge(const char *start, const char *dest); + +/** + * qos_graph_edge_get_arg(): returns the args assigned + * to that @edge. + * + * Returns: on success: the arg + * otherwise: #NULL + */ +void *qos_graph_edge_get_arg(QOSGraphEdge *edge); + +/** + * qos_graph_edge_get_after_cmd_line(): returns the edge + * command line that will be added after all the node arguments + * and all the before_cmd_line arguments. + * + * Returns: on success: the char* arg + * otherwise: #NULL + */ +char *qos_graph_edge_get_after_cmd_line(QOSGraphEdge *edge); + +/** + * qos_graph_edge_get_before_cmd_line(): returns the edge + * command line that will be added before the node command + * line argument. + * + * Returns: on success: the char* arg + * otherwise: #NULL + */ +char *qos_graph_edge_get_before_cmd_line(QOSGraphEdge *edge); + +/** + * qos_graph_edge_get_extra_device_opts(): returns the arg + * command line that will be added to the node command + * line argument. + * + * Returns: on success: the char* arg + * otherwise: #NULL + */ +char *qos_graph_edge_get_extra_device_opts(QOSGraphEdge *edge); + +/** + * qos_graph_edge_get_name(): returns the name + * assigned to the destination node (different only) + * if there are multiple devices with the same node name + * e.g. a node has two "generic-sdhci", "emmc" and "sdcard" + * there will be two edges with edge_name ="emmc" and "sdcard" + * + * Returns always the char* edge_name + */ +char *qos_graph_edge_get_name(QOSGraphEdge *edge); + +/** + * qos_graph_get_machine(): returns the machine assigned + * to that @node name. + * + * It performs a search only trough the list of machines + * (i.e. the QOS_ROOT child). + * + * Returns: on success: the %QOSGraphNode + * otherwise: #NULL + */ +QOSGraphNode *qos_graph_get_machine(const char *node); + +/** + * qos_graph_has_machine(): returns #TRUE if the node + * has map has a node mapped to that @node. + */ +bool qos_graph_has_machine(const char *node); + + +/** + * qos_print_graph(): walks the graph and prints + * all machine-to-test paths. + */ +void qos_print_graph(void); + +/** + * qos_graph_foreach_test_path(): executes the Depth First search + * algorithm and applies @fn to all discovered paths. + * + * See qos_traverse_graph() in qgraph.c for more info on + * how it works. + */ +void qos_graph_foreach_test_path(QOSTestCallback fn); + +/** + * qos_get_machine_type(): return QEMU machine type for a machine node. + * This function requires every machine @name to be in the form + * /, like "arm/raspi2" or "x86_64/pc". + * + * The function will validate the format and return a pointer to + * @machine to . For example, when passed "x86_64/pc" + * it will return "pc". + * + * Note that this function *does not* allocate any new string. + */ +char *qos_get_machine_type(char *name); + +/** + * qos_delete_cmd_line(): delete the + * command line present in node mapped with key @name. + * + * This function is called when the QMP query returns a node with + * { "abstract" : true } attribute. + */ +void qos_delete_cmd_line(const char *name); + +/** + * qos_graph_node_set_availability(): sets the node identified + * by @node with availability @av. + */ +void qos_graph_node_set_availability(const char *node, bool av); + +#endif diff --git a/tests/libqtest.h b/tests/libqtest.h index 5937f91912..1a2f87ef0b 100644 --- a/tests/libqtest.h +++ b/tests/libqtest.h @@ -598,6 +598,9 @@ static inline QTestState *qtest_start(const char *args) */ static inline void qtest_end(void) { + if (!global_qtest) { + return; + } qtest_quit(global_qtest); global_qtest = NULL; } diff --git a/tests/qos-test.c b/tests/qos-test.c new file mode 100644 index 0000000000..6b1145eccc --- /dev/null +++ b/tests/qos-test.c @@ -0,0 +1,445 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qbool.h" +#include "qapi/qmp/qstring.h" +#include "qapi/qmp/qlist.h" +#include "libqos/malloc.h" +#include "libqos/qgraph.h" +#include "libqos/qgraph_internal.h" + +static char *old_path; + +static void apply_to_node(const char *name, bool is_machine, bool is_abstract) +{ + char *machine_name = NULL; + if (is_machine) { + const char *arch = qtest_get_arch(); + machine_name = g_strconcat(arch, "/", name, NULL); + name = machine_name; + } + qos_graph_node_set_availability(name, true); + if (is_abstract) { + qos_delete_cmd_line(name); + } + g_free(machine_name); +} + +/** + * apply_to_qlist(): using QMP queries QEMU for a list of + * machines and devices available, and sets the respective node + * as true. If a node is found, also all its produced and contained + * child are marked available. + * + * See qos_graph_node_set_availability() for more info + */ +static void apply_to_qlist(QList *list, bool is_machine) +{ + const QListEntry *p; + const char *name; + bool abstract; + QDict *minfo; + QObject *qobj; + QString *qstr; + QBool *qbool; + + for (p = qlist_first(list); p; p = qlist_next(p)) { + minfo = qobject_to(QDict, qlist_entry_obj(p)); + qobj = qdict_get(minfo, "name"); + qstr = qobject_to(QString, qobj); + name = qstring_get_str(qstr); + + qobj = qdict_get(minfo, "abstract"); + if (qobj) { + qbool = qobject_to(QBool, qobj); + abstract = qbool_get_bool(qbool); + } else { + abstract = false; + } + + apply_to_node(name, is_machine, abstract); + qobj = qdict_get(minfo, "alias"); + if (qobj) { + qstr = qobject_to(QString, qobj); + name = qstring_get_str(qstr); + apply_to_node(name, is_machine, abstract); + } + } +} + +/** + * qos_set_machines_devices_available(): sets availability of qgraph + * machines and devices. + * + * This function firstly starts QEMU with "-machine none" option, + * and then executes the QMP protocol asking for the list of devices + * and machines available. + * + * for each of these items, it looks up the corresponding qgraph node, + * setting it as available. The list currently returns all devices that + * are either machines or QEDGE_CONSUMED_BY other nodes. + * Therefore, in order to mark all other nodes, it recursively sets + * all its QEDGE_CONTAINS and QEDGE_PRODUCES child as available too. + */ +static void qos_set_machines_devices_available(void) +{ + QDict *response; + QDict *args = qdict_new(); + QList *list; + + qtest_start("-machine none"); + response = qmp("{ 'execute': 'query-machines' }"); + list = qdict_get_qlist(response, "return"); + + apply_to_qlist(list, true); + + qobject_unref(response); + + qdict_put_bool(args, "abstract", true); + qdict_put_str(args, "implements", "device"); + + response = qmp("{'execute': 'qom-list-types'," + " 'arguments': %p }", args); + g_assert(qdict_haskey(response, "return")); + list = qdict_get_qlist(response, "return"); + + apply_to_qlist(list, false); + + qtest_end(); + qobject_unref(response); +} + +static QGuestAllocator *get_machine_allocator(QOSGraphObject *obj) +{ + return obj->get_driver(obj, "memory"); +} + +static void restart_qemu_or_continue(char *path) +{ + /* compares the current command line with the + * one previously executed: if they are the same, + * don't restart QEMU, if they differ, stop previous + * QEMU subprocess (if active) and start over with + * the new command line + */ + if (g_strcmp0(old_path, path)) { + qtest_end(); + qos_invalidate_command_line(); + old_path = g_strdup(path); + qtest_start(path); + } else { /* if cmd line is the same, reset the guest */ + qobject_unref(qmp("{ 'execute': 'system_reset' }")); + qmp_eventwait("RESET"); + } +} + +void qos_invalidate_command_line(void) +{ + g_free(old_path); + old_path = NULL; +} + +/** + * allocate_objects(): given an array of nodes @arg, + * walks the path invoking all constructors and + * passing the corresponding parameter in order to + * continue the objects allocation. + * Once the test is reached, return the object it consumes. + * + * Since the machine and QEDGE_CONSUMED_BY nodes allocate + * memory in the constructor, g_test_queue_destroy is used so + * that after execution they can be safely free'd. (The test's + * ->before callback is also welcome to use g_test_queue_destroy). + * + * Note: as specified in walk_path() too, @arg is an array of + * char *, where arg[0] is a pointer to the command line + * string that will be used to properly start QEMU when executing + * the test, and the remaining elements represent the actual objects + * that will be allocated. + */ +static void *allocate_objects(QTestState *qts, char **path, QGuestAllocator **p_alloc) +{ + int current = 0; + QGuestAllocator *alloc; + QOSGraphObject *parent = NULL; + QOSGraphEdge *edge; + QOSGraphNode *node; + void *edge_arg; + void *obj; + + node = qos_graph_get_node(path[current]); + g_assert(node->type == QNODE_MACHINE); + + obj = qos_machine_new(node, qts); + qos_object_queue_destroy(obj); + + alloc = get_machine_allocator(obj); + if (p_alloc) { + *p_alloc = alloc; + } + + for (;;) { + if (node->type != QNODE_INTERFACE) { + qos_object_start_hw(obj); + parent = obj; + } + + /* follow edge and get object for next node constructor */ + current++; + edge = qos_graph_get_edge(path[current - 1], path[current]); + node = qos_graph_get_node(path[current]); + + if (node->type == QNODE_TEST) { + g_assert(qos_graph_edge_get_type(edge) == QEDGE_CONSUMED_BY); + return obj; + } + + switch (qos_graph_edge_get_type(edge)) { + case QEDGE_PRODUCES: + obj = parent->get_driver(parent, path[current]); + break; + + case QEDGE_CONSUMED_BY: + edge_arg = qos_graph_edge_get_arg(edge); + obj = qos_driver_new(node, obj, alloc, edge_arg); + qos_object_queue_destroy(obj); + break; + + case QEDGE_CONTAINS: + obj = parent->get_device(parent, path[current]); + break; + } + } +} + +/* The argument to run_one_test, which is the test function that is registered + * with GTest, is a vector of strings. The first item is the initial command + * line (before it is modified by the test's "before" function), the remaining + * items are node names forming the path to the test node. + */ +static char **current_path; + +const char *qos_get_current_command_line(void) +{ + return current_path[0]; +} + +void *qos_allocate_objects(QTestState *qts, QGuestAllocator **p_alloc) +{ + return allocate_objects(qts, current_path + 1, p_alloc); +} + +/** + * run_one_test(): given an array of nodes @arg, + * walks the path invoking all constructors and + * passing the corresponding parameter in order to + * continue the objects allocation. + * Once the test is reached, its function is executed. + * + * Since the machine and QEDGE_CONSUMED_BY nodes allocate + * memory in the constructor, g_test_queue_destroy is used so + * that after execution they can be safely free'd. The test's + * ->before callback is also welcome to use g_test_queue_destroy. + * + * Note: as specified in walk_path() too, @arg is an array of + * char *, where arg[0] is a pointer to the command line + * string that will be used to properly start QEMU when executing + * the test, and the remaining elements represent the actual objects + * that will be allocated. + * + * The order of execution is the following: + * 1) @before test function as defined in the given QOSGraphTestOptions + * 2) start QEMU + * 3) call all nodes constructor and get_driver/get_device depending on edge, + * start the hardware (*_device_enable functions) + * 4) start test + */ +static void run_one_test(const void *arg) +{ + QOSGraphNode *test_node; + QGuestAllocator *alloc = NULL; + void *obj; + char **path = (char **) arg; + GString *cmd_line = g_string_new(path[0]); + void *test_arg; + + /* Before test */ + current_path = path; + test_node = qos_graph_get_node(path[(g_strv_length(path) - 1)]); + test_arg = test_node->u.test.arg; + if (test_node->u.test.before) { + test_arg = test_node->u.test.before(cmd_line, test_arg); + } + + restart_qemu_or_continue(cmd_line->str); + g_string_free(cmd_line, true); + + obj = qos_allocate_objects(global_qtest, &alloc); + test_node->u.test.function(obj, test_arg, alloc); +} + +static void subprocess_run_one_test(const void *arg) +{ + const gchar *path = arg; + g_test_trap_subprocess(path, 0, 0); + g_test_trap_assert_passed(); +} + +/* + * in this function, 2 path will be built: + * path_str, a one-string path (ex "pc/i440FX-pcihost/...") + * path_vec, a string-array path (ex [0] = "pc", [1] = "i440FX-pcihost"). + * + * path_str will be only used to build the test name, and won't need the + * architecture name at beginning, since it will be added by qtest_add_func(). + * + * path_vec is used to allocate all constructors of the path nodes. + * Each name in this array except position 0 must correspond to a valid + * QOSGraphNode name. + * Position 0 is special, initially contains just the name of + * the node, (ex for "x86_64/pc" it will be "pc"), used to build the test + * path (see below). After it will contain the command line used to start + * qemu with all required devices. + * + * Note that the machine node name must be with format / + * (ex "x86_64/pc"), because it will identify the node "x86_64/pc" + * and start QEMU with "-M pc". For this reason, + * when building path_str, path_vec + * initially contains the at position 0 ("pc"), + * and the node name at position 1 (/) + * ("x86_64/pc"), followed by the rest of the nodes. + */ +static void walk_path(QOSGraphNode *orig_path, int len) +{ + QOSGraphNode *path; + QOSGraphEdge *edge; + + /* etype set to QEDGE_CONSUMED_BY so that machine can add to the command line */ + QOSEdgeType etype = QEDGE_CONSUMED_BY; + + /* twice QOS_PATH_MAX_ELEMENT_SIZE since each edge can have its arg */ + char **path_vec = g_new0(char *, (QOS_PATH_MAX_ELEMENT_SIZE * 2)); + int path_vec_size = 0; + + char *after_cmd = NULL, *before_cmd = NULL, *after_device = NULL; + char *node_name = orig_path->name, *path_str; + + GString *cmd_line = g_string_new(""); + GString *cmd_line2 = g_string_new(""); + + path = qos_graph_get_node(node_name); /* root */ + node_name = qos_graph_edge_get_dest(path->path_edge); /* machine name */ + + path_vec[path_vec_size++] = node_name; + path_vec[path_vec_size++] = qos_get_machine_type(node_name); + + for (;;) { + path = qos_graph_get_node(node_name); + if (!path->path_edge) { + break; + } + + node_name = qos_graph_edge_get_dest(path->path_edge); + + /* append node command line + previous edge command line */ + if (path->command_line && etype == QEDGE_CONSUMED_BY) { + g_string_append(cmd_line, path->command_line); + if (after_device) { + g_string_append(cmd_line, after_device); + } + } + + path_vec[path_vec_size++] = qos_graph_edge_get_name(path->path_edge); + /* detect if edge has command line args */ + after_cmd = qos_graph_edge_get_after_cmd_line(path->path_edge); + after_device = qos_graph_edge_get_extra_device_opts(path->path_edge); + before_cmd = qos_graph_edge_get_before_cmd_line(path->path_edge); + edge = qos_graph_get_edge(path->name, node_name); + etype = qos_graph_edge_get_type(edge); + + if (before_cmd) { + g_string_append(cmd_line, before_cmd); + } + if (after_cmd) { + g_string_append(cmd_line2, after_cmd); + } + } + + path_vec[path_vec_size++] = NULL; + if (after_device) { + g_string_append(cmd_line, after_device); + } + g_string_append(cmd_line, cmd_line2->str); + g_string_free(cmd_line2, true); + + /* here position 0 has /, position 1 has . + * The path must not have the , qtest_add_data_func adds it. + */ + path_str = g_strjoinv("/", path_vec + 1); + + /* put arch/machine in position 1 so run_one_test can do its work + * and add the command line at position 0. + */ + path_vec[1] = path_vec[0]; + path_vec[0] = g_string_free(cmd_line, false); + + if (path->u.test.subprocess) { + gchar *subprocess_path = g_strdup_printf("/%s/%s/subprocess", + qtest_get_arch(), path_str); + qtest_add_data_func(path_str, subprocess_path, subprocess_run_one_test); + g_test_add_data_func(subprocess_path, path_vec, run_one_test); + } else { + qtest_add_data_func(path_str, path_vec, run_one_test); + } + + g_free(path_str); +} + + + +/** + * main(): heart of the qgraph framework. + * + * - Initializes the glib test framework + * - Creates the graph by invoking the various _init constructors + * - Starts QEMU to mark the available devices + * - Walks the graph, and each path is added to + * the glib test framework (walk_path) + * - Runs the tests, calling allocate_object() and allocating the + * machine/drivers/test objects + * - Cleans up everything + */ +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + qos_graph_init(); + module_call_init(MODULE_INIT_QOM); + module_call_init(MODULE_INIT_LIBQOS); + qos_set_machines_devices_available(); + + qos_graph_foreach_test_path(walk_path); + g_test_run(); + qtest_end(); + qos_graph_destroy(); + g_free(old_path); + return 0; +} diff --git a/tests/test-qgraph.c b/tests/test-qgraph.c new file mode 100644 index 0000000000..f6a6565e31 --- /dev/null +++ b/tests/test-qgraph.c @@ -0,0 +1,434 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/qgraph_internal.h" + +#define MACHINE_PC "x86_64/pc" +#define MACHINE_RASPI2 "arm/raspi2" +#define I440FX "i440FX-pcihost" +#define PCIBUS_PC "pcibus-pc" +#define SDHCI "sdhci" +#define PCIBUS "pci-bus" +#define SDHCI_PCI "sdhci-pci" +#define SDHCI_MM "generic-sdhci" +#define REGISTER_TEST "register-test" + +int npath; + +static void *machinefunct(QTestState *qts) +{ + return NULL; +} + +static void *driverfunct(void *obj, QGuestAllocator *machine, void *arg) +{ + return NULL; +} + +static void testfunct(void *obj, void *arg, QGuestAllocator *alloc) +{ + return; +} + +static void check_interface(const char *interface) +{ + g_assert_cmpint(qos_graph_has_machine(interface), ==, FALSE); + g_assert_nonnull(qos_graph_get_node(interface)); + g_assert_cmpint(qos_graph_has_node(interface), ==, TRUE); + g_assert_cmpint(qos_graph_get_node_type(interface), ==, QNODE_INTERFACE); + qos_graph_node_set_availability(interface, TRUE); + g_assert_cmpint(qos_graph_get_node_availability(interface), ==, TRUE); +} + +static void check_machine(const char *machine) +{ + qos_node_create_machine(machine, machinefunct); + g_assert_nonnull(qos_graph_get_machine(machine)); + g_assert_cmpint(qos_graph_has_machine(machine), ==, TRUE); + g_assert_nonnull(qos_graph_get_node(machine)); + g_assert_cmpint(qos_graph_get_node_availability(machine), ==, FALSE); + qos_graph_node_set_availability(machine, TRUE); + g_assert_cmpint(qos_graph_get_node_availability(machine), ==, TRUE); + g_assert_cmpint(qos_graph_has_node(machine), ==, TRUE); + g_assert_cmpint(qos_graph_get_node_type(machine), ==, QNODE_MACHINE); +} + +static void check_contains(const char *machine, const char *driver) +{ + QOSGraphEdge *edge; + qos_node_contains(machine, driver, NULL); + + edge = qos_graph_get_edge(machine, driver); + g_assert_nonnull(edge); + g_assert_cmpint(qos_graph_edge_get_type(edge), ==, QEDGE_CONTAINS); + g_assert_cmpint(qos_graph_has_edge(machine, driver), ==, TRUE); +} + +static void check_produces(const char *machine, const char *interface) +{ + QOSGraphEdge *edge; + + qos_node_produces(machine, interface); + check_interface(interface); + edge = qos_graph_get_edge(machine, interface); + g_assert_nonnull(edge); + g_assert_cmpint(qos_graph_edge_get_type(edge), ==, + QEDGE_PRODUCES); + g_assert_cmpint(qos_graph_has_edge(machine, interface), ==, TRUE); +} + +static void check_consumes(const char *driver, const char *interface) +{ + QOSGraphEdge *edge; + + qos_node_consumes(driver, interface, NULL); + check_interface(interface); + edge = qos_graph_get_edge(interface, driver); + g_assert_nonnull(edge); + g_assert_cmpint(qos_graph_edge_get_type(edge), ==, QEDGE_CONSUMED_BY); + g_assert_cmpint(qos_graph_has_edge(interface, driver), ==, TRUE); +} + +static void check_driver(const char *driver) +{ + qos_node_create_driver(driver, driverfunct); + g_assert_cmpint(qos_graph_has_machine(driver), ==, FALSE); + g_assert_nonnull(qos_graph_get_node(driver)); + g_assert_cmpint(qos_graph_has_node(driver), ==, TRUE); + g_assert_cmpint(qos_graph_get_node_type(driver), ==, QNODE_DRIVER); + g_assert_cmpint(qos_graph_get_node_availability(driver), ==, FALSE); + qos_graph_node_set_availability(driver, TRUE); + g_assert_cmpint(qos_graph_get_node_availability(driver), ==, TRUE); +} + +static void check_test(const char *test, const char *interface) +{ + QOSGraphEdge *edge; + const char *full_name = g_strdup_printf("%s-tests/%s", interface, test); + + qos_add_test(test, interface, testfunct, NULL); + g_assert_cmpint(qos_graph_has_machine(test), ==, FALSE); + g_assert_cmpint(qos_graph_has_machine(full_name), ==, FALSE); + g_assert_nonnull(qos_graph_get_node(full_name)); + g_assert_cmpint(qos_graph_has_node(full_name), ==, TRUE); + g_assert_cmpint(qos_graph_get_node_type(full_name), ==, QNODE_TEST); + edge = qos_graph_get_edge(interface, full_name); + g_assert_nonnull(edge); + g_assert_cmpint(qos_graph_edge_get_type(edge), ==, + QEDGE_CONSUMED_BY); + g_assert_cmpint(qos_graph_has_edge(interface, full_name), ==, TRUE); + g_assert_cmpint(qos_graph_get_node_availability(full_name), ==, TRUE); + qos_graph_node_set_availability(full_name, FALSE); + g_assert_cmpint(qos_graph_get_node_availability(full_name), ==, FALSE); +} + +static void count_each_test(QOSGraphNode *path, int len) +{ + npath++; +} + +static void check_leaf_discovered(int n) +{ + npath = 0; + qos_graph_foreach_test_path(count_each_test); + g_assert_cmpint(n, ==, npath); +} + +/* G_Test functions */ + +static void init_nop(void) +{ + qos_graph_init(); + qos_graph_destroy(); +} + +static void test_machine(void) +{ + qos_graph_init(); + check_machine(MACHINE_PC); + qos_graph_destroy(); +} + +static void test_contains(void) +{ + qos_graph_init(); + check_contains(MACHINE_PC, I440FX); + g_assert_null(qos_graph_get_machine(MACHINE_PC)); + g_assert_null(qos_graph_get_machine(I440FX)); + g_assert_null(qos_graph_get_node(MACHINE_PC)); + g_assert_null(qos_graph_get_node(I440FX)); + qos_graph_destroy(); +} + +static void test_multiple_contains(void) +{ + qos_graph_init(); + check_contains(MACHINE_PC, I440FX); + check_contains(MACHINE_PC, PCIBUS_PC); + qos_graph_destroy(); +} + +static void test_produces(void) +{ + qos_graph_init(); + check_produces(MACHINE_PC, I440FX); + g_assert_null(qos_graph_get_machine(MACHINE_PC)); + g_assert_null(qos_graph_get_machine(I440FX)); + g_assert_null(qos_graph_get_node(MACHINE_PC)); + g_assert_nonnull(qos_graph_get_node(I440FX)); + qos_graph_destroy(); +} + +static void test_multiple_produces(void) +{ + qos_graph_init(); + check_produces(MACHINE_PC, I440FX); + check_produces(MACHINE_PC, PCIBUS_PC); + qos_graph_destroy(); +} + +static void test_consumes(void) +{ + qos_graph_init(); + check_consumes(I440FX, SDHCI); + g_assert_null(qos_graph_get_machine(I440FX)); + g_assert_null(qos_graph_get_machine(SDHCI)); + g_assert_null(qos_graph_get_node(I440FX)); + g_assert_nonnull(qos_graph_get_node(SDHCI)); + qos_graph_destroy(); +} + +static void test_multiple_consumes(void) +{ + qos_graph_init(); + check_consumes(I440FX, SDHCI); + check_consumes(PCIBUS_PC, SDHCI); + qos_graph_destroy(); +} + +static void test_driver(void) +{ + qos_graph_init(); + check_driver(I440FX); + qos_graph_destroy(); +} + +static void test_test(void) +{ + qos_graph_init(); + check_test(REGISTER_TEST, SDHCI); + qos_graph_destroy(); +} + +static void test_machine_contains_driver(void) +{ + qos_graph_init(); + check_machine(MACHINE_PC); + check_driver(I440FX); + check_contains(MACHINE_PC, I440FX); + qos_graph_destroy(); +} + +static void test_driver_contains_driver(void) +{ + qos_graph_init(); + check_driver(PCIBUS_PC); + check_driver(I440FX); + check_contains(PCIBUS_PC, I440FX); + qos_graph_destroy(); +} + +static void test_machine_produces_interface(void) +{ + qos_graph_init(); + check_machine(MACHINE_PC); + check_produces(MACHINE_PC, SDHCI); + qos_graph_destroy(); +} + +static void test_driver_produces_interface(void) +{ + qos_graph_init(); + check_driver(I440FX); + check_produces(I440FX, SDHCI); + qos_graph_destroy(); +} + +static void test_machine_consumes_interface(void) +{ + qos_graph_init(); + check_machine(MACHINE_PC); + check_consumes(MACHINE_PC, SDHCI); + qos_graph_destroy(); +} + +static void test_driver_consumes_interface(void) +{ + qos_graph_init(); + check_driver(I440FX); + check_consumes(I440FX, SDHCI); + qos_graph_destroy(); +} + +static void test_test_consumes_interface(void) +{ + qos_graph_init(); + check_test(REGISTER_TEST, SDHCI); + qos_graph_destroy(); +} + +static void test_full_sample(void) +{ + qos_graph_init(); + check_machine(MACHINE_PC); + check_contains(MACHINE_PC, I440FX); + check_driver(I440FX); + check_driver(PCIBUS_PC); + check_contains(I440FX, PCIBUS_PC); + check_produces(PCIBUS_PC, PCIBUS); + check_driver(SDHCI_PCI); + qos_node_consumes(SDHCI_PCI, PCIBUS, NULL); + check_produces(SDHCI_PCI, SDHCI); + check_driver(SDHCI_MM); + check_produces(SDHCI_MM, SDHCI); + qos_add_test(REGISTER_TEST, SDHCI, testfunct, NULL); + check_leaf_discovered(1); + qos_print_graph(); + qos_graph_destroy(); +} + +static void test_full_sample_raspi(void) +{ + qos_graph_init(); + check_machine(MACHINE_PC); + check_contains(MACHINE_PC, I440FX); + check_driver(I440FX); + check_driver(PCIBUS_PC); + check_contains(I440FX, PCIBUS_PC); + check_produces(PCIBUS_PC, PCIBUS); + check_driver(SDHCI_PCI); + qos_node_consumes(SDHCI_PCI, PCIBUS, NULL); + check_produces(SDHCI_PCI, SDHCI); + check_machine(MACHINE_RASPI2); + check_contains(MACHINE_RASPI2, SDHCI_MM); + check_driver(SDHCI_MM); + check_produces(SDHCI_MM, SDHCI); + qos_add_test(REGISTER_TEST, SDHCI, testfunct, NULL); + qos_print_graph(); + check_leaf_discovered(2); + qos_graph_destroy(); +} + +static void test_cycle(void) +{ + qos_graph_init(); + check_machine(MACHINE_RASPI2); + check_driver("B"); + check_driver("C"); + check_driver("D"); + check_contains(MACHINE_RASPI2, "B"); + check_contains("B", "C"); + check_contains("C", "D"); + check_contains("D", MACHINE_RASPI2); + check_leaf_discovered(0); + qos_print_graph(); + qos_graph_destroy(); +} + +static void test_two_test_same_interface(void) +{ + qos_graph_init(); + check_machine(MACHINE_RASPI2); + check_produces(MACHINE_RASPI2, "B"); + qos_add_test("C", "B", testfunct, NULL); + qos_add_test("D", "B", testfunct, NULL); + check_contains(MACHINE_RASPI2, "B"); + check_leaf_discovered(4); + qos_print_graph(); + qos_graph_destroy(); +} + +static void test_test_in_path(void) +{ + qos_graph_init(); + check_machine(MACHINE_RASPI2); + check_produces(MACHINE_RASPI2, "B"); + qos_add_test("C", "B", testfunct, NULL); + check_driver("D"); + check_consumes("D", "B"); + check_produces("D", "E"); + qos_add_test("F", "E", testfunct, NULL); + check_leaf_discovered(2); + qos_print_graph(); + qos_graph_destroy(); +} + +static void test_double_edge(void) +{ + qos_graph_init(); + check_machine(MACHINE_RASPI2); + check_produces("B", "C"); + qos_node_consumes("C", "B", NULL); + qos_add_test("D", "C", testfunct, NULL); + check_contains(MACHINE_RASPI2, "B"); + qos_print_graph(); + qos_graph_destroy(); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/qgraph/init_nop", init_nop); + g_test_add_func("/qgraph/test_machine", test_machine); + g_test_add_func("/qgraph/test_contains", test_contains); + g_test_add_func("/qgraph/test_multiple_contains", test_multiple_contains); + g_test_add_func("/qgraph/test_produces", test_produces); + g_test_add_func("/qgraph/test_multiple_produces", test_multiple_produces); + g_test_add_func("/qgraph/test_consumes", test_consumes); + g_test_add_func("/qgraph/test_multiple_consumes", + test_multiple_consumes); + g_test_add_func("/qgraph/test_driver", test_driver); + g_test_add_func("/qgraph/test_test", test_test); + g_test_add_func("/qgraph/test_machine_contains_driver", + test_machine_contains_driver); + g_test_add_func("/qgraph/test_driver_contains_driver", + test_driver_contains_driver); + g_test_add_func("/qgraph/test_machine_produces_interface", + test_machine_produces_interface); + g_test_add_func("/qgraph/test_driver_produces_interface", + test_driver_produces_interface); + g_test_add_func("/qgraph/test_machine_consumes_interface", + test_machine_consumes_interface); + g_test_add_func("/qgraph/test_driver_consumes_interface", + test_driver_consumes_interface); + g_test_add_func("/qgraph/test_test_consumes_interface", + test_test_consumes_interface); + g_test_add_func("/qgraph/test_full_sample", test_full_sample); + g_test_add_func("/qgraph/test_full_sample_raspi", test_full_sample_raspi); + g_test_add_func("/qgraph/test_cycle", test_cycle); + g_test_add_func("/qgraph/test_two_test_same_interface", + test_two_test_same_interface); + g_test_add_func("/qgraph/test_test_in_path", test_test_in_path); + g_test_add_func("/qgraph/test_double_edge", test_double_edge); + + g_test_run(); + return 0; +} From 85af0057e7be4375e9563be5b6d064f963ec2c39 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Tue, 3 Jul 2018 16:53:10 +0200 Subject: [PATCH 06/57] tests/libqos: pci-pc driver and interface nodes Add pci-bus-pc node, move QPCIBusPC struct declaration in its header (since it will be needed by other drivers) and introduce a setter method for drivers that do not need to allocate but have to initialize QPCIBusPC. Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 6 ++- tests/libqos/pci-pc.c | 83 +++++++++++++++++++++++++++--------------- tests/libqos/pci-pc.h | 19 +++++++++- tests/libqos/pci.c | 32 +++++++++++++++- tests/libqos/pci.h | 12 ++++++ 5 files changed, 118 insertions(+), 34 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index 94ace7b675..5a7ea15e76 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -749,11 +749,15 @@ libqos-imx-obj-y = $(libqos-obj-y) tests/libqos/i2c-imx.o libqos-usb-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/usb.o libqos-virtio-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o +# Devices +qos-test-obj-y = tests/qos-test.o $(libqgraph-obj-y) +qos-test-obj-y += $(libqos-pc-obj-y) + check-unit-y += tests/test-qgraph$(EXESUF) tests/test-qgraph$(EXESUF): tests/test-qgraph.o $(libqgraph-obj-y) check-qtest-generic-y += tests/qos-test$(EXESUF) -tests/qos-test$(EXESUF): tests/qos-test.o $(libqgraph-obj-y) +tests/qos-test$(EXESUF): $(qos-test-obj-y) tests/qmp-test$(EXESUF): tests/qmp-test.o tests/qmp-cmd-test$(EXESUF): tests/qmp-cmd-test.o diff --git a/tests/libqos/pci-pc.c b/tests/libqos/pci-pc.c index 43d7082516..d729ad4057 100644 --- a/tests/libqos/pci-pc.c +++ b/tests/libqos/pci-pc.c @@ -18,15 +18,9 @@ #include "qemu-common.h" - #define ACPI_PCIHP_ADDR 0xae00 #define PCI_EJ_BASE 0x0008 -typedef struct QPCIBusPC -{ - QPCIBus bus; -} QPCIBusPC; - static uint8_t qpci_pc_pio_readb(QPCIBus *bus, uint32_t addr) { return qtest_inb(bus->qts, addr); @@ -116,44 +110,65 @@ static void qpci_pc_config_writel(QPCIBus *bus, int devfn, uint8_t offset, uint3 qtest_outl(bus->qts, 0xcfc, value); } -QPCIBus *qpci_new_pc(QTestState *qts, QGuestAllocator *alloc) +static void *qpci_pc_get_driver(void *obj, const char *interface) { - QPCIBusPC *ret = g_new0(QPCIBusPC, 1); + QPCIBusPC *qpci = obj; + if (!g_strcmp0(interface, "pci-bus")) { + return &qpci->bus; + } + fprintf(stderr, "%s not present in pci-bus-pc\n", interface); + g_assert_not_reached(); +} +void qpci_init_pc(QPCIBusPC *qpci, QTestState *qts, QGuestAllocator *alloc) +{ assert(qts); - ret->bus.pio_readb = qpci_pc_pio_readb; - ret->bus.pio_readw = qpci_pc_pio_readw; - ret->bus.pio_readl = qpci_pc_pio_readl; - ret->bus.pio_readq = qpci_pc_pio_readq; + qpci->bus.pio_readb = qpci_pc_pio_readb; + qpci->bus.pio_readw = qpci_pc_pio_readw; + qpci->bus.pio_readl = qpci_pc_pio_readl; + qpci->bus.pio_readq = qpci_pc_pio_readq; - ret->bus.pio_writeb = qpci_pc_pio_writeb; - ret->bus.pio_writew = qpci_pc_pio_writew; - ret->bus.pio_writel = qpci_pc_pio_writel; - ret->bus.pio_writeq = qpci_pc_pio_writeq; + qpci->bus.pio_writeb = qpci_pc_pio_writeb; + qpci->bus.pio_writew = qpci_pc_pio_writew; + qpci->bus.pio_writel = qpci_pc_pio_writel; + qpci->bus.pio_writeq = qpci_pc_pio_writeq; - ret->bus.memread = qpci_pc_memread; - ret->bus.memwrite = qpci_pc_memwrite; + qpci->bus.memread = qpci_pc_memread; + qpci->bus.memwrite = qpci_pc_memwrite; - ret->bus.config_readb = qpci_pc_config_readb; - ret->bus.config_readw = qpci_pc_config_readw; - ret->bus.config_readl = qpci_pc_config_readl; + qpci->bus.config_readb = qpci_pc_config_readb; + qpci->bus.config_readw = qpci_pc_config_readw; + qpci->bus.config_readl = qpci_pc_config_readl; - ret->bus.config_writeb = qpci_pc_config_writeb; - ret->bus.config_writew = qpci_pc_config_writew; - ret->bus.config_writel = qpci_pc_config_writel; + qpci->bus.config_writeb = qpci_pc_config_writeb; + qpci->bus.config_writew = qpci_pc_config_writew; + qpci->bus.config_writel = qpci_pc_config_writel; - ret->bus.qts = qts; - ret->bus.pio_alloc_ptr = 0xc000; - ret->bus.mmio_alloc_ptr = 0xE0000000; - ret->bus.mmio_limit = 0x100000000ULL; + qpci->bus.qts = qts; + qpci->bus.pio_alloc_ptr = 0xc000; + qpci->bus.mmio_alloc_ptr = 0xE0000000; + qpci->bus.mmio_limit = 0x100000000ULL; - return &ret->bus; + qpci->obj.get_driver = qpci_pc_get_driver; +} + +QPCIBus *qpci_new_pc(QTestState *qts, QGuestAllocator *alloc) +{ + QPCIBusPC *qpci = g_new0(QPCIBusPC, 1); + qpci_init_pc(qpci, qts, alloc); + + return &qpci->bus; } void qpci_free_pc(QPCIBus *bus) { - QPCIBusPC *s = container_of(bus, QPCIBusPC, bus); + QPCIBusPC *s; + + if (!bus) { + return; + } + s = container_of(bus, QPCIBusPC, bus); g_free(s); } @@ -172,3 +187,11 @@ void qpci_unplug_acpi_device_test(const char *id, uint8_t slot) qmp_eventwait("DEVICE_DELETED"); } + +static void qpci_pc_register_nodes(void) +{ + qos_node_create_driver("pci-bus-pc", NULL); + qos_node_produces("pci-bus-pc", "pci-bus"); +} + +libqos_init(qpci_pc_register_nodes); diff --git a/tests/libqos/pci-pc.h b/tests/libqos/pci-pc.h index 5be68ba7de..4690005232 100644 --- a/tests/libqos/pci-pc.h +++ b/tests/libqos/pci-pc.h @@ -15,6 +15,22 @@ #include "libqos/pci.h" #include "libqos/malloc.h" +#include "libqos/qgraph.h" + +typedef struct QPCIBusPC { + QOSGraphObject obj; + QPCIBus bus; +} QPCIBusPC; + +/* qpci_init_pc(): + * @ret: A valid QPCIBusPC * pointer + * @qts: The %QTestState for this PC machine + * @alloc: A previously initialized @alloc providing memory for @qts + * + * This function initializes an already allocated + * QPCIBusPC object. + */ +void qpci_init_pc(QPCIBusPC *ret, QTestState *qts, QGuestAllocator *alloc); /* qpci_pc_new(): * @qts: The %QTestState for this PC machine @@ -23,7 +39,8 @@ * This function creates a new QPCIBusPC object, * and properly initialize its fields. * - * Returns a newly allocated QPCIBus. + * Returns the QPCIBus *bus field of a newly + * allocated QPCIBusPC. */ QPCIBus *qpci_new_pc(QTestState *qts, QGuestAllocator *alloc); diff --git a/tests/libqos/pci.c b/tests/libqos/pci.c index e8c342c257..8257904ba7 100644 --- a/tests/libqos/pci.c +++ b/tests/libqos/pci.c @@ -15,6 +15,7 @@ #include "hw/pci/pci_regs.h" #include "qemu/host-utils.h" +#include "libqos/qgraph.h" void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id, void (*func)(QPCIDevice *dev, int devfn, void *data), @@ -50,13 +51,20 @@ void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id, } } +static void qpci_device_set(QPCIDevice *dev, QPCIBus *bus, int devfn) +{ + g_assert(dev); + + dev->bus = bus; + dev->devfn = devfn; +} + QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn) { QPCIDevice *dev; dev = g_malloc0(sizeof(*dev)); - dev->bus = bus; - dev->devfn = devfn; + qpci_device_set(dev, bus, devfn); if (qpci_config_readw(dev, PCI_VENDOR_ID) == 0xFFFF) { g_free(dev); @@ -66,6 +74,17 @@ QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn) return dev; } +void qpci_device_init(QPCIDevice *dev, QPCIBus *bus, QPCIAddress *addr) +{ + uint16_t vendor_id, device_id; + + qpci_device_set(dev, bus, addr->devfn); + vendor_id = qpci_config_readw(dev, PCI_VENDOR_ID); + device_id = qpci_config_readw(dev, PCI_DEVICE_ID); + g_assert(!addr->vendor_id || vendor_id == addr->vendor_id); + g_assert(!addr->device_id || device_id == addr->device_id); +} + void qpci_device_enable(QPCIDevice *dev) { uint16_t cmd; @@ -395,3 +414,12 @@ QPCIBar qpci_legacy_iomap(QPCIDevice *dev, uint16_t addr) QPCIBar bar = { .addr = addr }; return bar; } + +void add_qpci_address(QOSGraphEdgeOptions *opts, QPCIAddress *addr) +{ + g_assert(addr); + g_assert(opts); + + opts->arg = addr; + opts->size_arg = sizeof(QPCIAddress); +} diff --git a/tests/libqos/pci.h b/tests/libqos/pci.h index 0b7e936174..9fd521aed5 100644 --- a/tests/libqos/pci.h +++ b/tests/libqos/pci.h @@ -14,6 +14,7 @@ #define LIBQOS_PCI_H #include "libqtest.h" +#include "libqos/qgraph.h" #define QPCI_PIO_LIMIT 0x10000 @@ -22,6 +23,7 @@ typedef struct QPCIDevice QPCIDevice; typedef struct QPCIBus QPCIBus; typedef struct QPCIBar QPCIBar; +typedef struct QPCIAddress QPCIAddress; struct QPCIBus { uint8_t (*pio_readb)(QPCIBus *bus, uint32_t addr); @@ -51,6 +53,7 @@ struct QPCIBus { QTestState *qts; uint16_t pio_alloc_ptr; uint64_t mmio_alloc_ptr, mmio_limit; + }; struct QPCIBar { @@ -66,10 +69,17 @@ struct QPCIDevice uint64_t msix_table_off, msix_pba_off; }; +struct QPCIAddress { + uint32_t devfn; + uint16_t vendor_id; + uint16_t device_id; +}; + void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id, void (*func)(QPCIDevice *dev, int devfn, void *data), void *data); QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn); +void qpci_device_init(QPCIDevice *dev, QPCIBus *bus, QPCIAddress *addr); void qpci_device_enable(QPCIDevice *dev); uint8_t qpci_find_capability(QPCIDevice *dev, uint8_t id); @@ -110,4 +120,6 @@ void qpci_iounmap(QPCIDevice *dev, QPCIBar addr); QPCIBar qpci_legacy_iomap(QPCIDevice *dev, uint16_t addr); void qpci_unplug_acpi_device_test(const char *id, uint8_t slot); + +void add_qpci_address(QOSGraphEdgeOptions *opts, QPCIAddress *addr); #endif From 46113f88e6a64cbb0f11e4062627485c3103694b Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Tue, 3 Jul 2018 16:53:35 +0200 Subject: [PATCH 07/57] tests/libqos: x86_64/pc machine node Add pc machine for the x86_64 QEMU binary. This machine contains an i440FX-pcihost driver, that contains itself a pci-bus-pc that produces the pci-bus interface. Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 3 + tests/libqos/x86_64_pc-machine.c | 114 +++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 tests/libqos/x86_64_pc-machine.c diff --git a/tests/Makefile.include b/tests/Makefile.include index 5a7ea15e76..8a760bde47 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -753,6 +753,9 @@ libqos-virtio-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/virt qos-test-obj-y = tests/qos-test.o $(libqgraph-obj-y) qos-test-obj-y += $(libqos-pc-obj-y) +# Machines +qos-test-obj-y += tests/libqos/x86_64_pc-machine.o + check-unit-y += tests/test-qgraph$(EXESUF) tests/test-qgraph$(EXESUF): tests/test-qgraph.o $(libqgraph-obj-y) diff --git a/tests/libqos/x86_64_pc-machine.c b/tests/libqos/x86_64_pc-machine.c new file mode 100644 index 0000000000..8bd0360ba9 --- /dev/null +++ b/tests/libqos/x86_64_pc-machine.c @@ -0,0 +1,114 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "pci-pc.h" +#include "malloc-pc.h" + +typedef struct QX86PCMachine QX86PCMachine; +typedef struct i440FX_pcihost i440FX_pcihost; +typedef struct QSDHCI_PCI QSDHCI_PCI; + +struct i440FX_pcihost { + QOSGraphObject obj; + QPCIBusPC pci; +}; + +struct QX86PCMachine { + QOSGraphObject obj; + QGuestAllocator alloc; + i440FX_pcihost bridge; +}; + +/* i440FX_pcihost */ + +static QOSGraphObject *i440FX_host_get_device(void *obj, const char *device) +{ + i440FX_pcihost *host = obj; + if (!g_strcmp0(device, "pci-bus-pc")) { + return &host->pci.obj; + } + fprintf(stderr, "%s not present in i440FX-pcihost\n", device); + g_assert_not_reached(); +} + +static void qos_create_i440FX_host(i440FX_pcihost *host, + QTestState *qts, + QGuestAllocator *alloc) +{ + host->obj.get_device = i440FX_host_get_device; + qpci_init_pc(&host->pci, qts, alloc); +} + +/* x86_64/pc machine */ + +static void pc_destructor(QOSGraphObject *obj) +{ + QX86PCMachine *machine = (QX86PCMachine *) obj; + alloc_destroy(&machine->alloc); +} + +static void *pc_get_driver(void *object, const char *interface) +{ + QX86PCMachine *machine = object; + if (!g_strcmp0(interface, "memory")) { + return &machine->alloc; + } + + fprintf(stderr, "%s not present in x86_64/pc\n", interface); + g_assert_not_reached(); +} + +static QOSGraphObject *pc_get_device(void *obj, const char *device) +{ + QX86PCMachine *machine = obj; + if (!g_strcmp0(device, "i440FX-pcihost")) { + return &machine->bridge.obj; + } + + fprintf(stderr, "%s not present in x86_64/pc\n", device); + g_assert_not_reached(); +} + +static void *qos_create_machine_pc(QTestState *qts) +{ + QX86PCMachine *machine = g_new0(QX86PCMachine, 1); + machine->obj.get_device = pc_get_device; + machine->obj.get_driver = pc_get_driver; + machine->obj.destructor = pc_destructor; + pc_alloc_init(&machine->alloc, qts, ALLOC_NO_FLAGS); + qos_create_i440FX_host(&machine->bridge, qts, &machine->alloc); + + return &machine->obj; +} + +static void pc_machine_register_nodes(void) +{ + qos_node_create_machine("i386/pc", qos_create_machine_pc); + qos_node_contains("i386/pc", "i440FX-pcihost", NULL); + + qos_node_create_machine("x86_64/pc", qos_create_machine_pc); + qos_node_contains("x86_64/pc", "i440FX-pcihost", NULL); + + qos_node_create_driver("i440FX-pcihost", NULL); + qos_node_contains("i440FX-pcihost", "pci-bus-pc", NULL); +} + +libqos_init(pc_machine_register_nodes); From a0a9ba66f6784fe93ee0feb8a50250d7fa8440be Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Tue, 3 Jul 2018 16:51:58 +0200 Subject: [PATCH 08/57] tests/libqos: sdhci driver and interface nodes Add qgraph nodes for sdhci-pci and generic-sdhci (memory mapped) drivers. Both drivers implement (produce) the same interface sdhci, that provides the readw - readq - writeq functions. Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 1 + tests/libqos/sdhci.c | 163 +++++++++++++++++++++++++++++++++++++++++ tests/libqos/sdhci.h | 70 ++++++++++++++++++ 3 files changed, 234 insertions(+) create mode 100644 tests/libqos/sdhci.c create mode 100644 tests/libqos/sdhci.h diff --git a/tests/Makefile.include b/tests/Makefile.include index 8a760bde47..b1b03e64cf 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -752,6 +752,7 @@ libqos-virtio-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/virt # Devices qos-test-obj-y = tests/qos-test.o $(libqgraph-obj-y) qos-test-obj-y += $(libqos-pc-obj-y) +qos-test-obj-y += tests/libqos/sdhci.o # Machines qos-test-obj-y += tests/libqos/x86_64_pc-machine.o diff --git a/tests/libqos/sdhci.c b/tests/libqos/sdhci.c new file mode 100644 index 0000000000..22c33de453 --- /dev/null +++ b/tests/libqos/sdhci.c @@ -0,0 +1,163 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "pci.h" +#include "sdhci.h" +#include "hw/pci/pci.h" + +static void set_qsdhci_fields(QSDHCI *s, uint8_t version, uint8_t baseclock, + bool sdma, uint64_t reg) +{ + s->props.version = version; + s->props.baseclock = baseclock; + s->props.capab.sdma = sdma; + s->props.capab.reg = reg; +} + +/* Memory mapped implementation of QSDHCI */ + +static uint16_t sdhci_mm_readw(QSDHCI *s, uint32_t reg) +{ + QSDHCI_MemoryMapped *smm = container_of(s, QSDHCI_MemoryMapped, sdhci); + return qtest_readw(smm->qts, smm->addr + reg); +} + +static uint64_t sdhci_mm_readq(QSDHCI *s, uint32_t reg) +{ + QSDHCI_MemoryMapped *smm = container_of(s, QSDHCI_MemoryMapped, sdhci); + return qtest_readq(smm->qts, smm->addr + reg); +} + +static void sdhci_mm_writeq(QSDHCI *s, uint32_t reg, uint64_t val) +{ + QSDHCI_MemoryMapped *smm = container_of(s, QSDHCI_MemoryMapped, sdhci); + qtest_writeq(smm->qts, smm->addr + reg, val); +} + +static void *sdhci_mm_get_driver(void *obj, const char *interface) +{ + QSDHCI_MemoryMapped *smm = obj; + if (!g_strcmp0(interface, "sdhci")) { + return &smm->sdhci; + } + fprintf(stderr, "%s not present in generic-sdhci\n", interface); + g_assert_not_reached(); +} + +void qos_init_sdhci_mm(QSDHCI_MemoryMapped *sdhci, QTestState *qts, + uint32_t addr, QSDHCIProperties *common) +{ + sdhci->obj.get_driver = sdhci_mm_get_driver; + sdhci->sdhci.readw = sdhci_mm_readw; + sdhci->sdhci.readq = sdhci_mm_readq; + sdhci->sdhci.writeq = sdhci_mm_writeq; + memcpy(&sdhci->sdhci.props, common, sizeof(QSDHCIProperties)); + sdhci->addr = addr; + sdhci->qts = qts; +} + +/* PCI implementation of QSDHCI */ + +static uint16_t sdhci_pci_readw(QSDHCI *s, uint32_t reg) +{ + QSDHCI_PCI *spci = container_of(s, QSDHCI_PCI, sdhci); + return qpci_io_readw(&spci->dev, spci->mem_bar, reg); +} + +static uint64_t sdhci_pci_readq(QSDHCI *s, uint32_t reg) +{ + QSDHCI_PCI *spci = container_of(s, QSDHCI_PCI, sdhci); + return qpci_io_readq(&spci->dev, spci->mem_bar, reg); +} + +static void sdhci_pci_writeq(QSDHCI *s, uint32_t reg, uint64_t val) +{ + QSDHCI_PCI *spci = container_of(s, QSDHCI_PCI, sdhci); + return qpci_io_writeq(&spci->dev, spci->mem_bar, reg, val); +} + +static void *sdhci_pci_get_driver(void *object, const char *interface) +{ + QSDHCI_PCI *spci = object; + if (!g_strcmp0(interface, "sdhci")) { + return &spci->sdhci; + } + + fprintf(stderr, "%s not present in sdhci-pci\n", interface); + g_assert_not_reached(); +} + +static void sdhci_pci_start_hw(QOSGraphObject *obj) +{ + QSDHCI_PCI *spci = (QSDHCI_PCI *)obj; + qpci_device_enable(&spci->dev); +} + +static void sdhci_destructor(QOSGraphObject *obj) +{ + QSDHCI_PCI *spci = (QSDHCI_PCI *)obj; + qpci_iounmap(&spci->dev, spci->mem_bar); +} + +static void *sdhci_pci_create(void *pci_bus, QGuestAllocator *alloc, void *addr) +{ + QSDHCI_PCI *spci = g_new0(QSDHCI_PCI, 1); + QPCIBus *bus = pci_bus; + uint64_t barsize; + + qpci_device_init(&spci->dev, bus, addr); + spci->mem_bar = qpci_iomap(&spci->dev, 0, &barsize); + spci->sdhci.readw = sdhci_pci_readw; + spci->sdhci.readq = sdhci_pci_readq; + spci->sdhci.writeq = sdhci_pci_writeq; + set_qsdhci_fields(&spci->sdhci, 2, 0, 1, 0x057834b4); + + spci->obj.get_driver = sdhci_pci_get_driver; + spci->obj.start_hw = sdhci_pci_start_hw; + spci->obj.destructor = sdhci_destructor; + return &spci->obj; +} + +static void qsdhci_register_nodes(void) +{ + QPCIAddress addr = { + .devfn = QPCI_DEVFN(4, 0), + .vendor_id = PCI_VENDOR_ID_REDHAT, + .device_id = PCI_DEVICE_ID_REDHAT_SDHCI, + }; + + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + + /* generic-sdhci */ + qos_node_create_driver("generic-sdhci", NULL); + qos_node_produces("generic-sdhci", "sdhci"); + + /* sdhci-pci */ + add_qpci_address(&opts, &addr); + qos_node_create_driver("sdhci-pci", sdhci_pci_create); + qos_node_produces("sdhci-pci", "sdhci"); + qos_node_consumes("sdhci-pci", "pci-bus", &opts); + +} + +libqos_init(qsdhci_register_nodes); diff --git a/tests/libqos/sdhci.h b/tests/libqos/sdhci.h new file mode 100644 index 0000000000..032d815c38 --- /dev/null +++ b/tests/libqos/sdhci.h @@ -0,0 +1,70 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#ifndef QGRAPH_QSDHCI +#define QGRAPH_QSDHCI + +#include "libqos/qgraph.h" +#include "pci.h" + +typedef struct QSDHCI QSDHCI; +typedef struct QSDHCI_MemoryMapped QSDHCI_MemoryMapped; +typedef struct QSDHCI_PCI QSDHCI_PCI; +typedef struct QSDHCIProperties QSDHCIProperties; + +/* Properties common to all QSDHCI devices */ +struct QSDHCIProperties { + uint8_t version; + uint8_t baseclock; + struct { + bool sdma; + uint64_t reg; + } capab; +}; + +struct QSDHCI { + uint16_t (*readw)(QSDHCI *s, uint32_t reg); + uint64_t (*readq)(QSDHCI *s, uint32_t reg); + void (*writeq)(QSDHCI *s, uint32_t reg, uint64_t val); + QSDHCIProperties props; +}; + +/* Memory Mapped implementation of QSDHCI */ +struct QSDHCI_MemoryMapped { + QOSGraphObject obj; + QTestState *qts; + QSDHCI sdhci; + uint64_t addr; +}; + +/* PCI implementation of QSDHCI */ +struct QSDHCI_PCI { + QOSGraphObject obj; + QPCIDevice dev; + QSDHCI sdhci; + QPCIBar mem_bar; +}; + +/** + * qos_init_sdhci_mm(): external constructor used by all drivers/machines + * that "contain" a #QSDHCI_MemoryMapped driver + */ +void qos_init_sdhci_mm(QSDHCI_MemoryMapped *sdhci, QTestState *qts, + uint32_t addr, QSDHCIProperties *common); + +#endif From ad60d6d4acf722ddf9db353e3757d77ae37b4885 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Tue, 3 Jul 2018 16:52:20 +0200 Subject: [PATCH 09/57] tests/libqos: arm/raspi2 machine node Add arm/raspi2 machine to the graph. This machine contains a generic-sdhci, so its constructor must take care of setting it properly when called. Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 1 + tests/libqos/arm-raspi2-machine.c | 91 +++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 tests/libqos/arm-raspi2-machine.c diff --git a/tests/Makefile.include b/tests/Makefile.include index b1b03e64cf..41d248d344 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -755,6 +755,7 @@ qos-test-obj-y += $(libqos-pc-obj-y) qos-test-obj-y += tests/libqos/sdhci.o # Machines +qos-test-obj-y += tests/libqos/arm-raspi2-machine.o qos-test-obj-y += tests/libqos/x86_64_pc-machine.o check-unit-y += tests/test-qgraph$(EXESUF) diff --git a/tests/libqos/arm-raspi2-machine.c b/tests/libqos/arm-raspi2-machine.c new file mode 100644 index 0000000000..3aff670f76 --- /dev/null +++ b/tests/libqos/arm-raspi2-machine.c @@ -0,0 +1,91 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/malloc.h" +#include "libqos/qgraph.h" +#include "sdhci.h" + +#define ARM_PAGE_SIZE 4096 +#define RASPI2_RAM_ADDR 0 +#define RASPI2_RAM_SIZE 0x20000000 + +typedef struct QRaspi2Machine QRaspi2Machine; + +struct QRaspi2Machine { + QOSGraphObject obj; + QGuestAllocator alloc; + QSDHCI_MemoryMapped sdhci; +}; + +static void *raspi2_get_driver(void *object, const char *interface) +{ + QRaspi2Machine *machine = object; + if (!g_strcmp0(interface, "memory")) { + return &machine->alloc; + } + + fprintf(stderr, "%s not present in arm/raspi2\n", interface); + g_assert_not_reached(); +} + +static QOSGraphObject *raspi2_get_device(void *obj, const char *device) +{ + QRaspi2Machine *machine = obj; + if (!g_strcmp0(device, "generic-sdhci")) { + return &machine->sdhci.obj; + } + + fprintf(stderr, "%s not present in arm/raspi2\n", device); + g_assert_not_reached(); +} + +static void raspi2_destructor(QOSGraphObject *obj) +{ + QRaspi2Machine *machine = (QRaspi2Machine *) obj; + alloc_destroy(&machine->alloc); +} + +static void *qos_create_machine_arm_raspi2(QTestState *qts) +{ + QRaspi2Machine *machine = g_new0(QRaspi2Machine, 1); + + alloc_init(&machine->alloc, 0, + RASPI2_RAM_ADDR + (1 << 20), + RASPI2_RAM_ADDR + RASPI2_RAM_SIZE, + ARM_PAGE_SIZE); + machine->obj.get_device = raspi2_get_device; + machine->obj.get_driver = raspi2_get_driver; + machine->obj.destructor = raspi2_destructor; + qos_init_sdhci_mm(&machine->sdhci, qts, 0x3f300000, &(QSDHCIProperties) { + .version = 3, + .baseclock = 52, + .capab.sdma = false, + .capab.reg = 0x052134b4 + }); + return &machine->obj; +} + +static void raspi2_register_nodes(void) +{ + qos_node_create_machine("arm/raspi2", qos_create_machine_arm_raspi2); + qos_node_contains("arm/raspi2", "generic-sdhci", NULL); +} + +libqos_init(raspi2_register_nodes); From c9599c9a607c526a39f5504e76ead3daa71a3c92 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Mon, 13 Aug 2018 16:13:54 +0200 Subject: [PATCH 10/57] tests/libqos: arm/smdkc210 machine node Add arm/smdkc210 machine machine to the graph. This machine contains generic-sdhci, so its constructor must take care of setting it properly when called. Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 1 + tests/libqos/arm-smdkc210-machine.c | 91 +++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 tests/libqos/arm-smdkc210-machine.c diff --git a/tests/Makefile.include b/tests/Makefile.include index 41d248d344..959d026b49 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -756,6 +756,7 @@ qos-test-obj-y += tests/libqos/sdhci.o # Machines qos-test-obj-y += tests/libqos/arm-raspi2-machine.o +qos-test-obj-y += tests/libqos/arm-smdkc210-machine.o qos-test-obj-y += tests/libqos/x86_64_pc-machine.o check-unit-y += tests/test-qgraph$(EXESUF) diff --git a/tests/libqos/arm-smdkc210-machine.c b/tests/libqos/arm-smdkc210-machine.c new file mode 100644 index 0000000000..1fb9dfc0cb --- /dev/null +++ b/tests/libqos/arm-smdkc210-machine.c @@ -0,0 +1,91 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/malloc.h" +#include "libqos/qgraph.h" +#include "sdhci.h" + +#define ARM_PAGE_SIZE 4096 +#define SMDKC210_RAM_ADDR 0x40000000ull +#define SMDKC210_RAM_SIZE 0x40000000ull + +typedef struct QSmdkc210Machine QSmdkc210Machine; + +struct QSmdkc210Machine { + QOSGraphObject obj; + QGuestAllocator alloc; + QSDHCI_MemoryMapped sdhci; +}; + +static void *smdkc210_get_driver(void *object, const char *interface) +{ + QSmdkc210Machine *machine = object; + if (!g_strcmp0(interface, "memory")) { + return &machine->alloc; + } + + fprintf(stderr, "%s not present in arm/smdkc210\n", interface); + g_assert_not_reached(); +} + +static QOSGraphObject *smdkc210_get_device(void *obj, const char *device) +{ + QSmdkc210Machine *machine = obj; + if (!g_strcmp0(device, "generic-sdhci")) { + return &machine->sdhci.obj; + } + + fprintf(stderr, "%s not present in arm/smdkc210\n", device); + g_assert_not_reached(); +} + +static void smdkc210_destructor(QOSGraphObject *obj) +{ + QSmdkc210Machine *machine = (QSmdkc210Machine *) obj; + alloc_destroy(&machine->alloc); +} + +static void *qos_create_machine_arm_smdkc210(QTestState *qts) +{ + QSmdkc210Machine *machine = g_new0(QSmdkc210Machine, 1); + + alloc_init(&machine->alloc, 0, + SMDKC210_RAM_ADDR, + SMDKC210_RAM_ADDR + SMDKC210_RAM_SIZE, + ARM_PAGE_SIZE); + machine->obj.get_device = smdkc210_get_device; + machine->obj.get_driver = smdkc210_get_driver; + machine->obj.destructor = smdkc210_destructor; + qos_init_sdhci_mm(&machine->sdhci, qts, 0x12510000, &(QSDHCIProperties) { + .version = 2, + .baseclock = 0, + .capab.sdma = true, + .capab.reg = 0x5e80080, + }); + return &machine->obj; +} + +static void smdkc210_register_nodes(void) +{ + qos_node_create_machine("arm/smdkc210", qos_create_machine_arm_smdkc210); + qos_node_contains("arm/smdkc210", "generic-sdhci", NULL); +} + +libqos_init(smdkc210_register_nodes); From aea9ac5172d17ab5a8c334fc7b8822b4ea949e15 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Mon, 13 Aug 2018 16:20:07 +0200 Subject: [PATCH 11/57] tests/libqos: arm/sabrelite machine node Add arm/sabrelite machine to the graph. This machine contains generic-sdhci, so its constructor must take care of setting it properly when called. Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 1 + tests/libqos/arm-sabrelite-machine.c | 91 ++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 tests/libqos/arm-sabrelite-machine.c diff --git a/tests/Makefile.include b/tests/Makefile.include index 959d026b49..47583529cc 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -756,6 +756,7 @@ qos-test-obj-y += tests/libqos/sdhci.o # Machines qos-test-obj-y += tests/libqos/arm-raspi2-machine.o +qos-test-obj-y += tests/libqos/arm-sabrelite-machine.o qos-test-obj-y += tests/libqos/arm-smdkc210-machine.o qos-test-obj-y += tests/libqos/x86_64_pc-machine.o diff --git a/tests/libqos/arm-sabrelite-machine.c b/tests/libqos/arm-sabrelite-machine.c new file mode 100644 index 0000000000..c4128d8686 --- /dev/null +++ b/tests/libqos/arm-sabrelite-machine.c @@ -0,0 +1,91 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/malloc.h" +#include "libqos/qgraph.h" +#include "sdhci.h" + +#define ARM_PAGE_SIZE 4096 +#define SABRELITE_RAM_START 0x10000000 +#define SABRELITE_RAM_END 0x30000000 + +typedef struct QSabreliteMachine QSabreliteMachine; + +struct QSabreliteMachine { + QOSGraphObject obj; + QGuestAllocator alloc; + QSDHCI_MemoryMapped sdhci; +}; + +static void *sabrelite_get_driver(void *object, const char *interface) +{ + QSabreliteMachine *machine = object; + if (!g_strcmp0(interface, "memory")) { + return &machine->alloc; + } + + fprintf(stderr, "%s not present in arm/sabrelite\n", interface); + g_assert_not_reached(); +} + +static QOSGraphObject *sabrelite_get_device(void *obj, const char *device) +{ + QSabreliteMachine *machine = obj; + if (!g_strcmp0(device, "generic-sdhci")) { + return &machine->sdhci.obj; + } + + fprintf(stderr, "%s not present in arm/sabrelite\n", device); + g_assert_not_reached(); +} + +static void sabrelite_destructor(QOSGraphObject *obj) +{ + QSabreliteMachine *machine = (QSabreliteMachine *) obj; + alloc_destroy(&machine->alloc); +} + +static void *qos_create_machine_arm_sabrelite(QTestState *qts) +{ + QSabreliteMachine *machine = g_new0(QSabreliteMachine, 1); + + alloc_init(&machine->alloc, 0, + SABRELITE_RAM_START, + SABRELITE_RAM_END, + ARM_PAGE_SIZE); + machine->obj.get_device = sabrelite_get_device; + machine->obj.get_driver = sabrelite_get_driver; + machine->obj.destructor = sabrelite_destructor; + qos_init_sdhci_mm(&machine->sdhci, qts, 0x02190000, &(QSDHCIProperties) { + .version = 3, + .baseclock = 0, + .capab.sdma = true, + .capab.reg = 0x057834b4, + }); + return &machine->obj; +} + +static void sabrelite_register_nodes(void) +{ + qos_node_create_machine("arm/sabrelite", qos_create_machine_arm_sabrelite); + qos_node_contains("arm/sabrelite", "generic-sdhci", NULL); +} + +libqos_init(sabrelite_register_nodes); From 2013199d44cf9c20ab281c512279b45bc366379b Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Mon, 13 Aug 2018 16:25:43 +0200 Subject: [PATCH 12/57] tests/libqos: arm/xilinx-zynq-a9 machine node Add xilinx-zynq-a9 machine to the graph. This machine contains generic-sdhci, so its constructor must take care of setting it properly when called. Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 1 + tests/libqos/arm-xilinx-zynq-a9-machine.c | 94 +++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 tests/libqos/arm-xilinx-zynq-a9-machine.c diff --git a/tests/Makefile.include b/tests/Makefile.include index 47583529cc..c7800d57cd 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -758,6 +758,7 @@ qos-test-obj-y += tests/libqos/sdhci.o qos-test-obj-y += tests/libqos/arm-raspi2-machine.o qos-test-obj-y += tests/libqos/arm-sabrelite-machine.o qos-test-obj-y += tests/libqos/arm-smdkc210-machine.o +qos-test-obj-y += tests/libqos/arm-xilinx-zynq-a9-machine.o qos-test-obj-y += tests/libqos/x86_64_pc-machine.o check-unit-y += tests/test-qgraph$(EXESUF) diff --git a/tests/libqos/arm-xilinx-zynq-a9-machine.c b/tests/libqos/arm-xilinx-zynq-a9-machine.c new file mode 100644 index 0000000000..4e199fcd48 --- /dev/null +++ b/tests/libqos/arm-xilinx-zynq-a9-machine.c @@ -0,0 +1,94 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/malloc.h" +#include "libqos/qgraph.h" +#include "sdhci.h" + +typedef struct QXilinxZynqA9Machine QXilinxZynqA9Machine; + +struct QXilinxZynqA9Machine { + QOSGraphObject obj; + QGuestAllocator alloc; + QSDHCI_MemoryMapped sdhci; +}; + +#define ARM_PAGE_SIZE 4096 +#define XILINX_ZYNQ_A9_RAM_ADDR 0 +#define XILINX_ZYNQ_A9_RAM_SIZE 0x20000000 + +static void *xilinx_zynq_a9_get_driver(void *object, const char *interface) +{ + QXilinxZynqA9Machine *machine = object; + if (!g_strcmp0(interface, "memory")) { + return &machine->alloc; + } + + fprintf(stderr, "%s not present in arm/xilinx-zynq-a9\n", interface); + g_assert_not_reached(); +} + +static QOSGraphObject *xilinx_zynq_a9_get_device(void *obj, const char *device) +{ + QXilinxZynqA9Machine *machine = obj; + if (!g_strcmp0(device, "generic-sdhci")) { + return &machine->sdhci.obj; + } + + fprintf(stderr, "%s not present in arm/xilinx-zynq-a9\n", device); + g_assert_not_reached(); +} + +static void xilinx_zynq_a9_destructor(QOSGraphObject *obj) +{ + QXilinxZynqA9Machine *machine = (QXilinxZynqA9Machine *) obj; + alloc_destroy(&machine->alloc); +} + +static void *qos_create_machine_arm_xilinx_zynq_a9(QTestState *qts) +{ + QXilinxZynqA9Machine *machine = g_new0(QXilinxZynqA9Machine, 1); + + alloc_init(&machine->alloc, 0, + XILINX_ZYNQ_A9_RAM_ADDR + (1 << 20), + XILINX_ZYNQ_A9_RAM_ADDR + XILINX_ZYNQ_A9_RAM_SIZE, + ARM_PAGE_SIZE); + + machine->obj.get_device = xilinx_zynq_a9_get_device; + machine->obj.get_driver = xilinx_zynq_a9_get_driver; + machine->obj.destructor = xilinx_zynq_a9_destructor; + /* Datasheet: UG585 (v1.12.1) */ + qos_init_sdhci_mm(&machine->sdhci, qts, 0xe0100000, &(QSDHCIProperties) { + .version = 2, + .baseclock = 0, + .capab.sdma = true, + .capab.reg = 0x69ec0080, + }); + return &machine->obj; +} + +static void xilinx_zynq_a9_register_nodes(void) +{ + qos_node_create_machine("arm/xilinx-zynq-a9", + qos_create_machine_arm_xilinx_zynq_a9); + qos_node_contains("arm/xilinx-zynq-a9", "generic-sdhci", NULL); +} + +libqos_init(xilinx_zynq_a9_register_nodes); From 555299cb563e4156057706e9eb26a1f23b4fc7ae Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Mon, 13 Aug 2018 16:31:53 +0200 Subject: [PATCH 13/57] tests/libqos: aarch64/xlnx-zcu102 machine node Add xlnx-zcu102 machine to the graph. This machine contains generic-sdhci, so its constructor must take care of setting it properly when called. Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 1 + tests/libqos/aarch64-xlnx-zcu102-machine.c | 94 ++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 tests/libqos/aarch64-xlnx-zcu102-machine.c diff --git a/tests/Makefile.include b/tests/Makefile.include index c7800d57cd..d675ad82c9 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -755,6 +755,7 @@ qos-test-obj-y += $(libqos-pc-obj-y) qos-test-obj-y += tests/libqos/sdhci.o # Machines +qos-test-obj-y += tests/libqos/aarch64-xlnx-zcu102-machine.o qos-test-obj-y += tests/libqos/arm-raspi2-machine.o qos-test-obj-y += tests/libqos/arm-sabrelite-machine.o qos-test-obj-y += tests/libqos/arm-smdkc210-machine.o diff --git a/tests/libqos/aarch64-xlnx-zcu102-machine.c b/tests/libqos/aarch64-xlnx-zcu102-machine.c new file mode 100644 index 0000000000..6fff040b37 --- /dev/null +++ b/tests/libqos/aarch64-xlnx-zcu102-machine.c @@ -0,0 +1,94 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/malloc.h" +#include "libqos/qgraph.h" +#include "sdhci.h" + +typedef struct QXlnxZCU102Machine QXlnxZCU102Machine; + +struct QXlnxZCU102Machine { + QOSGraphObject obj; + QGuestAllocator alloc; + QSDHCI_MemoryMapped sdhci; +}; + +#define ARM_PAGE_SIZE 4096 +#define XLNX_ZCU102_RAM_ADDR 0 +#define XLNX_ZCU102_RAM_SIZE 0x20000000 + +static void *xlnx_zcu102_get_driver(void *object, const char *interface) +{ + QXlnxZCU102Machine *machine = object; + if (!g_strcmp0(interface, "memory")) { + return &machine->alloc; + } + + fprintf(stderr, "%s not present in aarch64/xlnx-zcu102\n", interface); + g_assert_not_reached(); +} + +static QOSGraphObject *xlnx_zcu102_get_device(void *obj, const char *device) +{ + QXlnxZCU102Machine *machine = obj; + if (!g_strcmp0(device, "generic-sdhci")) { + return &machine->sdhci.obj; + } + + fprintf(stderr, "%s not present in aarch64/xlnx-zcu102\n", device); + g_assert_not_reached(); +} + +static void xlnx_zcu102_destructor(QOSGraphObject *obj) +{ + QXlnxZCU102Machine *machine = (QXlnxZCU102Machine *) obj; + alloc_destroy(&machine->alloc); +} + +static void *qos_create_machine_aarch64_xlnx_zcu102(QTestState *qts) +{ + QXlnxZCU102Machine *machine = g_new0(QXlnxZCU102Machine, 1); + + alloc_init(&machine->alloc, 0, + XLNX_ZCU102_RAM_ADDR + (1 << 20), + XLNX_ZCU102_RAM_ADDR + XLNX_ZCU102_RAM_SIZE, + ARM_PAGE_SIZE); + + machine->obj.get_device = xlnx_zcu102_get_device; + machine->obj.get_driver = xlnx_zcu102_get_driver; + machine->obj.destructor = xlnx_zcu102_destructor; + /* Datasheet: UG1085 (v1.7) */ + qos_init_sdhci_mm(&machine->sdhci, qts, 0xff160000, &(QSDHCIProperties) { + .version = 3, + .baseclock = 0, + .capab.sdma = true, + .capab.reg = 0x280737ec6481 + }); + return &machine->obj; +} + +static void xlnx_zcu102_register_nodes(void) +{ + qos_node_create_machine("aarch64/xlnx-zcu102", + qos_create_machine_aarch64_xlnx_zcu102); + qos_node_contains("aarch64/xlnx-zcu102", "generic-sdhci", NULL); +} + +libqos_init(xlnx_zcu102_register_nodes); From 757c6eac8ec0548d4c74154259535946fd90ab13 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Wed, 11 Jul 2018 14:40:25 +0200 Subject: [PATCH 14/57] qos-test: sdhci test node Convert tests/sdhci-test in first qgraph test node, sdhci-test. This test consumes an sdhci interface and checks that its function return the expected values. Note that this test does not allocate any sdhci structure, it's all done by the qtest walking graph mechanism Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 7 +- tests/sdhci-test.c | 185 +++++------------------------------------ 2 files changed, 24 insertions(+), 168 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index d675ad82c9..4b545ac905 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -229,7 +229,6 @@ check-qtest-i386-y += tests/test-announce-self$(EXESUF) check-qtest-i386-y += tests/test-x86-cpuid-compat$(EXESUF) check-qtest-i386-y += tests/numa-test$(EXESUF) check-qtest-x86_64-y += $(check-qtest-i386-y) -check-qtest-x86_64-$(CONFIG_SDHCI) += tests/sdhci-test$(EXESUF) check-qtest-alpha-y += tests/boot-serial-test$(EXESUF) check-qtest-alpha-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) @@ -299,11 +298,9 @@ check-qtest-arm-y += tests/m25p80-test$(EXESUF) check-qtest-arm-$(CONFIG_VIRTIO_BLK) += tests/virtio-blk-test$(EXESUF) check-qtest-arm-y += tests/test-arm-mptimer$(EXESUF) check-qtest-arm-y += tests/boot-serial-test$(EXESUF) -check-qtest-arm-$(CONFIG_SDHCI) += tests/sdhci-test$(EXESUF) check-qtest-arm-y += tests/hexloader-test$(EXESUF) check-qtest-aarch64-y = tests/numa-test$(EXESUF) -check-qtest-aarch64-$(CONFIG_SDHCI) += tests/sdhci-test$(EXESUF) check-qtest-aarch64-y += tests/boot-serial-test$(EXESUF) check-qtest-aarch64-y += tests/migration-test$(EXESUF) @@ -762,6 +759,9 @@ qos-test-obj-y += tests/libqos/arm-smdkc210-machine.o qos-test-obj-y += tests/libqos/arm-xilinx-zynq-a9-machine.o qos-test-obj-y += tests/libqos/x86_64_pc-machine.o +# Tests +qos-test-obj-y += tests/sdhci-test.o + check-unit-y += tests/test-qgraph$(EXESUF) tests/test-qgraph$(EXESUF): tests/test-qgraph.o $(libqgraph-obj-y) @@ -857,7 +857,6 @@ tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o tests/test-qapi-util$(EXESUF): tests/test-qapi-util.o $(test-util-obj-y) tests/numa-test$(EXESUF): tests/numa-test.o tests/vmgenid-test$(EXESUF): tests/vmgenid-test.o tests/boot-sector.o tests/acpi-utils.o -tests/sdhci-test$(EXESUF): tests/sdhci-test.o $(libqos-pc-obj-y) tests/cdrom-test$(EXESUF): tests/cdrom-test.o tests/boot-sector.o $(libqos-obj-y) tests/migration/stress$(EXESUF): tests/migration/stress.o diff --git a/tests/sdhci-test.c b/tests/sdhci-test.c index 28d481b62f..2f177e569f 100644 --- a/tests/sdhci-test.c +++ b/tests/sdhci-test.c @@ -12,6 +12,8 @@ #include "libqtest.h" #include "libqos/pci-pc.h" #include "hw/pci/pci.h" +#include "libqos/qgraph.h" +#include "libqos/sdhci.h" #define SDHC_CAPAB 0x40 FIELD(SDHC_CAPAB, BASECLKFREQ, 8, 8); /* since v2 */ @@ -20,99 +22,11 @@ FIELD(SDHC_CAPAB, SDR, 32, 3); /* since v3 */ FIELD(SDHC_CAPAB, DRIVER, 36, 3); /* since v3 */ #define SDHC_HCVER 0xFE -static const struct sdhci_t { - const char *arch, *machine; - struct { - uintptr_t addr; - uint8_t version; - uint8_t baseclock; - struct { - bool sdma; - uint64_t reg; - } capab; - } sdhci; - struct { - uint16_t vendor_id, device_id; - } pci; -} models[] = { - /* PC via PCI */ - { "x86_64", "pc", - {-1, 2, 0, {1, 0x057834b4} }, - .pci = { PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_SDHCI } }, - - /* Exynos4210 */ - { "arm", "smdkc210", - {0x12510000, 2, 0, {1, 0x5e80080} } }, - - /* i.MX 6 */ - { "arm", "sabrelite", - {0x02190000, 3, 0, {1, 0x057834b4} } }, - - /* BCM2835 */ - { "arm", "raspi2", - {0x3f300000, 3, 52, {0, 0x052134b4} } }, - - /* Zynq-7000 */ - { "arm", "xilinx-zynq-a9", /* Datasheet: UG585 (v1.12.1) */ - {0xe0100000, 2, 0, {1, 0x69ec0080} } }, - - /* ZynqMP */ - { "aarch64", "xlnx-zcu102", /* Datasheet: UG1085 (v1.7) */ - {0xff160000, 3, 0, {1, 0x280737ec6481} } }, - -}; - -typedef struct QSDHCI { - struct { - QPCIBus *bus; - QPCIDevice *dev; - } pci; - union { - QPCIBar mem_bar; - uint64_t addr; - }; -} QSDHCI; - -static uint16_t sdhci_readw(QSDHCI *s, uint32_t reg) -{ - uint16_t val; - - if (s->pci.dev) { - val = qpci_io_readw(s->pci.dev, s->mem_bar, reg); - } else { - val = qtest_readw(global_qtest, s->addr + reg); - } - - return val; -} - -static uint64_t sdhci_readq(QSDHCI *s, uint32_t reg) -{ - uint64_t val; - - if (s->pci.dev) { - val = qpci_io_readq(s->pci.dev, s->mem_bar, reg); - } else { - val = qtest_readq(global_qtest, s->addr + reg); - } - - return val; -} - -static void sdhci_writeq(QSDHCI *s, uint32_t reg, uint64_t val) -{ - if (s->pci.dev) { - qpci_io_writeq(s->pci.dev, s->mem_bar, reg, val); - } else { - qtest_writeq(global_qtest, s->addr + reg, val); - } -} - static void check_specs_version(QSDHCI *s, uint8_t version) { uint32_t v; - v = sdhci_readw(s, SDHC_HCVER); + v = s->readw(s, SDHC_HCVER); v &= 0xff; v += 1; g_assert_cmpuint(v, ==, version); @@ -122,7 +36,7 @@ static void check_capab_capareg(QSDHCI *s, uint64_t expec_capab) { uint64_t capab; - capab = sdhci_readq(s, SDHC_CAPAB); + capab = s->readq(s, SDHC_CAPAB); g_assert_cmphex(capab, ==, expec_capab); } @@ -131,11 +45,11 @@ static void check_capab_readonly(QSDHCI *s) const uint64_t vrand = 0x123456789abcdef; uint64_t capab0, capab1; - capab0 = sdhci_readq(s, SDHC_CAPAB); + capab0 = s->readq(s, SDHC_CAPAB); g_assert_cmpuint(capab0, !=, vrand); - sdhci_writeq(s, SDHC_CAPAB, vrand); - capab1 = sdhci_readq(s, SDHC_CAPAB); + s->writeq(s, SDHC_CAPAB, vrand); + capab1 = s->readq(s, SDHC_CAPAB); g_assert_cmpuint(capab1, !=, vrand); g_assert_cmpuint(capab1, ==, capab0); } @@ -147,7 +61,7 @@ static void check_capab_baseclock(QSDHCI *s, uint8_t expec_freq) if (!expec_freq) { return; } - capab = sdhci_readq(s, SDHC_CAPAB); + capab = s->readq(s, SDHC_CAPAB); capab_freq = FIELD_EX64(capab, SDHC_CAPAB, BASECLKFREQ); g_assert_cmpuint(capab_freq, ==, expec_freq); } @@ -156,7 +70,7 @@ static void check_capab_sdma(QSDHCI *s, bool supported) { uint64_t capab, capab_sdma; - capab = sdhci_readq(s, SDHC_CAPAB); + capab = s->readq(s, SDHC_CAPAB); capab_sdma = FIELD_EX64(capab, SDHC_CAPAB, SDMA); g_assert_cmpuint(capab_sdma, ==, supported); } @@ -167,7 +81,7 @@ static void check_capab_v3(QSDHCI *s, uint8_t version) if (version < 3) { /* before v3 those fields are RESERVED */ - capab = sdhci_readq(s, SDHC_CAPAB); + capab = s->readq(s, SDHC_CAPAB); capab_v3 = FIELD_EX64(capab, SDHC_CAPAB, SDR); g_assert_cmpuint(capab_v3, ==, 0); capab_v3 = FIELD_EX64(capab, SDHC_CAPAB, DRIVER); @@ -175,78 +89,21 @@ static void check_capab_v3(QSDHCI *s, uint8_t version) } } -static QSDHCI *machine_start(const struct sdhci_t *test) +static void test_registers(void *obj, void *data, QGuestAllocator *alloc) { - QSDHCI *s = g_new0(QSDHCI, 1); + QSDHCI *s = obj; - if (test->pci.vendor_id) { - /* PCI */ - uint16_t vendor_id, device_id; - uint64_t barsize; - - global_qtest = qtest_initf("-machine %s -device sdhci-pci", - test->machine); - - s->pci.bus = qpci_new_pc(global_qtest, NULL); - - /* Find PCI device and verify it's the right one */ - s->pci.dev = qpci_device_find(s->pci.bus, QPCI_DEVFN(4, 0)); - g_assert_nonnull(s->pci.dev); - vendor_id = qpci_config_readw(s->pci.dev, PCI_VENDOR_ID); - device_id = qpci_config_readw(s->pci.dev, PCI_DEVICE_ID); - g_assert(vendor_id == test->pci.vendor_id); - g_assert(device_id == test->pci.device_id); - s->mem_bar = qpci_iomap(s->pci.dev, 0, &barsize); - qpci_device_enable(s->pci.dev); - } else { - /* SysBus */ - global_qtest = qtest_initf("-machine %s", test->machine); - s->addr = test->sdhci.addr; - } - - return s; -} - -static void machine_stop(QSDHCI *s) -{ - qpci_free_pc(s->pci.bus); - g_free(s->pci.dev); - qtest_quit(global_qtest); - g_free(s); -} - -static void test_machine(const void *data) -{ - const struct sdhci_t *test = data; - QSDHCI *s; - - s = machine_start(test); - - check_specs_version(s, test->sdhci.version); - check_capab_capareg(s, test->sdhci.capab.reg); + check_specs_version(s, s->props.version); + check_capab_capareg(s, s->props.capab.reg); check_capab_readonly(s); - check_capab_v3(s, test->sdhci.version); - check_capab_sdma(s, test->sdhci.capab.sdma); - check_capab_baseclock(s, test->sdhci.baseclock); - - machine_stop(s); + check_capab_v3(s, s->props.version); + check_capab_sdma(s, s->props.capab.sdma); + check_capab_baseclock(s, s->props.baseclock); } -int main(int argc, char *argv[]) +static void register_sdhci_test(void) { - const char *arch = qtest_get_arch(); - char *name; - int i; - - g_test_init(&argc, &argv, NULL); - for (i = 0; i < ARRAY_SIZE(models); i++) { - if (strcmp(arch, models[i].arch)) { - continue; - } - name = g_strdup_printf("sdhci/%s", models[i].machine); - qtest_add_data_func(name, &models[i], test_machine); - g_free(name); - } - - return g_test_run(); + qos_add_test("registers", "sdhci", test_registers, NULL); } + +libqos_init(register_sdhci_test); From db1f266838e627cb320acd25fc9b524bd814ed73 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 5 Oct 2018 19:00:19 +0200 Subject: [PATCH 15/57] tests/qgraph: add generic PCI testcases Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 1 + tests/pci-test.c | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 tests/pci-test.c diff --git a/tests/Makefile.include b/tests/Makefile.include index 4b545ac905..4391b7f984 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -760,6 +760,7 @@ qos-test-obj-y += tests/libqos/arm-xilinx-zynq-a9-machine.o qos-test-obj-y += tests/libqos/x86_64_pc-machine.o # Tests +qos-test-obj-y += tests/pci-test.o qos-test-obj-y += tests/sdhci-test.o check-unit-y += tests/test-qgraph$(EXESUF) diff --git a/tests/pci-test.c b/tests/pci-test.c new file mode 100644 index 0000000000..ff80985093 --- /dev/null +++ b/tests/pci-test.c @@ -0,0 +1,25 @@ +/* + * QTest testcase for PCI + * + * Copyright (c) 2018 Red Hat, Inc. + * + * 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 "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" + +/* Tests only initialization so far. TODO: Replace with functional tests */ +static void nop(void *obj, void *data, QGuestAllocator *alloc) +{ +} + +static void register_pci_test(void) +{ + qos_add_test("nop", "pci-device", nop, NULL); +} + +libqos_init(register_pci_test); From b8782d2a47891a5f627f794f56d276ae69ece79e Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Tue, 10 Jul 2018 16:09:26 +0200 Subject: [PATCH 16/57] tests/libqos: pci-spapr driver and interface nodes Add pci-bus-spapr node, that produces pci-bus. Move QPCIBusSPAPR struct declaration in its header (since it will be needed by other drivers) and introduce a setter method for drivers that do not need to allocate but have to initialize QPCIBusSPAPR. Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 2 +- tests/libqos/pci-spapr.c | 116 +++++++++++++++++++++------------------ tests/libqos/pci-spapr.h | 24 ++++++++ 3 files changed, 88 insertions(+), 54 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index 4391b7f984..37ca53005e 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -748,7 +748,7 @@ libqos-virtio-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/virt # Devices qos-test-obj-y = tests/qos-test.o $(libqgraph-obj-y) -qos-test-obj-y += $(libqos-pc-obj-y) +qos-test-obj-y += $(libqos-pc-obj-y) $(libqos-spapr-obj-y) qos-test-obj-y += tests/libqos/sdhci.o # Machines diff --git a/tests/libqos/pci-spapr.c b/tests/libqos/pci-spapr.c index 59679f6a92..d9b87a7695 100644 --- a/tests/libqos/pci-spapr.c +++ b/tests/libqos/pci-spapr.c @@ -9,33 +9,13 @@ #include "libqtest.h" #include "libqos/pci-spapr.h" #include "libqos/rtas.h" +#include "libqos/qgraph.h" #include "hw/pci/pci_regs.h" #include "qemu-common.h" #include "qemu/host-utils.h" - -/* From include/hw/pci-host/spapr.h */ - -typedef struct QPCIWindow { - uint64_t pci_base; /* window address in PCI space */ - uint64_t size; /* window size */ -} QPCIWindow; - -typedef struct QPCIBusSPAPR { - QPCIBus bus; - QGuestAllocator *alloc; - - uint64_t buid; - - uint64_t pio_cpu_base; - QPCIWindow pio; - - uint64_t mmio32_cpu_base; - QPCIWindow mmio32; -} QPCIBusSPAPR; - /* * PCI devices are always little-endian * SPAPR by default is big-endian @@ -160,60 +140,90 @@ static void qpci_spapr_config_writel(QPCIBus *bus, int devfn, uint8_t offset, #define SPAPR_PCI_MMIO32_WIN_SIZE 0x80000000 /* 2 GiB */ #define SPAPR_PCI_IO_WIN_SIZE 0x10000 -QPCIBus *qpci_new_spapr(QTestState *qts, QGuestAllocator *alloc) +static void *qpci_spapr_get_driver(void *obj, const char *interface) { - QPCIBusSPAPR *ret = g_new0(QPCIBusSPAPR, 1); + QPCIBusSPAPR *qpci = obj; + if (!g_strcmp0(interface, "pci-bus")) { + return &qpci->bus; + } + fprintf(stderr, "%s not present in pci-bus-spapr", interface); + g_assert_not_reached(); +} +void qpci_init_spapr(QPCIBusSPAPR *qpci, QTestState *qts, + QGuestAllocator *alloc) +{ assert(qts); - ret->alloc = alloc; + qpci->alloc = alloc; - ret->bus.pio_readb = qpci_spapr_pio_readb; - ret->bus.pio_readw = qpci_spapr_pio_readw; - ret->bus.pio_readl = qpci_spapr_pio_readl; - ret->bus.pio_readq = qpci_spapr_pio_readq; + qpci->bus.pio_readb = qpci_spapr_pio_readb; + qpci->bus.pio_readw = qpci_spapr_pio_readw; + qpci->bus.pio_readl = qpci_spapr_pio_readl; + qpci->bus.pio_readq = qpci_spapr_pio_readq; - ret->bus.pio_writeb = qpci_spapr_pio_writeb; - ret->bus.pio_writew = qpci_spapr_pio_writew; - ret->bus.pio_writel = qpci_spapr_pio_writel; - ret->bus.pio_writeq = qpci_spapr_pio_writeq; + qpci->bus.pio_writeb = qpci_spapr_pio_writeb; + qpci->bus.pio_writew = qpci_spapr_pio_writew; + qpci->bus.pio_writel = qpci_spapr_pio_writel; + qpci->bus.pio_writeq = qpci_spapr_pio_writeq; - ret->bus.memread = qpci_spapr_memread; - ret->bus.memwrite = qpci_spapr_memwrite; + qpci->bus.memread = qpci_spapr_memread; + qpci->bus.memwrite = qpci_spapr_memwrite; - ret->bus.config_readb = qpci_spapr_config_readb; - ret->bus.config_readw = qpci_spapr_config_readw; - ret->bus.config_readl = qpci_spapr_config_readl; + qpci->bus.config_readb = qpci_spapr_config_readb; + qpci->bus.config_readw = qpci_spapr_config_readw; + qpci->bus.config_readl = qpci_spapr_config_readl; - ret->bus.config_writeb = qpci_spapr_config_writeb; - ret->bus.config_writew = qpci_spapr_config_writew; - ret->bus.config_writel = qpci_spapr_config_writel; + qpci->bus.config_writeb = qpci_spapr_config_writeb; + qpci->bus.config_writew = qpci_spapr_config_writew; + qpci->bus.config_writel = qpci_spapr_config_writel; /* FIXME: We assume the default location of the PHB for now. * Ideally we'd parse the device tree deposited in the guest to * get the window locations */ - ret->buid = 0x800000020000000ULL; + qpci->buid = 0x800000020000000ULL; - ret->pio_cpu_base = SPAPR_PCI_BASE; - ret->pio.pci_base = 0; - ret->pio.size = SPAPR_PCI_IO_WIN_SIZE; + qpci->pio_cpu_base = SPAPR_PCI_BASE; + qpci->pio.pci_base = 0; + qpci->pio.size = SPAPR_PCI_IO_WIN_SIZE; /* 32-bit portion of the MMIO window is at PCI address 2..4 GiB */ - ret->mmio32_cpu_base = SPAPR_PCI_BASE; - ret->mmio32.pci_base = SPAPR_PCI_MMIO32_WIN_SIZE; - ret->mmio32.size = SPAPR_PCI_MMIO32_WIN_SIZE; + qpci->mmio32_cpu_base = SPAPR_PCI_BASE; + qpci->mmio32.pci_base = SPAPR_PCI_MMIO32_WIN_SIZE; + qpci->mmio32.size = SPAPR_PCI_MMIO32_WIN_SIZE; - ret->bus.qts = qts; - ret->bus.pio_alloc_ptr = 0xc000; - ret->bus.mmio_alloc_ptr = ret->mmio32.pci_base; - ret->bus.mmio_limit = ret->mmio32.pci_base + ret->mmio32.size; + qpci->bus.qts = qts; + qpci->bus.pio_alloc_ptr = 0xc000; + qpci->bus.mmio_alloc_ptr = qpci->mmio32.pci_base; + qpci->bus.mmio_limit = qpci->mmio32.pci_base + qpci->mmio32.size; - return &ret->bus; + qpci->obj.get_driver = qpci_spapr_get_driver; +} + +QPCIBus *qpci_new_spapr(QTestState *qts, QGuestAllocator *alloc) +{ + QPCIBusSPAPR *qpci = g_new0(QPCIBusSPAPR, 1); + qpci_init_spapr(qpci, qts, alloc); + + return &qpci->bus; } void qpci_free_spapr(QPCIBus *bus) { - QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + QPCIBusSPAPR *s; + + if (!bus) { + return; + } + s = container_of(bus, QPCIBusSPAPR, bus); g_free(s); } + +static void qpci_spapr_register_nodes(void) +{ + qos_node_create_driver("pci-bus-spapr", NULL); + qos_node_produces("pci-bus-spapr", "pci-bus"); +} + +libqos_init(qpci_spapr_register_nodes); diff --git a/tests/libqos/pci-spapr.h b/tests/libqos/pci-spapr.h index 177e8c002c..d9e25631c6 100644 --- a/tests/libqos/pci-spapr.h +++ b/tests/libqos/pci-spapr.h @@ -10,7 +10,31 @@ #include "libqos/malloc.h" #include "libqos/pci.h" +#include "libqos/qgraph.h" +/* From include/hw/pci-host/spapr.h */ + +typedef struct QPCIWindow { + uint64_t pci_base; /* window address in PCI space */ + uint64_t size; /* window size */ +} QPCIWindow; + +typedef struct QPCIBusSPAPR { + QOSGraphObject obj; + QPCIBus bus; + QGuestAllocator *alloc; + + uint64_t buid; + + uint64_t pio_cpu_base; + QPCIWindow pio; + + uint64_t mmio32_cpu_base; + QPCIWindow mmio32; +} QPCIBusSPAPR; + +void qpci_init_spapr(QPCIBusSPAPR *ret, QTestState *qts, + QGuestAllocator *alloc); QPCIBus *qpci_new_spapr(QTestState *qts, QGuestAllocator *alloc); void qpci_free_spapr(QPCIBus *bus); From 7985b582d68a990d0285afd3ad6f9eff5ff5c3c5 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Tue, 10 Jul 2018 15:38:43 +0200 Subject: [PATCH 17/57] tests/qgraph: ppc64/pseries machine node Add pseries machine for the ppc64 QEMU binary. This machine contains a spapr-pci-host-bridge driver, that contains itself a pci-bus-spapr that produces the pci-bus interface. Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 1 + tests/libqos/ppc64_pseries-machine.c | 111 +++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 tests/libqos/ppc64_pseries-machine.c diff --git a/tests/Makefile.include b/tests/Makefile.include index 37ca53005e..a89032739c 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -757,6 +757,7 @@ qos-test-obj-y += tests/libqos/arm-raspi2-machine.o qos-test-obj-y += tests/libqos/arm-sabrelite-machine.o qos-test-obj-y += tests/libqos/arm-smdkc210-machine.o qos-test-obj-y += tests/libqos/arm-xilinx-zynq-a9-machine.o +qos-test-obj-y += tests/libqos/ppc64_pseries-machine.o qos-test-obj-y += tests/libqos/x86_64_pc-machine.o # Tests diff --git a/tests/libqos/ppc64_pseries-machine.c b/tests/libqos/ppc64_pseries-machine.c new file mode 100644 index 0000000000..2f3640010d --- /dev/null +++ b/tests/libqos/ppc64_pseries-machine.c @@ -0,0 +1,111 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + + #include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "pci-spapr.h" +#include "libqos/malloc-spapr.h" + +typedef struct QSPAPR_pci_host QSPAPR_pci_host; +typedef struct Qppc64_pseriesMachine Qppc64_pseriesMachine; + +struct QSPAPR_pci_host { + QOSGraphObject obj; + QPCIBusSPAPR pci; +}; + +struct Qppc64_pseriesMachine { + QOSGraphObject obj; + QGuestAllocator alloc; + QSPAPR_pci_host bridge; +}; + +/* QSPAPR_pci_host */ + +static QOSGraphObject *QSPAPR_host_get_device(void *obj, const char *device) +{ + QSPAPR_pci_host *host = obj; + if (!g_strcmp0(device, "pci-bus-spapr")) { + return &host->pci.obj; + } + fprintf(stderr, "%s not present in QSPAPR_pci_host\n", device); + g_assert_not_reached(); +} + +static void qos_create_QSPAPR_host(QSPAPR_pci_host *host, + QTestState *qts, + QGuestAllocator *alloc) +{ + host->obj.get_device = QSPAPR_host_get_device; + qpci_init_spapr(&host->pci, qts, alloc); +} + +/* ppc64/pseries machine */ + +static void spapr_destructor(QOSGraphObject *obj) +{ + Qppc64_pseriesMachine *machine = (Qppc64_pseriesMachine *) obj; + alloc_destroy(&machine->alloc); +} + +static void *spapr_get_driver(void *object, const char *interface) +{ + Qppc64_pseriesMachine *machine = object; + if (!g_strcmp0(interface, "memory")) { + return &machine->alloc; + } + + fprintf(stderr, "%s not present in ppc64/pseries\n", interface); + g_assert_not_reached(); +} + +static QOSGraphObject *spapr_get_device(void *obj, const char *device) +{ + Qppc64_pseriesMachine *machine = obj; + if (!g_strcmp0(device, "spapr-pci-host-bridge")) { + return &machine->bridge.obj; + } + + fprintf(stderr, "%s not present in ppc64/pseries\n", device); + g_assert_not_reached(); +} + +static void *qos_create_machine_spapr(QTestState *qts) +{ + Qppc64_pseriesMachine *machine = g_new0(Qppc64_pseriesMachine, 1); + machine->obj.get_device = spapr_get_device; + machine->obj.get_driver = spapr_get_driver; + machine->obj.destructor = spapr_destructor; + spapr_alloc_init(&machine->alloc, qts, ALLOC_NO_FLAGS); + + qos_create_QSPAPR_host(&machine->bridge, qts, &machine->alloc); + + return &machine->obj; +} + +static void spapr_machine_register_nodes(void) +{ + qos_node_create_machine("ppc64/pseries", qos_create_machine_spapr); + qos_node_create_driver("spapr-pci-host-bridge", NULL); + qos_node_contains("ppc64/pseries", "spapr-pci-host-bridge", NULL); + qos_node_contains("spapr-pci-host-bridge", "pci-bus-spapr", NULL); +} + +libqos_init(spapr_machine_register_nodes); + From 92bbafc71812fb9d20e9da00286a2e319e77a103 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Thu, 9 Aug 2018 12:44:56 +0200 Subject: [PATCH 18/57] tests/libqos: has_buggy_msi flag The Qgraph framework makes any test using pci bus run the same function using pci-pci and pci-spapr bus. However, some tests are not ready to use the spapr bus, due to a MSI bug. Until it does not get fixed, this flag allows them to skip the test Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/libqos/pci-pc.c | 3 +++ tests/libqos/pci-spapr.c | 3 +++ tests/libqos/pci.c | 14 ++++++++++++++ tests/libqos/pci.h | 4 ++++ 4 files changed, 24 insertions(+) diff --git a/tests/libqos/pci-pc.c b/tests/libqos/pci-pc.c index d729ad4057..4ab16facf2 100644 --- a/tests/libqos/pci-pc.c +++ b/tests/libqos/pci-pc.c @@ -124,6 +124,9 @@ void qpci_init_pc(QPCIBusPC *qpci, QTestState *qts, QGuestAllocator *alloc) { assert(qts); + /* tests can use pci-bus */ + qpci->bus.has_buggy_msi = FALSE; + qpci->bus.pio_readb = qpci_pc_pio_readb; qpci->bus.pio_readw = qpci_pc_pio_readw; qpci->bus.pio_readl = qpci_pc_pio_readl; diff --git a/tests/libqos/pci-spapr.c b/tests/libqos/pci-spapr.c index d9b87a7695..6925925997 100644 --- a/tests/libqos/pci-spapr.c +++ b/tests/libqos/pci-spapr.c @@ -155,6 +155,9 @@ void qpci_init_spapr(QPCIBusSPAPR *qpci, QTestState *qts, { assert(qts); + /* tests cannot use spapr, needs to be fixed first */ + qpci->bus.has_buggy_msi = TRUE; + qpci->alloc = alloc; qpci->bus.pio_readb = qpci_spapr_pio_readb; diff --git a/tests/libqos/pci.c b/tests/libqos/pci.c index 8257904ba7..662ee7a517 100644 --- a/tests/libqos/pci.c +++ b/tests/libqos/pci.c @@ -51,6 +51,20 @@ void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id, } } +bool qpci_has_buggy_msi(QPCIDevice *dev) +{ + return dev->bus->has_buggy_msi; +} + +bool qpci_check_buggy_msi(QPCIDevice *dev) +{ + if (qpci_has_buggy_msi(dev)) { + g_test_skip("Skipping due to incomplete support for MSI"); + return true; + } + return false; +} + static void qpci_device_set(QPCIDevice *dev, QPCIBus *bus, int devfn) { g_assert(dev); diff --git a/tests/libqos/pci.h b/tests/libqos/pci.h index 9fd521aed5..8e1d292a7d 100644 --- a/tests/libqos/pci.h +++ b/tests/libqos/pci.h @@ -53,6 +53,7 @@ struct QPCIBus { QTestState *qts; uint16_t pio_alloc_ptr; uint64_t mmio_alloc_ptr, mmio_limit; + bool has_buggy_msi; /* TRUE for spapr, FALSE for pci */ }; @@ -81,6 +82,9 @@ void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id, QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn); void qpci_device_init(QPCIDevice *dev, QPCIBus *bus, QPCIAddress *addr); +bool qpci_has_buggy_msi(QPCIDevice *dev); +bool qpci_check_buggy_msi(QPCIDevice *dev); + void qpci_device_enable(QPCIDevice *dev); uint8_t qpci_find_capability(QPCIDevice *dev, uint8_t id); void qpci_msix_enable(QPCIDevice *dev); From a4cc719b6b92743b8102494d68b29aaa2d7a6e43 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Fri, 20 Jul 2018 15:35:36 +0200 Subject: [PATCH 19/57] tests/libqos: e1000e driver and interface nodes Add qgraph nodes for virtio-e1000e. It consumes a pci-bus, and it's directly used by tests (e1000e is pci based). Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 1 + tests/libqos/e1000e.c | 260 +++++++++++++++++++++++++++++++++++++++++ tests/libqos/e1000e.h | 53 +++++++++ 3 files changed, 314 insertions(+) create mode 100644 tests/libqos/e1000e.c create mode 100644 tests/libqos/e1000e.h diff --git a/tests/Makefile.include b/tests/Makefile.include index a89032739c..5bd60a55e8 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -749,6 +749,7 @@ libqos-virtio-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/virt # Devices qos-test-obj-y = tests/qos-test.o $(libqgraph-obj-y) qos-test-obj-y += $(libqos-pc-obj-y) $(libqos-spapr-obj-y) +qos-test-obj-y += tests/libqos/e1000e.o qos-test-obj-y += tests/libqos/sdhci.o # Machines diff --git a/tests/libqos/e1000e.c b/tests/libqos/e1000e.c new file mode 100644 index 0000000000..54d3898899 --- /dev/null +++ b/tests/libqos/e1000e.c @@ -0,0 +1,260 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu-common.h" +#include "libqos/pci-pc.h" +#include "qemu/sockets.h" +#include "qemu/iov.h" +#include "qemu/bitops.h" +#include "libqos/malloc.h" +#include "libqos/qgraph.h" +#include "e1000e.h" + +#define E1000E_IMS (0x00d0) + +#define E1000E_STATUS (0x0008) +#define E1000E_STATUS_LU BIT(1) +#define E1000E_STATUS_ASDV1000 BIT(9) + +#define E1000E_CTRL (0x0000) +#define E1000E_CTRL_RESET BIT(26) + +#define E1000E_RCTL (0x0100) +#define E1000E_RCTL_EN BIT(1) +#define E1000E_RCTL_UPE BIT(3) +#define E1000E_RCTL_MPE BIT(4) + +#define E1000E_RFCTL (0x5008) +#define E1000E_RFCTL_EXTEN BIT(15) + +#define E1000E_TCTL (0x0400) +#define E1000E_TCTL_EN BIT(1) + +#define E1000E_CTRL_EXT (0x0018) +#define E1000E_CTRL_EXT_DRV_LOAD BIT(28) +#define E1000E_CTRL_EXT_TXLSFLOW BIT(22) + +#define E1000E_IVAR (0x00E4) +#define E1000E_IVAR_TEST_CFG ((E1000E_RX0_MSG_ID << 0) | BIT(3) | \ + (E1000E_TX0_MSG_ID << 8) | BIT(11) | \ + (E1000E_OTHER_MSG_ID << 16) | BIT(19) | \ + BIT(31)) + +#define E1000E_RING_LEN (0x1000) + +#define E1000E_TDBAL (0x3800) + +#define E1000E_TDBAH (0x3804) +#define E1000E_TDH (0x3810) + +#define E1000E_RDBAL (0x2800) +#define E1000E_RDBAH (0x2804) +#define E1000E_RDH (0x2810) + +#define E1000E_TXD_LEN (16) +#define E1000E_RXD_LEN (16) + +static void e1000e_macreg_write(QE1000E *d, uint32_t reg, uint32_t val) +{ + QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e); + qpci_io_writel(&d_pci->pci_dev, d_pci->mac_regs, reg, val); +} + +static uint32_t e1000e_macreg_read(QE1000E *d, uint32_t reg) +{ + QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e); + return qpci_io_readl(&d_pci->pci_dev, d_pci->mac_regs, reg); +} + +void e1000e_tx_ring_push(QE1000E *d, void *descr) +{ + uint32_t tail = e1000e_macreg_read(d, E1000E_TDT); + uint32_t len = e1000e_macreg_read(d, E1000E_TDLEN) / E1000E_TXD_LEN; + + memwrite(d->tx_ring + tail * E1000E_TXD_LEN, descr, E1000E_TXD_LEN); + e1000e_macreg_write(d, E1000E_TDT, (tail + 1) % len); + + /* Read WB data for the packet transmitted */ + memread(d->tx_ring + tail * E1000E_TXD_LEN, descr, E1000E_TXD_LEN); +} + +void e1000e_rx_ring_push(QE1000E *d, void *descr) +{ + uint32_t tail = e1000e_macreg_read(d, E1000E_RDT); + uint32_t len = e1000e_macreg_read(d, E1000E_RDLEN) / E1000E_RXD_LEN; + + memwrite(d->rx_ring + tail * E1000E_RXD_LEN, descr, E1000E_RXD_LEN); + e1000e_macreg_write(d, E1000E_RDT, (tail + 1) % len); + + /* Read WB data for the packet received */ + memread(d->rx_ring + tail * E1000E_RXD_LEN, descr, E1000E_RXD_LEN); +} + +static void e1000e_foreach_callback(QPCIDevice *dev, int devfn, void *data) +{ + QPCIDevice *res = data; + memcpy(res, dev, sizeof(QPCIDevice)); + g_free(dev); +} + +void e1000e_wait_isr(QE1000E *d, uint16_t msg_id) +{ + QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e); + guint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; + + do { + if (qpci_msix_pending(&d_pci->pci_dev, msg_id)) { + return; + } + clock_step(10000); + } while (g_get_monotonic_time() < end_time); + + g_error("Timeout expired"); +} + +static void e1000e_pci_destructor(QOSGraphObject *obj) +{ + QE1000E_PCI *epci = (QE1000E_PCI *) obj; + qpci_iounmap(&epci->pci_dev, epci->mac_regs); + qpci_msix_disable(&epci->pci_dev); +} + +static void e1000e_pci_start_hw(QOSGraphObject *obj) +{ + QE1000E_PCI *d = (QE1000E_PCI *) obj; + uint32_t val; + + /* Enable the device */ + qpci_device_enable(&d->pci_dev); + + /* Reset the device */ + val = e1000e_macreg_read(&d->e1000e, E1000E_CTRL); + e1000e_macreg_write(&d->e1000e, E1000E_CTRL, val | E1000E_CTRL_RESET); + + /* Enable and configure MSI-X */ + qpci_msix_enable(&d->pci_dev); + e1000e_macreg_write(&d->e1000e, E1000E_IVAR, E1000E_IVAR_TEST_CFG); + + /* Check the device status - link and speed */ + val = e1000e_macreg_read(&d->e1000e, E1000E_STATUS); + g_assert_cmphex(val & (E1000E_STATUS_LU | E1000E_STATUS_ASDV1000), + ==, E1000E_STATUS_LU | E1000E_STATUS_ASDV1000); + + /* Initialize TX/RX logic */ + e1000e_macreg_write(&d->e1000e, E1000E_RCTL, 0); + e1000e_macreg_write(&d->e1000e, E1000E_TCTL, 0); + + /* Notify the device that the driver is ready */ + val = e1000e_macreg_read(&d->e1000e, E1000E_CTRL_EXT); + e1000e_macreg_write(&d->e1000e, E1000E_CTRL_EXT, + val | E1000E_CTRL_EXT_DRV_LOAD | E1000E_CTRL_EXT_TXLSFLOW); + + e1000e_macreg_write(&d->e1000e, E1000E_TDBAL, + (uint32_t) d->e1000e.tx_ring); + e1000e_macreg_write(&d->e1000e, E1000E_TDBAH, + (uint32_t) (d->e1000e.tx_ring >> 32)); + e1000e_macreg_write(&d->e1000e, E1000E_TDLEN, E1000E_RING_LEN); + e1000e_macreg_write(&d->e1000e, E1000E_TDT, 0); + e1000e_macreg_write(&d->e1000e, E1000E_TDH, 0); + + /* Enable transmit */ + e1000e_macreg_write(&d->e1000e, E1000E_TCTL, E1000E_TCTL_EN); + e1000e_macreg_write(&d->e1000e, E1000E_RDBAL, + (uint32_t)d->e1000e.rx_ring); + e1000e_macreg_write(&d->e1000e, E1000E_RDBAH, + (uint32_t)(d->e1000e.rx_ring >> 32)); + e1000e_macreg_write(&d->e1000e, E1000E_RDLEN, E1000E_RING_LEN); + e1000e_macreg_write(&d->e1000e, E1000E_RDT, 0); + e1000e_macreg_write(&d->e1000e, E1000E_RDH, 0); + + /* Enable receive */ + e1000e_macreg_write(&d->e1000e, E1000E_RFCTL, E1000E_RFCTL_EXTEN); + e1000e_macreg_write(&d->e1000e, E1000E_RCTL, E1000E_RCTL_EN | + E1000E_RCTL_UPE | + E1000E_RCTL_MPE); + + /* Enable all interrupts */ + e1000e_macreg_write(&d->e1000e, E1000E_IMS, 0xFFFFFFFF); + +} + +static void *e1000e_pci_get_driver(void *obj, const char *interface) +{ + QE1000E_PCI *epci = obj; + if (!g_strcmp0(interface, "e1000e-if")) { + return &epci->e1000e; + } + + /* implicit contains */ + if (!g_strcmp0(interface, "pci-device")) { + return &epci->pci_dev; + } + + fprintf(stderr, "%s not present in e1000e\n", interface); + g_assert_not_reached(); +} + +static void *e1000e_pci_create(void *pci_bus, QGuestAllocator *alloc, + void *addr) +{ + QE1000E_PCI *d = g_new0(QE1000E_PCI, 1); + QPCIBus *bus = pci_bus; + QPCIAddress *address = addr; + + qpci_device_foreach(bus, address->vendor_id, address->device_id, + e1000e_foreach_callback, &d->pci_dev); + + /* Map BAR0 (mac registers) */ + d->mac_regs = qpci_iomap(&d->pci_dev, 0, NULL); + + /* Allocate and setup TX ring */ + d->e1000e.tx_ring = guest_alloc(alloc, E1000E_RING_LEN); + g_assert(d->e1000e.tx_ring != 0); + + /* Allocate and setup RX ring */ + d->e1000e.rx_ring = guest_alloc(alloc, E1000E_RING_LEN); + g_assert(d->e1000e.rx_ring != 0); + + d->obj.get_driver = e1000e_pci_get_driver; + d->obj.start_hw = e1000e_pci_start_hw; + d->obj.destructor = e1000e_pci_destructor; + + return &d->obj; +} + +static void e1000e_register_nodes(void) +{ + QPCIAddress addr = { + .vendor_id = 0x8086, + .device_id = 0x10D3, + }; + + /* FIXME: every test using this node needs to setup a -netdev socket,id=hs0 + * otherwise QEMU is not going to start */ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "netdev=hs0", + }; + add_qpci_address(&opts, &addr); + + qos_node_create_driver("e1000e", e1000e_pci_create); + qos_node_consumes("e1000e", "pci-bus", &opts); +} + +libqos_init(e1000e_register_nodes); diff --git a/tests/libqos/e1000e.h b/tests/libqos/e1000e.h new file mode 100644 index 0000000000..9d37094f43 --- /dev/null +++ b/tests/libqos/e1000e.h @@ -0,0 +1,53 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#ifndef QGRAPH_E1000E +#define QGRAPH_E1000E + +#include "libqos/qgraph.h" +#include "pci.h" + +#define E1000E_RX0_MSG_ID (0) +#define E1000E_TX0_MSG_ID (1) +#define E1000E_OTHER_MSG_ID (2) + +#define E1000E_TDLEN (0x3808) +#define E1000E_TDT (0x3818) +#define E1000E_RDLEN (0x2808) +#define E1000E_RDT (0x2818) + +typedef struct QE1000E QE1000E; +typedef struct QE1000E_PCI QE1000E_PCI; + +struct QE1000E { + uint64_t tx_ring; + uint64_t rx_ring; +}; + +struct QE1000E_PCI { + QOSGraphObject obj; + QPCIDevice pci_dev; + QPCIBar mac_regs; + QE1000E e1000e; +}; + +void e1000e_wait_isr(QE1000E *d, uint16_t msg_id); +void e1000e_tx_ring_push(QE1000E *d, void *descr); +void e1000e_rx_ring_push(QE1000E *d, void *descr); + +#endif From b026393c34779578106d3cc889271b1757b8d287 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Fri, 20 Jul 2018 15:35:56 +0200 Subject: [PATCH 20/57] qos-test: e1000e test node Convert tests/e1000e-test in qgraph test node, e1000e-test. This test consumes an e1000e interface and checks that its function return the expected values. Note that this test does not allocate any e1000e structure, it's all done by the qtest walking graph mechanism Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 3 +- tests/e1000e-test.c | 364 +++++++++-------------------------------- 2 files changed, 82 insertions(+), 285 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index 5bd60a55e8..dbd0e6857c 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -167,7 +167,6 @@ check-qtest-virtio-$(CONFIG_VIRTIO_SERIAL) += tests/virtio-serial-test$(EXESUF) check-qtest-virtio-y += $(check-qtest-virtioserial-y) check-qtest-pci-y += tests/e1000-test$(EXESUF) -check-qtest-pci-y += tests/e1000e-test$(EXESUF) check-qtest-pci-$(CONFIG_RTL8139_PCI) += tests/rtl8139-test$(EXESUF) check-qtest-pci-$(CONFIG_PCNET_PCI) += tests/pcnet-test$(EXESUF) check-qtest-pci-$(CONFIG_EEPRO100_PCI) += tests/eepro100-test$(EXESUF) @@ -762,6 +761,7 @@ qos-test-obj-y += tests/libqos/ppc64_pseries-machine.o qos-test-obj-y += tests/libqos/x86_64_pc-machine.o # Tests +qos-test-obj-y += tests/e1000e-test.o qos-test-obj-y += tests/pci-test.o qos-test-obj-y += tests/sdhci-test.o @@ -801,7 +801,6 @@ tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y) tests/q35-test$(EXESUF): tests/q35-test.o $(libqos-pc-obj-y) tests/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y) tests/e1000-test$(EXESUF): tests/e1000-test.o -tests/e1000e-test$(EXESUF): tests/e1000e-test.o $(libqos-pc-obj-y) tests/rtl8139-test$(EXESUF): tests/rtl8139-test.o $(libqos-pc-obj-y) tests/pcnet-test$(EXESUF): tests/pcnet-test.o tests/pnv-xscom-test$(EXESUF): tests/pnv-xscom-test.o diff --git a/tests/e1000e-test.c b/tests/e1000e-test.c index 17a12b8ca0..77ba8095bb 100644 --- a/tests/e1000e-test.c +++ b/tests/e1000e-test.c @@ -32,210 +32,9 @@ #include "qemu/iov.h" #include "qemu/bitops.h" #include "libqos/malloc.h" -#include "libqos/malloc-pc.h" -#include "libqos/malloc-generic.h" +#include "libqos/e1000e.h" -#define E1000E_IMS (0x00d0) - -#define E1000E_STATUS (0x0008) -#define E1000E_STATUS_LU BIT(1) -#define E1000E_STATUS_ASDV1000 BIT(9) - -#define E1000E_CTRL (0x0000) -#define E1000E_CTRL_RESET BIT(26) - -#define E1000E_RCTL (0x0100) -#define E1000E_RCTL_EN BIT(1) -#define E1000E_RCTL_UPE BIT(3) -#define E1000E_RCTL_MPE BIT(4) - -#define E1000E_RFCTL (0x5008) -#define E1000E_RFCTL_EXTEN BIT(15) - -#define E1000E_TCTL (0x0400) -#define E1000E_TCTL_EN BIT(1) - -#define E1000E_CTRL_EXT (0x0018) -#define E1000E_CTRL_EXT_DRV_LOAD BIT(28) -#define E1000E_CTRL_EXT_TXLSFLOW BIT(22) - -#define E1000E_RX0_MSG_ID (0) -#define E1000E_TX0_MSG_ID (1) -#define E1000E_OTHER_MSG_ID (2) - -#define E1000E_IVAR (0x00E4) -#define E1000E_IVAR_TEST_CFG ((E1000E_RX0_MSG_ID << 0) | BIT(3) | \ - (E1000E_TX0_MSG_ID << 8) | BIT(11) | \ - (E1000E_OTHER_MSG_ID << 16) | BIT(19) | \ - BIT(31)) - -#define E1000E_RING_LEN (0x1000) -#define E1000E_TXD_LEN (16) -#define E1000E_RXD_LEN (16) - -#define E1000E_TDBAL (0x3800) -#define E1000E_TDBAH (0x3804) -#define E1000E_TDLEN (0x3808) -#define E1000E_TDH (0x3810) -#define E1000E_TDT (0x3818) - -#define E1000E_RDBAL (0x2800) -#define E1000E_RDBAH (0x2804) -#define E1000E_RDLEN (0x2808) -#define E1000E_RDH (0x2810) -#define E1000E_RDT (0x2818) - -typedef struct e1000e_device { - QPCIDevice *pci_dev; - QPCIBar mac_regs; - - uint64_t tx_ring; - uint64_t rx_ring; -} e1000e_device; - -static int test_sockets[2]; -static QGuestAllocator test_alloc; -static QPCIBus *test_bus; - -static void e1000e_pci_foreach_callback(QPCIDevice *dev, int devfn, void *data) -{ - QPCIDevice **res = data; - - g_assert_null(*res); - *res = dev; -} - -static QPCIDevice *e1000e_device_find(QPCIBus *bus) -{ - static const int e1000e_vendor_id = 0x8086; - static const int e1000e_dev_id = 0x10D3; - - QPCIDevice *e1000e_dev = NULL; - - qpci_device_foreach(bus, e1000e_vendor_id, e1000e_dev_id, - e1000e_pci_foreach_callback, &e1000e_dev); - - g_assert_nonnull(e1000e_dev); - - return e1000e_dev; -} - -static void e1000e_macreg_write(e1000e_device *d, uint32_t reg, uint32_t val) -{ - qpci_io_writel(d->pci_dev, d->mac_regs, reg, val); -} - -static uint32_t e1000e_macreg_read(e1000e_device *d, uint32_t reg) -{ - return qpci_io_readl(d->pci_dev, d->mac_regs, reg); -} - -static void e1000e_device_init(QPCIBus *bus, e1000e_device *d) -{ - uint32_t val; - - d->pci_dev = e1000e_device_find(bus); - - /* Enable the device */ - qpci_device_enable(d->pci_dev); - - /* Map BAR0 (mac registers) */ - d->mac_regs = qpci_iomap(d->pci_dev, 0, NULL); - - /* Reset the device */ - val = e1000e_macreg_read(d, E1000E_CTRL); - e1000e_macreg_write(d, E1000E_CTRL, val | E1000E_CTRL_RESET); - - /* Enable and configure MSI-X */ - qpci_msix_enable(d->pci_dev); - e1000e_macreg_write(d, E1000E_IVAR, E1000E_IVAR_TEST_CFG); - - /* Check the device status - link and speed */ - val = e1000e_macreg_read(d, E1000E_STATUS); - g_assert_cmphex(val & (E1000E_STATUS_LU | E1000E_STATUS_ASDV1000), - ==, E1000E_STATUS_LU | E1000E_STATUS_ASDV1000); - - /* Initialize TX/RX logic */ - e1000e_macreg_write(d, E1000E_RCTL, 0); - e1000e_macreg_write(d, E1000E_TCTL, 0); - - /* Notify the device that the driver is ready */ - val = e1000e_macreg_read(d, E1000E_CTRL_EXT); - e1000e_macreg_write(d, E1000E_CTRL_EXT, - val | E1000E_CTRL_EXT_DRV_LOAD | E1000E_CTRL_EXT_TXLSFLOW); - - /* Allocate and setup TX ring */ - d->tx_ring = guest_alloc(&test_alloc, E1000E_RING_LEN); - g_assert(d->tx_ring != 0); - - e1000e_macreg_write(d, E1000E_TDBAL, (uint32_t) d->tx_ring); - e1000e_macreg_write(d, E1000E_TDBAH, (uint32_t) (d->tx_ring >> 32)); - e1000e_macreg_write(d, E1000E_TDLEN, E1000E_RING_LEN); - e1000e_macreg_write(d, E1000E_TDT, 0); - e1000e_macreg_write(d, E1000E_TDH, 0); - - /* Enable transmit */ - e1000e_macreg_write(d, E1000E_TCTL, E1000E_TCTL_EN); - - /* Allocate and setup RX ring */ - d->rx_ring = guest_alloc(&test_alloc, E1000E_RING_LEN); - g_assert(d->rx_ring != 0); - - e1000e_macreg_write(d, E1000E_RDBAL, (uint32_t)d->rx_ring); - e1000e_macreg_write(d, E1000E_RDBAH, (uint32_t)(d->rx_ring >> 32)); - e1000e_macreg_write(d, E1000E_RDLEN, E1000E_RING_LEN); - e1000e_macreg_write(d, E1000E_RDT, 0); - e1000e_macreg_write(d, E1000E_RDH, 0); - - /* Enable receive */ - e1000e_macreg_write(d, E1000E_RFCTL, E1000E_RFCTL_EXTEN); - e1000e_macreg_write(d, E1000E_RCTL, E1000E_RCTL_EN | - E1000E_RCTL_UPE | - E1000E_RCTL_MPE); - - /* Enable all interrupts */ - e1000e_macreg_write(d, E1000E_IMS, 0xFFFFFFFF); -} - -static void e1000e_tx_ring_push(e1000e_device *d, void *descr) -{ - uint32_t tail = e1000e_macreg_read(d, E1000E_TDT); - uint32_t len = e1000e_macreg_read(d, E1000E_TDLEN) / E1000E_TXD_LEN; - - memwrite(d->tx_ring + tail * E1000E_TXD_LEN, descr, E1000E_TXD_LEN); - e1000e_macreg_write(d, E1000E_TDT, (tail + 1) % len); - - /* Read WB data for the packet transmitted */ - memread(d->tx_ring + tail * E1000E_TXD_LEN, descr, E1000E_TXD_LEN); -} - -static void e1000e_rx_ring_push(e1000e_device *d, void *descr) -{ - uint32_t tail = e1000e_macreg_read(d, E1000E_RDT); - uint32_t len = e1000e_macreg_read(d, E1000E_RDLEN) / E1000E_RXD_LEN; - - memwrite(d->rx_ring + tail * E1000E_RXD_LEN, descr, E1000E_RXD_LEN); - e1000e_macreg_write(d, E1000E_RDT, (tail + 1) % len); - - /* Read WB data for the packet received */ - memread(d->rx_ring + tail * E1000E_RXD_LEN, descr, E1000E_RXD_LEN); -} - -static void e1000e_wait_isr(e1000e_device *d, uint16_t msg_id) -{ - guint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; - - do { - if (qpci_msix_pending(d->pci_dev, msg_id)) { - return; - } - clock_step(10000); - } while (g_get_monotonic_time() < end_time); - - g_error("Timeout expired"); -} - -static void e1000e_send_verify(e1000e_device *d) +static void e1000e_send_verify(QE1000E *d, int *test_sockets, QGuestAllocator *alloc) { struct { uint64_t buffer_addr; @@ -268,7 +67,7 @@ static void e1000e_send_verify(e1000e_device *d) uint32_t recv_len; /* Prepare test data buffer */ - uint64_t data = guest_alloc(&test_alloc, data_len); + uint64_t data = guest_alloc(alloc, data_len); memwrite(data, "TEST", 5); /* Prepare TX descriptor */ @@ -296,10 +95,10 @@ static void e1000e_send_verify(e1000e_device *d) g_assert_cmpstr(buffer, == , "TEST"); /* Free test data buffer */ - guest_free(&test_alloc, data); + guest_free(alloc, data); } -static void e1000e_receive_verify(e1000e_device *d) +static void e1000e_receive_verify(QE1000E *d, int *test_sockets, QGuestAllocator *alloc) { union { struct { @@ -348,7 +147,7 @@ static void e1000e_receive_verify(e1000e_device *d) g_assert_cmpint(ret, == , sizeof(test) + sizeof(len)); /* Prepare test data buffer */ - uint64_t data = guest_alloc(&test_alloc, data_len); + uint64_t data = guest_alloc(alloc, data_len); /* Prepare RX descriptor */ memset(&descr, 0, sizeof(descr)); @@ -369,109 +168,108 @@ static void e1000e_receive_verify(e1000e_device *d) g_assert_cmpstr(buffer, == , "TEST"); /* Free test data buffer */ - guest_free(&test_alloc, data); + guest_free(alloc, data); } -static void e1000e_device_clear(QPCIBus *bus, e1000e_device *d) +static void test_e1000e_init(void *obj, void *data, QGuestAllocator * alloc) { - qpci_iounmap(d->pci_dev, d->mac_regs); - qpci_msix_disable(d->pci_dev); + /* init does nothing */ } -static void data_test_init(e1000e_device *d) +static void test_e1000e_tx(void *obj, void *data, QGuestAllocator * alloc) { - char *cmdline; + QE1000E_PCI *e1000e = obj; + QE1000E *d = &e1000e->e1000e; + QOSGraphObject *e_object = obj; + QPCIDevice *dev = e_object->get_driver(e_object, "pci-device"); - int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, test_sockets); - g_assert_cmpint(ret, != , -1); + /* FIXME: add spapr support */ + if (qpci_check_buggy_msi(dev)) { + return; + } - cmdline = g_strdup_printf("-netdev socket,fd=%d,id=hs0 " - "-device e1000e,netdev=hs0", test_sockets[1]); - g_assert_nonnull(cmdline); - - qtest_start(cmdline); - g_free(cmdline); - - pc_alloc_init(&test_alloc, global_qtest, 0); - test_bus = qpci_new_pc(global_qtest, &test_alloc); - g_assert_nonnull(test_bus); - - e1000e_device_init(test_bus, d); + e1000e_send_verify(d, data, alloc); } -static void data_test_clear(e1000e_device *d) +static void test_e1000e_rx(void *obj, void *data, QGuestAllocator * alloc) { - e1000e_device_clear(test_bus, d); - close(test_sockets[0]); - alloc_destroy(&test_alloc); - g_free(d->pci_dev); - qpci_free_pc(test_bus); - qtest_end(); + QE1000E_PCI *e1000e = obj; + QE1000E *d = &e1000e->e1000e; + QOSGraphObject *e_object = obj; + QPCIDevice *dev = e_object->get_driver(e_object, "pci-device"); + + /* FIXME: add spapr support */ + if (qpci_check_buggy_msi(dev)) { + return; + } + + e1000e_receive_verify(d, data, alloc); } -static void test_e1000e_init(gconstpointer data) -{ - e1000e_device d; - - data_test_init(&d); - data_test_clear(&d); -} - -static void test_e1000e_tx(gconstpointer data) -{ - e1000e_device d; - - data_test_init(&d); - e1000e_send_verify(&d); - data_test_clear(&d); -} - -static void test_e1000e_rx(gconstpointer data) -{ - e1000e_device d; - - data_test_init(&d); - e1000e_receive_verify(&d); - data_test_clear(&d); -} - -static void test_e1000e_multiple_transfers(gconstpointer data) +static void test_e1000e_multiple_transfers(void *obj, void *data, + QGuestAllocator *alloc) { static const long iterations = 4 * 1024; long i; - e1000e_device d; + QE1000E_PCI *e1000e = obj; + QE1000E *d = &e1000e->e1000e; + QOSGraphObject *e_object = obj; + QPCIDevice *dev = e_object->get_driver(e_object, "pci-device"); - data_test_init(&d); - - for (i = 0; i < iterations; i++) { - e1000e_send_verify(&d); - e1000e_receive_verify(&d); + /* FIXME: add spapr support */ + if (qpci_check_buggy_msi(dev)) { + return; + } + + for (i = 0; i < iterations; i++) { + e1000e_send_verify(d, data, alloc); + e1000e_receive_verify(d, data, alloc); } - data_test_clear(&d); } -static void test_e1000e_hotplug(gconstpointer data) +static void test_e1000e_hotplug(void *obj, void *data, QGuestAllocator * alloc) { - qtest_start("-device e1000e"); - qtest_qmp_device_add("e1000e", "e1000e_net", "{'addr': '0x06'}"); qpci_unplug_acpi_device_test("e1000e_net", 0x06); - - qtest_end(); } -int main(int argc, char **argv) +static void data_test_clear(void *sockets) { - g_test_init(&argc, &argv, NULL); + int *test_sockets = sockets; - qtest_add_data_func("e1000e/init", NULL, test_e1000e_init); - qtest_add_data_func("e1000e/tx", NULL, test_e1000e_tx); - qtest_add_data_func("e1000e/rx", NULL, test_e1000e_rx); - qtest_add_data_func("e1000e/multiple_transfers", NULL, - test_e1000e_multiple_transfers); - qtest_add_data_func("e1000e/hotplug", NULL, test_e1000e_hotplug); - - return g_test_run(); + close(test_sockets[0]); + qos_invalidate_command_line(); + close(test_sockets[1]); + g_free(test_sockets); } + +static void *data_test_init(GString *cmd_line, void *arg) +{ + int *test_sockets = g_new(int, 2); + int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, test_sockets); + g_assert_cmpint(ret, != , -1); + + g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ", + test_sockets[1]); + + g_test_queue_destroy(data_test_clear, test_sockets); + return test_sockets; +} + +static void register_e1000e_test(void) +{ + QOSGraphTestOptions opts = { + .before = data_test_init, + }; + + qos_add_test("init", "e1000e", test_e1000e_init, &opts); + qos_add_test("tx", "e1000e", test_e1000e_tx, &opts); + qos_add_test("rx", "e1000e", test_e1000e_rx, &opts); + qos_add_test("multiple_transfers", "e1000e", + test_e1000e_multiple_transfers, &opts); + qos_add_test("hotplug", "e1000e", test_e1000e_hotplug, &opts); +} + +libqos_init(register_e1000e_test); From 1ce66ecec0f26ea6a3b450c66bde6fe171471f1c Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Wed, 18 Jul 2018 16:02:41 +0200 Subject: [PATCH 21/57] tests/libqos: virtio-pci driver and interface nodes Add QOSGraphObject to QVirtioPCIDevice structure, with a basic constructor. virtio-pci is not present in qgraph, since it will be used as starting point by its subclasses (virtio-*-pci) Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 1 + tests/libqos/virtio-pci.c | 106 ++++++++++++++++++++++++++++---------- tests/libqos/virtio-pci.h | 14 +++++ tests/virtio-blk-test.c | 6 +++ 4 files changed, 99 insertions(+), 28 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index dbd0e6857c..c9c6019bc8 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -750,6 +750,7 @@ qos-test-obj-y = tests/qos-test.o $(libqgraph-obj-y) qos-test-obj-y += $(libqos-pc-obj-y) $(libqos-spapr-obj-y) qos-test-obj-y += tests/libqos/e1000e.o qos-test-obj-y += tests/libqos/sdhci.o +qos-test-obj-y += $(libqos-virtio-obj-y) # Machines qos-test-obj-y += tests/libqos/aarch64-xlnx-zcu102-machine.o diff --git a/tests/libqos/virtio-pci.c b/tests/libqos/virtio-pci.c index 550dede0a2..6bcdbbb64d 100644 --- a/tests/libqos/virtio-pci.c +++ b/tests/libqos/virtio-pci.c @@ -15,12 +15,26 @@ #include "libqos/pci-pc.h" #include "libqos/malloc.h" #include "libqos/malloc-pc.h" +#include "libqos/qgraph.h" #include "standard-headers/linux/virtio_ring.h" #include "standard-headers/linux/virtio_pci.h" #include "hw/pci/pci.h" #include "hw/pci/pci_regs.h" +/* virtio-pci is a superclass of all virtio-xxx-pci devices; + * the relation between virtio-pci and virtio-xxx-pci is implicit, + * and therefore virtio-pci does not produce virtio and is not + * reached by any edge, not even as a "contains" edge. + * In facts, every device is a QVirtioPCIDevice with + * additional fields, since every one has its own + * number of queues and various attributes. + * Virtio-pci provides default functions to start the + * hw and destroy the object, and nodes that want to + * override them should always remember to call the + * original qvirtio_pci_destructor and qvirtio_pci_start_hw. + */ + typedef struct QVirtioPCIForeachData { void (*func)(QVirtioDevice *d, void *data); uint16_t device_type; @@ -32,36 +46,36 @@ typedef struct QVirtioPCIForeachData { void qvirtio_pci_device_free(QVirtioPCIDevice *dev) { g_free(dev->pdev); - g_free(dev); } -static QVirtioPCIDevice *qpcidevice_to_qvirtiodevice(QPCIDevice *pdev) +static void qvirtio_pci_init_from_pcidev(QVirtioPCIDevice *dev, QPCIDevice *pci_dev) { - QVirtioPCIDevice *vpcidev; - vpcidev = g_malloc0(sizeof(*vpcidev)); + dev->pdev = pci_dev; + dev->vdev.device_type = qpci_config_readw(pci_dev, PCI_SUBSYSTEM_ID); - if (pdev) { - vpcidev->pdev = pdev; - vpcidev->vdev.device_type = - qpci_config_readw(vpcidev->pdev, PCI_SUBSYSTEM_ID); - } + dev->config_msix_entry = -1; - vpcidev->config_msix_entry = -1; + dev->vdev.bus = &qvirtio_pci; - return vpcidev; + /* each virtio-xxx-pci device should override at least this function */ + dev->obj.get_driver = NULL; + dev->obj.start_hw = qvirtio_pci_start_hw; + dev->obj.destructor = qvirtio_pci_destructor; } static void qvirtio_pci_foreach_callback( QPCIDevice *dev, int devfn, void *data) { QVirtioPCIForeachData *d = data; - QVirtioPCIDevice *vpcidev = qpcidevice_to_qvirtiodevice(dev); + QVirtioPCIDevice *vpcidev = g_new0(QVirtioPCIDevice, 1); + qvirtio_pci_init_from_pcidev(vpcidev, dev); if (vpcidev->vdev.device_type == d->device_type && (!d->has_slot || vpcidev->pdev->devfn == d->slot << 3)) { d->func(&vpcidev->vdev, d->user_data); } else { qvirtio_pci_device_free(vpcidev); + g_free(vpcidev); } } @@ -69,14 +83,14 @@ static void qvirtio_pci_assign_device(QVirtioDevice *d, void *data) { QVirtioPCIDevice **vpcidev = data; assert(!*vpcidev); - *vpcidev = (QVirtioPCIDevice *)d; + *vpcidev = container_of(d, QVirtioPCIDevice, vdev); } #define CONFIG_BASE(dev) (VIRTIO_PCI_CONFIG_OFF((dev)->pdev->msix_enabled)) static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t off) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); return qpci_io_readb(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); } @@ -90,7 +104,7 @@ static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t off) static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t off) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); uint16_t value; value = qpci_io_readw(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); @@ -102,7 +116,7 @@ static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t off) static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, uint64_t off) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); uint32_t value; value = qpci_io_readl(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); @@ -114,7 +128,7 @@ static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, uint64_t off) static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t off) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); uint64_t val; val = qpci_io_readq(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); @@ -127,37 +141,37 @@ static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t off) static uint32_t qvirtio_pci_get_features(QVirtioDevice *d) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_HOST_FEATURES); } static void qvirtio_pci_set_features(QVirtioDevice *d, uint32_t features) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_GUEST_FEATURES, features); } static uint32_t qvirtio_pci_get_guest_features(QVirtioDevice *d) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_GUEST_FEATURES); } static uint8_t qvirtio_pci_get_status(QVirtioDevice *d) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_STATUS); } static void qvirtio_pci_set_status(QVirtioDevice *d, uint8_t status) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); qpci_io_writeb(dev->pdev, dev->bar, VIRTIO_PCI_STATUS, status); } static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); QVirtQueuePCI *vqpci = (QVirtQueuePCI *)vq; uint32_t data; @@ -182,7 +196,7 @@ static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); uint32_t data; if (dev->pdev->msix_enabled) { @@ -206,19 +220,19 @@ static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d) static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); qpci_io_writeb(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_SEL, index); } static uint16_t qvirtio_pci_get_queue_size(QVirtioDevice *d) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); return qpci_io_readw(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NUM); } static void qvirtio_pci_set_queue_address(QVirtioDevice *d, uint32_t pfn) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_PFN, pfn); } @@ -270,7 +284,7 @@ static void qvirtio_pci_virtqueue_cleanup(QVirtQueue *vq, static void qvirtio_pci_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); qpci_io_writew(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NOTIFY, vq->index); } @@ -294,6 +308,7 @@ const QVirtioBus qvirtio_pci = { .virtqueue_kick = qvirtio_pci_virtqueue_kick, }; +/* TODO: delete this once qgraph is completed */ static void qvirtio_pci_foreach(QPCIBus *bus, uint16_t device_type, bool has_slot, int slot, void (*func)(QVirtioDevice *d, void *data), void *data) @@ -416,3 +431,38 @@ void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d, vector = qpci_io_readw(d->pdev, d->bar, VIRTIO_MSI_CONFIG_VECTOR); g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR); } + +void qvirtio_pci_destructor(QOSGraphObject *obj) +{ + QVirtioPCIDevice *dev = (QVirtioPCIDevice *)obj; + qvirtio_pci_device_disable(dev); + qvirtio_pci_device_free(dev); +} + +void qvirtio_pci_start_hw(QOSGraphObject *obj) +{ + QVirtioPCIDevice *dev = (QVirtioPCIDevice *)obj; + qvirtio_pci_device_enable(dev); + qvirtio_start_device(&dev->vdev); +} + +void virtio_pci_init(QVirtioPCIDevice *dev, QPCIBus *bus, QPCIAddress * addr) +{ + QPCIDevice *pci_dev = qpci_device_find(bus, addr->devfn); + g_assert_nonnull(pci_dev); + qvirtio_pci_init_from_pcidev(dev, pci_dev); +} + +QVirtioPCIDevice *virtio_pci_new(QPCIBus *bus, QPCIAddress * addr) +{ + QVirtioPCIDevice *dev; + QPCIDevice *pci_dev = qpci_device_find(bus, addr->devfn); + if (!pci_dev) { + return NULL; + } + + dev = g_new0(QVirtioPCIDevice, 1); + qvirtio_pci_init_from_pcidev(dev, pci_dev); + dev->obj.free = g_free; + return dev; +} diff --git a/tests/libqos/virtio-pci.h b/tests/libqos/virtio-pci.h index 6ef19094cb..56313527a7 100644 --- a/tests/libqos/virtio-pci.h +++ b/tests/libqos/virtio-pci.h @@ -12,8 +12,10 @@ #include "libqos/virtio.h" #include "libqos/pci.h" +#include "libqos/qgraph.h" typedef struct QVirtioPCIDevice { + QOSGraphObject obj; QVirtioDevice vdev; QPCIDevice *pdev; QPCIBar bar; @@ -31,11 +33,23 @@ typedef struct QVirtQueuePCI { extern const QVirtioBus qvirtio_pci; +void virtio_pci_init(QVirtioPCIDevice *dev, QPCIBus *bus, QPCIAddress * addr); +QVirtioPCIDevice *virtio_pci_new(QPCIBus *bus, QPCIAddress * addr); QVirtioPCIDevice *qvirtio_pci_device_find(QPCIBus *bus, uint16_t device_type); QVirtioPCIDevice *qvirtio_pci_device_find_slot(QPCIBus *bus, uint16_t device_type, int slot); void qvirtio_pci_device_free(QVirtioPCIDevice *dev); +/* virtio-pci object functions available for subclasses that + * override the original start_hw and destroy + * function. All virtio-xxx-pci subclass that override must + * take care of calling these two functions in the respective + * places + */ +void qvirtio_pci_destructor(QOSGraphObject *obj); +void qvirtio_pci_start_hw(QOSGraphObject *obj); + + void qvirtio_pci_device_enable(QVirtioPCIDevice *d); void qvirtio_pci_device_disable(QVirtioPCIDevice *d); diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c index b449970d01..8f8ef19eb6 100644 --- a/tests/virtio-blk-test.c +++ b/tests/virtio-blk-test.c @@ -412,6 +412,7 @@ static void pci_basic(void) qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, &qs->alloc); qvirtio_pci_device_disable(dev); qvirtio_pci_device_free(dev); + g_free(dev); qtest_shutdown(qs); } @@ -505,6 +506,7 @@ static void pci_indirect(void) qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, &qs->alloc); qvirtio_pci_device_disable(dev); qvirtio_pci_device_free(dev); + g_free(dev); qtest_shutdown(qs); } @@ -534,6 +536,7 @@ static void pci_config(void) qvirtio_pci_device_disable(dev); qvirtio_pci_device_free(dev); + g_free(dev); qtest_shutdown(qs); } @@ -642,6 +645,7 @@ static void pci_msix(void) qpci_msix_disable(dev->pdev); qvirtio_pci_device_disable(dev); qvirtio_pci_device_free(dev); + g_free(dev); qtest_shutdown(qs); } @@ -765,6 +769,7 @@ static void pci_idx(void) qpci_msix_disable(dev->pdev); qvirtio_pci_device_disable(dev); qvirtio_pci_device_free(dev); + g_free(dev); qtest_shutdown(qs); } @@ -785,6 +790,7 @@ static void pci_hotplug(void) g_assert(dev); qvirtio_pci_device_disable(dev); qvirtio_pci_device_free(dev); + g_free(dev); /* unplug secondary disk */ if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { From d5006a45f572ee4c0768d810d91920b2b2a7421b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 7 Oct 2018 09:37:44 +0200 Subject: [PATCH 22/57] tests/libqos: remove global_qtest from virtio endianness checks This is needed to support migration tests with qgraph. Signed-off-by: Paolo Bonzini --- tests/libqos/virtio-pci.c | 11 ++++++++++- tests/libqos/virtio.c | 5 +++++ tests/libqos/virtio.h | 8 ++------ 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/tests/libqos/virtio-pci.c b/tests/libqos/virtio-pci.c index 6bcdbbb64d..0b58b74267 100644 --- a/tests/libqos/virtio-pci.c +++ b/tests/libqos/virtio-pci.c @@ -43,6 +43,14 @@ typedef struct QVirtioPCIForeachData { void *user_data; } QVirtioPCIForeachData; +static inline bool qvirtio_pci_is_big_endian(QVirtioPCIDevice *dev) +{ + QPCIBus *bus = dev->pdev->bus; + + /* FIXME: virtio 1.0 is always little-endian */ + return qtest_big_endian(bus->qts); +} + void qvirtio_pci_device_free(QVirtioPCIDevice *dev) { g_free(dev->pdev); @@ -56,6 +64,7 @@ static void qvirtio_pci_init_from_pcidev(QVirtioPCIDevice *dev, QPCIDevice *pci_ dev->config_msix_entry = -1; dev->vdev.bus = &qvirtio_pci; + dev->vdev.big_endian = qvirtio_pci_is_big_endian(dev); /* each virtio-xxx-pci device should override at least this function */ dev->obj.get_driver = NULL; @@ -99,7 +108,7 @@ static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t off) * so with a big-endian guest the order has been reversed, * reverse it again * virtio-1.0 is always little-endian, like PCI, but this - * case will be managed inside qvirtio_is_big_endian() + * case will be managed inside qvirtio_pci_is_big_endian() */ static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t off) diff --git a/tests/libqos/virtio.c b/tests/libqos/virtio.c index c1ff0206d5..5a62950ccd 100644 --- a/tests/libqos/virtio.c +++ b/tests/libqos/virtio.c @@ -372,3 +372,8 @@ void qvirtio_start_device(QVirtioDevice *vdev) qvirtio_set_acknowledge(vdev); qvirtio_set_driver(vdev); } + +bool qvirtio_is_big_endian(QVirtioDevice *d) +{ + return d->big_endian; +} diff --git a/tests/libqos/virtio.h b/tests/libqos/virtio.h index 2c68668de0..7f0f9dd920 100644 --- a/tests/libqos/virtio.h +++ b/tests/libqos/virtio.h @@ -21,6 +21,7 @@ typedef struct QVirtioDevice { const QVirtioBus *bus; /* Device type */ uint16_t device_type; + bool big_endian; } QVirtioDevice; typedef struct QVirtQueue { @@ -90,12 +91,6 @@ struct QVirtioBus { void (*virtqueue_kick)(QVirtioDevice *d, QVirtQueue *vq); }; -static inline bool qvirtio_is_big_endian(QVirtioDevice *d) -{ - /* FIXME: virtio 1.0 is always little-endian */ - return qtest_big_endian(global_qtest); -} - static inline uint32_t qvring_size(uint32_t num, uint32_t align) { return ((sizeof(struct vring_desc) * num + sizeof(uint16_t) * (3 + num) @@ -109,6 +104,7 @@ uint32_t qvirtio_config_readl(QVirtioDevice *d, uint64_t addr); uint64_t qvirtio_config_readq(QVirtioDevice *d, uint64_t addr); uint32_t qvirtio_get_features(QVirtioDevice *d); void qvirtio_set_features(QVirtioDevice *d, uint32_t features); +bool qvirtio_is_big_endian(QVirtioDevice *d); void qvirtio_reset(QVirtioDevice *d); void qvirtio_set_acknowledge(QVirtioDevice *d); From 57ed038ad977883af6e68fa33a3981a8de243e5f Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Thu, 19 Jul 2018 15:14:44 +0200 Subject: [PATCH 23/57] tests/libqos: virtio-mmio driver and interface nodes Add virtio-mmio node in qgraph framework. virtio-mmio produces virtio-bus, the interface consumed by all virtio-*-device nodes. Being a memory-mapped device, it doesn't have to provide a constructor to qgraph, since it's always "contained" inside some other nodes. Signed-off-by: Emanuele Giuseppe Esposito Reviewed-by: Laurent Vivier Signed-off-by: Paolo Bonzini --- tests/libqos/virtio-mmio.c | 116 ++++++++++++++++++++++--------------- tests/libqos/virtio-mmio.h | 6 +- tests/virtio-blk-test.c | 3 +- 3 files changed, 77 insertions(+), 48 deletions(-) diff --git a/tests/libqos/virtio-mmio.c b/tests/libqos/virtio-mmio.c index 7aa8383338..3678c07ef0 100644 --- a/tests/libqos/virtio-mmio.c +++ b/tests/libqos/virtio-mmio.c @@ -12,74 +12,74 @@ #include "libqos/virtio.h" #include "libqos/virtio-mmio.h" #include "libqos/malloc.h" -#include "libqos/malloc-generic.h" +#include "libqos/qgraph.h" #include "standard-headers/linux/virtio_ring.h" static uint8_t qvirtio_mmio_config_readb(QVirtioDevice *d, uint64_t off) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - return readb(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); + return qtest_readb(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); } static uint16_t qvirtio_mmio_config_readw(QVirtioDevice *d, uint64_t off) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - return readw(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); + return qtest_readw(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); } static uint32_t qvirtio_mmio_config_readl(QVirtioDevice *d, uint64_t off) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - return readl(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); + return qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); } static uint64_t qvirtio_mmio_config_readq(QVirtioDevice *d, uint64_t off) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - return readq(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); + return qtest_readq(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); } static uint32_t qvirtio_mmio_get_features(QVirtioDevice *d) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - writel(dev->addr + QVIRTIO_MMIO_HOST_FEATURES_SEL, 0); - return readl(dev->addr + QVIRTIO_MMIO_HOST_FEATURES); + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_HOST_FEATURES_SEL, 0); + return qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_HOST_FEATURES); } static void qvirtio_mmio_set_features(QVirtioDevice *d, uint32_t features) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); dev->features = features; - writel(dev->addr + QVIRTIO_MMIO_GUEST_FEATURES_SEL, 0); - writel(dev->addr + QVIRTIO_MMIO_GUEST_FEATURES, features); + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_GUEST_FEATURES_SEL, 0); + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_GUEST_FEATURES, features); } static uint32_t qvirtio_mmio_get_guest_features(QVirtioDevice *d) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); return dev->features; } static uint8_t qvirtio_mmio_get_status(QVirtioDevice *d) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - return (uint8_t)readl(dev->addr + QVIRTIO_MMIO_DEVICE_STATUS); + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); + return (uint8_t)qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_STATUS); } static void qvirtio_mmio_set_status(QVirtioDevice *d, uint8_t status) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - writel(dev->addr + QVIRTIO_MMIO_DEVICE_STATUS, (uint32_t)status); + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_STATUS, (uint32_t)status); } static bool qvirtio_mmio_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); uint32_t isr; - isr = readl(dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 1; + isr = qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 1; if (isr != 0) { - writel(dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 1); + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 1); return true; } @@ -88,12 +88,12 @@ static bool qvirtio_mmio_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) static bool qvirtio_mmio_get_config_isr_status(QVirtioDevice *d) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); uint32_t isr; - isr = readl(dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 2; + isr = qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 2; if (isr != 0) { - writel(dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 2); + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 2); return true; } @@ -102,34 +102,34 @@ static bool qvirtio_mmio_get_config_isr_status(QVirtioDevice *d) static void qvirtio_mmio_queue_select(QVirtioDevice *d, uint16_t index) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - writel(dev->addr + QVIRTIO_MMIO_QUEUE_SEL, (uint32_t)index); + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_SEL, (uint32_t)index); - g_assert_cmphex(readl(dev->addr + QVIRTIO_MMIO_QUEUE_PFN), ==, 0); + g_assert_cmphex(qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_PFN), ==, 0); } static uint16_t qvirtio_mmio_get_queue_size(QVirtioDevice *d) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - return (uint16_t)readl(dev->addr + QVIRTIO_MMIO_QUEUE_NUM_MAX); + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); + return (uint16_t)qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_NUM_MAX); } static void qvirtio_mmio_set_queue_address(QVirtioDevice *d, uint32_t pfn) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - writel(dev->addr + QVIRTIO_MMIO_QUEUE_PFN, pfn); + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_PFN, pfn); } static QVirtQueue *qvirtio_mmio_virtqueue_setup(QVirtioDevice *d, QGuestAllocator *alloc, uint16_t index) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); QVirtQueue *vq; uint64_t addr; vq = g_malloc0(sizeof(*vq)); qvirtio_mmio_queue_select(d, index); - writel(dev->addr + QVIRTIO_MMIO_QUEUE_ALIGN, dev->page_size); + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_ALIGN, dev->page_size); vq->index = index; vq->size = qvirtio_mmio_get_queue_size(d); @@ -139,7 +139,7 @@ static QVirtQueue *qvirtio_mmio_virtqueue_setup(QVirtioDevice *d, vq->indirect = (dev->features & (1u << VIRTIO_RING_F_INDIRECT_DESC)) != 0; vq->event = (dev->features & (1u << VIRTIO_RING_F_EVENT_IDX)) != 0; - writel(dev->addr + QVIRTIO_MMIO_QUEUE_NUM, vq->size); + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_NUM, vq->size); /* Check different than 0 */ g_assert_cmpint(vq->size, !=, 0); @@ -163,8 +163,8 @@ static void qvirtio_mmio_virtqueue_cleanup(QVirtQueue *vq, static void qvirtio_mmio_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - writel(dev->addr + QVIRTIO_MMIO_QUEUE_NOTIFY, vq->index); + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_NOTIFY, vq->index); } const QVirtioBus qvirtio_mmio = { @@ -187,21 +187,45 @@ const QVirtioBus qvirtio_mmio = { .virtqueue_kick = qvirtio_mmio_virtqueue_kick, }; -QVirtioMMIODevice *qvirtio_mmio_init_device(uint64_t addr, uint32_t page_size) +static void *qvirtio_mmio_get_driver(void *obj, const char *interface) { - QVirtioMMIODevice *dev; - uint32_t magic; - dev = g_malloc0(sizeof(*dev)); + QVirtioMMIODevice *virtio_mmio = obj; + if (!g_strcmp0(interface, "virtio-bus")) { + return &virtio_mmio->vdev; + } + fprintf(stderr, "%s not present in virtio-mmio\n", interface); + g_assert_not_reached(); +} - magic = readl(addr + QVIRTIO_MMIO_MAGIC_VALUE); +static void qvirtio_mmio_start_hw(QOSGraphObject *obj) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *) obj; + qvirtio_start_device(&dev->vdev); +} + +void qvirtio_mmio_init_device(QVirtioMMIODevice *dev, QTestState *qts, + uint64_t addr, uint32_t page_size) +{ + uint32_t magic; + magic = qtest_readl(qts, addr + QVIRTIO_MMIO_MAGIC_VALUE); g_assert(magic == ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)); + dev->qts = qts; dev->addr = addr; dev->page_size = page_size; - dev->vdev.device_type = readl(addr + QVIRTIO_MMIO_DEVICE_ID); + dev->vdev.device_type = qtest_readl(qts, addr + QVIRTIO_MMIO_DEVICE_ID); dev->vdev.bus = &qvirtio_mmio; - writel(addr + QVIRTIO_MMIO_GUEST_PAGE_SIZE, page_size); + qtest_writel(qts, addr + QVIRTIO_MMIO_GUEST_PAGE_SIZE, page_size); - return dev; + dev->obj.get_driver = qvirtio_mmio_get_driver; + dev->obj.start_hw = qvirtio_mmio_start_hw; } + +static void virtio_mmio_register_nodes(void) +{ + qos_node_create_driver("virtio-mmio", NULL); + qos_node_produces("virtio-mmio", "virtio-bus"); +} + +libqos_init(virtio_mmio_register_nodes); diff --git a/tests/libqos/virtio-mmio.h b/tests/libqos/virtio-mmio.h index e3e52b9ce1..17a17141c3 100644 --- a/tests/libqos/virtio-mmio.h +++ b/tests/libqos/virtio-mmio.h @@ -11,6 +11,7 @@ #define LIBQOS_VIRTIO_MMIO_H #include "libqos/virtio.h" +#include "libqos/qgraph.h" #define QVIRTIO_MMIO_MAGIC_VALUE 0x000 #define QVIRTIO_MMIO_VERSION 0x004 @@ -33,7 +34,9 @@ #define QVIRTIO_MMIO_DEVICE_SPECIFIC 0x100 typedef struct QVirtioMMIODevice { + QOSGraphObject obj; QVirtioDevice vdev; + QTestState *qts; uint64_t addr; uint32_t page_size; uint32_t features; /* As it cannot be read later, save it */ @@ -41,6 +44,7 @@ typedef struct QVirtioMMIODevice { extern const QVirtioBus qvirtio_mmio; -QVirtioMMIODevice *qvirtio_mmio_init_device(uint64_t addr, uint32_t page_size); +void qvirtio_mmio_init_device(QVirtioMMIODevice *dev, QTestState *qts, + uint64_t addr, uint32_t page_size); #endif diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c index 8f8ef19eb6..3a8126f194 100644 --- a/tests/virtio-blk-test.c +++ b/tests/virtio-blk-test.c @@ -833,7 +833,8 @@ static void mmio_basic(void) arm_test_start(); - dev = qvirtio_mmio_init_device(MMIO_DEV_BASE_ADDR, MMIO_PAGE_SIZE); + dev = g_malloc0(sizeof(QVirtioMMIODevice)); + qvirtio_mmio_init_device(dev, global_qtest, MMIO_DEV_BASE_ADDR, MMIO_PAGE_SIZE); g_assert(dev != NULL); g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK); From d18d2b57e7ccb896078fc66e97183a7d0b0c80aa Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Wed, 18 Jul 2018 16:32:49 +0200 Subject: [PATCH 24/57] tests/libqos: arm/virt machine node Add arm/virt machine to the graph. This machine contains virtio-mmio, so its constructor must take care of setting it properly when called. Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 1 + tests/libqos/arm-virt-machine.c | 90 +++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 tests/libqos/arm-virt-machine.c diff --git a/tests/Makefile.include b/tests/Makefile.include index c9c6019bc8..4a9e2e40ba 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -757,6 +757,7 @@ qos-test-obj-y += tests/libqos/aarch64-xlnx-zcu102-machine.o qos-test-obj-y += tests/libqos/arm-raspi2-machine.o qos-test-obj-y += tests/libqos/arm-sabrelite-machine.o qos-test-obj-y += tests/libqos/arm-smdkc210-machine.o +qos-test-obj-y += tests/libqos/arm-virt-machine.o qos-test-obj-y += tests/libqos/arm-xilinx-zynq-a9-machine.o qos-test-obj-y += tests/libqos/ppc64_pseries-machine.o qos-test-obj-y += tests/libqos/x86_64_pc-machine.o diff --git a/tests/libqos/arm-virt-machine.c b/tests/libqos/arm-virt-machine.c new file mode 100644 index 0000000000..2abc431ecf --- /dev/null +++ b/tests/libqos/arm-virt-machine.c @@ -0,0 +1,90 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/malloc.h" +#include "libqos/qgraph.h" +#include "libqos/virtio-mmio.h" + +#define ARM_PAGE_SIZE 4096 +#define VIRTIO_MMIO_BASE_ADDR 0x0A003E00 +#define ARM_VIRT_RAM_ADDR 0x40000000 +#define ARM_VIRT_RAM_SIZE 0x20000000 +#define VIRTIO_MMIO_SIZE 0x00000200 + +typedef struct QVirtMachine QVirtMachine; + +struct QVirtMachine { + QOSGraphObject obj; + QGuestAllocator alloc; + QVirtioMMIODevice virtio_mmio; +}; + +static void virt_destructor(QOSGraphObject *obj) +{ + QVirtMachine *machine = (QVirtMachine *) obj; + alloc_destroy(&machine->alloc); +} + +static void *virt_get_driver(void *object, const char *interface) +{ + QVirtMachine *machine = object; + if (!g_strcmp0(interface, "memory")) { + return &machine->alloc; + } + + fprintf(stderr, "%s not present in arm/virtio\n", interface); + g_assert_not_reached(); +} + +static QOSGraphObject *virt_get_device(void *obj, const char *device) +{ + QVirtMachine *machine = obj; + if (!g_strcmp0(device, "virtio-mmio")) { + return &machine->virtio_mmio.obj; + } + + fprintf(stderr, "%s not present in arm/virtio\n", device); + g_assert_not_reached(); +} + +static void *qos_create_machine_arm_virt(QTestState *qts) +{ + QVirtMachine *machine = g_new0(QVirtMachine, 1); + + alloc_init(&machine->alloc, 0, + ARM_VIRT_RAM_ADDR, + ARM_VIRT_RAM_ADDR + ARM_VIRT_RAM_SIZE, + ARM_PAGE_SIZE); + qvirtio_mmio_init_device(&machine->virtio_mmio, qts, VIRTIO_MMIO_BASE_ADDR, + VIRTIO_MMIO_SIZE); + + machine->obj.get_device = virt_get_device; + machine->obj.get_driver = virt_get_driver; + machine->obj.destructor = virt_destructor; + return machine; +} + +static void virtio_mmio_register_nodes(void) +{ + qos_node_create_machine("arm/virt", qos_create_machine_arm_virt); + qos_node_contains("arm/virt", "virtio-mmio", NULL); +} + +libqos_init(virtio_mmio_register_nodes); From 50d982e790f57cb6e11742f2b70f3b7c56291bfe Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 5 Oct 2018 19:00:19 +0200 Subject: [PATCH 25/57] tests/qgraph: add generic virtio testcases Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 1 + tests/virtio-test.c | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 tests/virtio-test.c diff --git a/tests/Makefile.include b/tests/Makefile.include index 4a9e2e40ba..ec929ccda6 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -766,6 +766,7 @@ qos-test-obj-y += tests/libqos/x86_64_pc-machine.o qos-test-obj-y += tests/e1000e-test.o qos-test-obj-y += tests/pci-test.o qos-test-obj-y += tests/sdhci-test.o +qos-test-obj-y += tests/virtio-test.o check-unit-y += tests/test-qgraph$(EXESUF) tests/test-qgraph$(EXESUF): tests/test-qgraph.o $(libqgraph-obj-y) diff --git a/tests/virtio-test.c b/tests/virtio-test.c new file mode 100644 index 0000000000..804e5371dc --- /dev/null +++ b/tests/virtio-test.c @@ -0,0 +1,25 @@ +/* + * QTest testcase for virtio + * + * Copyright (c) 2018 Red Hat, Inc. + * + * 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 "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" + +/* Tests only initialization so far. TODO: Replace with functional tests */ +static void nop(void *obj, void *data, QGuestAllocator *alloc) +{ +} + +static void register_virtio_test(void) +{ + qos_add_test("nop", "virtio", nop, NULL); +} + +libqos_init(register_virtio_test); From b3e7dc877187073628ec090728fd1aa58ccd60c8 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Tue, 24 Jul 2018 19:40:14 +0200 Subject: [PATCH 26/57] tests/libqos: virtio-serial driver and interface nodes Add qgraph nodes for virtio-serial-pci and virtio-serial-device. Both nodes produce virtio-serial, but virtio-serial-pci receives a pci-bus and uses virtio-pci QOSGraphObject and functions, while virtio-serial-device receives a virtio-bus and implements its own functions Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 1 + tests/libqos/virtio-serial.c | 110 +++++++++++++++++++++++++++++++++++ tests/libqos/virtio-serial.h | 39 +++++++++++++ 3 files changed, 150 insertions(+) create mode 100644 tests/libqos/virtio-serial.c create mode 100644 tests/libqos/virtio-serial.h diff --git a/tests/Makefile.include b/tests/Makefile.include index ec929ccda6..2a51cabbd2 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -751,6 +751,7 @@ qos-test-obj-y += $(libqos-pc-obj-y) $(libqos-spapr-obj-y) qos-test-obj-y += tests/libqos/e1000e.o qos-test-obj-y += tests/libqos/sdhci.o qos-test-obj-y += $(libqos-virtio-obj-y) +qos-test-obj-y += tests/libqos/virtio-serial.o # Machines qos-test-obj-y += tests/libqos/aarch64-xlnx-zcu102-machine.o diff --git a/tests/libqos/virtio-serial.c b/tests/libqos/virtio-serial.c new file mode 100644 index 0000000000..91cedefb8d --- /dev/null +++ b/tests/libqos/virtio-serial.c @@ -0,0 +1,110 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/virtio-serial.h" + +static void *qvirtio_serial_get_driver(QVirtioSerial *v_serial, + const char *interface) +{ + if (!g_strcmp0(interface, "virtio-serial")) { + return v_serial; + } + if (!g_strcmp0(interface, "virtio")) { + return v_serial->vdev; + } + + fprintf(stderr, "%s not present in virtio-serial-device\n", interface); + g_assert_not_reached(); +} + +static void *qvirtio_serial_device_get_driver(void *object, + const char *interface) +{ + QVirtioSerialDevice *v_serial = object; + return qvirtio_serial_get_driver(&v_serial->serial, interface); +} + +static void *virtio_serial_device_create(void *virtio_dev, + QGuestAllocator *t_alloc, + void *addr) +{ + QVirtioSerialDevice *virtio_device = g_new0(QVirtioSerialDevice, 1); + QVirtioSerial *interface = &virtio_device->serial; + + interface->vdev = virtio_dev; + + virtio_device->obj.get_driver = qvirtio_serial_device_get_driver; + + return &virtio_device->obj; +} + +/* virtio-serial-pci */ +static void *qvirtio_serial_pci_get_driver(void *object, const char *interface) +{ + QVirtioSerialPCI *v_serial = object; + if (!g_strcmp0(interface, "pci-device")) { + return v_serial->pci_vdev.pdev; + } + return qvirtio_serial_get_driver(&v_serial->serial, interface); +} + +static void *virtio_serial_pci_create(void *pci_bus, QGuestAllocator *t_alloc, + void *addr) +{ + QVirtioSerialPCI *virtio_spci = g_new0(QVirtioSerialPCI, 1); + QVirtioSerial *interface = &virtio_spci->serial; + QOSGraphObject *obj = &virtio_spci->pci_vdev.obj; + + virtio_pci_init(&virtio_spci->pci_vdev, pci_bus, addr); + interface->vdev = &virtio_spci->pci_vdev.vdev; + + obj->get_driver = qvirtio_serial_pci_get_driver; + + return obj; +} + +static void virtio_serial_register_nodes(void) +{ + QPCIAddress addr = { + .devfn = QPCI_DEVFN(4, 0), + }; + + QOSGraphEdgeOptions edge_opts = { }; + + /* virtio-serial-device */ + edge_opts.extra_device_opts = "id=vser0"; + qos_node_create_driver("virtio-serial-device", + virtio_serial_device_create); + qos_node_consumes("virtio-serial-device", "virtio-bus", &edge_opts); + qos_node_produces("virtio-serial-device", "virtio"); + qos_node_produces("virtio-serial-device", "virtio-serial"); + + /* virtio-serial-pci */ + edge_opts.extra_device_opts = "id=vser0,addr=04.0"; + add_qpci_address(&edge_opts, &addr); + qos_node_create_driver("virtio-serial-pci", virtio_serial_pci_create); + qos_node_consumes("virtio-serial-pci", "pci-bus", &edge_opts); + qos_node_produces("virtio-serial-pci", "pci-device"); + qos_node_produces("virtio-serial-pci", "virtio"); + qos_node_produces("virtio-serial-pci", "virtio-serial"); +} + +libqos_init(virtio_serial_register_nodes); diff --git a/tests/libqos/virtio-serial.h b/tests/libqos/virtio-serial.h new file mode 100644 index 0000000000..b7e2a5d178 --- /dev/null +++ b/tests/libqos/virtio-serial.h @@ -0,0 +1,39 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "libqos/qgraph.h" +#include "libqos/virtio.h" +#include "libqos/virtio-pci.h" + +typedef struct QVirtioSerial QVirtioSerial; +typedef struct QVirtioSerialPCI QVirtioSerialPCI; +typedef struct QVirtioSerialDevice QVirtioSerialDevice; + +struct QVirtioSerial { + QVirtioDevice *vdev; +}; + +struct QVirtioSerialPCI { + QVirtioPCIDevice pci_vdev; + QVirtioSerial serial; +}; + +struct QVirtioSerialDevice { + QOSGraphObject obj; + QVirtioSerial serial; +}; From 1657291afd95f59dc3ef4c9545a7badf7dd75b35 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Wed, 18 Jul 2018 17:18:46 +0200 Subject: [PATCH 27/57] qos-test: virtio-console and virtio-serial test node Convert tests/virtio-console-test and tests/virtio-serial-test in qgraph test node. This test consumes a virtio-serial interface and checks that its function return the expected values. Note that this test does not allocate any virtio-console or virtio-serial structure, it's all done by the qtest walking graph mechanism Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 18 +++++++----------- tests/libqos/virtio.c | 1 + tests/virtio-console-test.c | 38 ------------------------------------- tests/virtio-serial-test.c | 27 ++++++++++++-------------- 4 files changed, 20 insertions(+), 64 deletions(-) delete mode 100644 tests/virtio-console-test.c diff --git a/tests/Makefile.include b/tests/Makefile.include index 2a51cabbd2..d0cd6d2f4b 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -153,18 +153,14 @@ check-qtest-generic-y += tests/cdrom-test$(EXESUF) check-qtest-ipack-y += tests/ipoctal232-test$(EXESUF) -check-qtest-virtioserial-$(CONFIG_VIRTIO_SERIAL) += tests/virtio-console-test$(EXESUF) - -check-qtest-virtio-$(CONFIG_VIRTIO_NET) += tests/virtio-net-test$(EXESUF) -check-qtest-virtio-$(CONFIG_VIRTIO_BALLOON) += tests/virtio-balloon-test$(EXESUF) -check-qtest-virtio-$(CONFIG_VIRTIO_BLK) += tests/virtio-blk-test$(EXESUF) -check-qtest-virtio-$(CONFIG_VIRTIO_RNG) += tests/virtio-rng-test$(EXESUF) -check-qtest-virtio-$(CONFIG_VIRTIO_SCSI) += tests/virtio-scsi-test$(EXESUF) +check-qtest-virtio-y += tests/virtio-net-test$(EXESUF) +check-qtest-virtio-y += tests/virtio-balloon-test$(EXESUF) +check-qtest-virtio-y += tests/virtio-blk-test$(EXESUF) +check-qtest-virtio-y += tests/virtio-rng-test$(EXESUF) +check-qtest-virtio-y += tests/virtio-scsi-test$(EXESUF) ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS)$(CONFIG_PCI),yyy) check-qtest-virtio-$(CONFIG_VIRTIO_9P) += tests/virtio-9p-test$(EXESUF) endif -check-qtest-virtio-$(CONFIG_VIRTIO_SERIAL) += tests/virtio-serial-test$(EXESUF) -check-qtest-virtio-y += $(check-qtest-virtioserial-y) check-qtest-pci-y += tests/e1000-test$(EXESUF) check-qtest-pci-$(CONFIG_RTL8139_PCI) += tests/rtl8139-test$(EXESUF) @@ -315,6 +311,7 @@ check-qtest-s390x-$(CONFIG_POSIX) += tests/test-filter-redirector$(EXESUF) check-qtest-s390x-y += tests/drive_del-test$(EXESUF) check-qtest-s390x-y += tests/device-plug-test$(EXESUF) check-qtest-s390x-y += tests/virtio-ccw-test$(EXESUF) +check-qtest-s390x-y += tests/virtio-balloon-test$(EXESUF) check-qtest-s390x-y += tests/cpu-plug-test$(EXESUF) check-qtest-s390x-y += tests/migration-test$(EXESUF) @@ -768,6 +765,7 @@ qos-test-obj-y += tests/e1000e-test.o qos-test-obj-y += tests/pci-test.o qos-test-obj-y += tests/sdhci-test.o qos-test-obj-y += tests/virtio-test.o +qos-test-obj-y += tests/virtio-serial-test.o check-unit-y += tests/test-qgraph$(EXESUF) tests/test-qgraph$(EXESUF): tests/test-qgraph.o $(libqgraph-obj-y) @@ -820,8 +818,6 @@ tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o $(libqos-pc-obj-y) $(lib tests/virtio-rng-test$(EXESUF): tests/virtio-rng-test.o $(libqos-pc-obj-y) 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 $(libqos-virtio-obj-y) -tests/virtio-console-test$(EXESUF): tests/virtio-console-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/libqos/virtio.c b/tests/libqos/virtio.c index 5a62950ccd..9fc8c7ed83 100644 --- a/tests/libqos/virtio.c +++ b/tests/libqos/virtio.c @@ -352,6 +352,7 @@ void qvirtqueue_set_used_event(QVirtQueue *vq, uint16_t idx) /* * qvirtio_get_dev_type: * Returns: the preferred virtio bus/device type for the current architecture. + * TODO: delete this */ const char *qvirtio_get_dev_type(void) { diff --git a/tests/virtio-console-test.c b/tests/virtio-console-test.c deleted file mode 100644 index a7c6f167c3..0000000000 --- a/tests/virtio-console-test.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * QTest testcase for VirtIO Console - * - * Copyright (c) 2014 SUSE LINUX Products GmbH - * - * 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 "qemu/osdep.h" -#include "libqtest.h" -#include "libqos/virtio.h" - -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void console_nop(void) -{ - global_qtest = qtest_initf("-device virtio-serial-%s,id=vser0 " - "-device virtconsole,bus=vser0.0", - qvirtio_get_dev_type()); - qtest_end(); -} - -static void serialport_nop(void) -{ - global_qtest = qtest_initf("-device virtio-serial-%s,id=vser0 " - "-device virtserialport,bus=vser0.0", - qvirtio_get_dev_type()); - qtest_end(); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - qtest_add_func("/virtio/console/nop", console_nop); - qtest_add_func("/virtio/serialport/nop", serialport_nop); - - return g_test_run(); -} diff --git a/tests/virtio-serial-test.c b/tests/virtio-serial-test.c index 8da9980a24..85f35e09b7 100644 --- a/tests/virtio-serial-test.c +++ b/tests/virtio-serial-test.c @@ -9,33 +9,30 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "libqos/virtio.h" +#include "libqos/virtio-serial.h" /* Tests only initialization so far. TODO: Replace with functional tests */ -static void virtio_serial_nop(void) +static void virtio_serial_nop(void *obj, void *data, QGuestAllocator *alloc) { + /* no operation */ } -static void hotplug(void) +static void serial_hotplug(void *obj, void *data, QGuestAllocator *alloc) { qtest_qmp_device_add("virtserialport", "hp-port", "{}"); - qtest_qmp_device_del("hp-port"); } -int main(int argc, char **argv) +static void register_virtio_serial_test(void) { - int ret; + QOSGraphTestOptions opts = { }; - g_test_init(&argc, &argv, NULL); - qtest_add_func("/virtio/serial/nop", virtio_serial_nop); - qtest_add_func("/virtio/serial/hotplug", hotplug); + opts.edge.before_cmd_line = "-device virtconsole,bus=vser0.0"; + qos_add_test("console-nop", "virtio-serial", virtio_serial_nop, &opts); - global_qtest = qtest_initf("-device virtio-serial-%s", - qvirtio_get_dev_type()); - ret = g_test_run(); + opts.edge.before_cmd_line = "-device virtserialport,bus=vser0.0"; + qos_add_test("serialport-nop", "virtio-serial", virtio_serial_nop, &opts); - qtest_end(); - - return ret; + qos_add_test("hotplug", "virtio-serial", serial_hotplug, NULL); } +libqos_init(register_virtio_serial_test); From 9d44725793700112b5a0ff71f64f7059f0a4b6c9 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Fri, 20 Jul 2018 15:41:46 +0200 Subject: [PATCH 28/57] tests/libqos: virtio-9p driver and interface nodes Add qgraph nodes for virtio-9p-pci and virtio-9p-device. Both nodes produce virtio-9p, but virtio-9p-pci receives a pci-bus and overrides virtio-pci QOSGraphObject and its functions, while virtio-9p-device receives a virtio and implements its own functions Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 1 + tests/libqos/virtio-9p.c | 173 +++++++++++++++++++++++++++++++++++++++ tests/libqos/virtio-9p.h | 42 ++++++++++ 3 files changed, 216 insertions(+) create mode 100644 tests/libqos/virtio-9p.c create mode 100644 tests/libqos/virtio-9p.h diff --git a/tests/Makefile.include b/tests/Makefile.include index d0cd6d2f4b..cb8f0c54ef 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -748,6 +748,7 @@ qos-test-obj-y += $(libqos-pc-obj-y) $(libqos-spapr-obj-y) qos-test-obj-y += tests/libqos/e1000e.o qos-test-obj-y += tests/libqos/sdhci.o qos-test-obj-y += $(libqos-virtio-obj-y) +qos-test-obj-$(CONFIG_VIRTFS) += tests/libqos/virtio-9p.o qos-test-obj-y += tests/libqos/virtio-serial.o # Machines diff --git a/tests/libqos/virtio-9p.c b/tests/libqos/virtio-9p.c new file mode 100644 index 0000000000..a378b56fa9 --- /dev/null +++ b/tests/libqos/virtio-9p.c @@ -0,0 +1,173 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "standard-headers/linux/virtio_ids.h" +#include "libqos/virtio-9p.h" +#include "libqos/qgraph.h" + +static QGuestAllocator *alloc; + +static void virtio_9p_cleanup(QVirtio9P *interface) +{ + qvirtqueue_cleanup(interface->vdev->bus, interface->vq, alloc); +} + +static void virtio_9p_setup(QVirtio9P *interface) +{ + interface->vq = qvirtqueue_setup(interface->vdev, alloc, 0); + qvirtio_set_driver_ok(interface->vdev); +} + +/* virtio-9p-device */ +static void virtio_9p_device_destructor(QOSGraphObject *obj) +{ + QVirtio9PDevice *v_9p = (QVirtio9PDevice *) obj; + QVirtio9P *v9p = &v_9p->v9p; + + virtio_9p_cleanup(v9p); +} + +static void virtio_9p_device_start_hw(QOSGraphObject *obj) +{ + QVirtio9PDevice *v_9p = (QVirtio9PDevice *) obj; + QVirtio9P *v9p = &v_9p->v9p; + + virtio_9p_setup(v9p); +} + +static void *virtio_9p_get_driver(QVirtio9P *v_9p, + const char *interface) +{ + if (!g_strcmp0(interface, "virtio-9p")) { + return v_9p; + } + if (!g_strcmp0(interface, "virtio")) { + return v_9p->vdev; + } + + fprintf(stderr, "%s not present in virtio-9p-device\n", interface); + g_assert_not_reached(); +} + +static void *virtio_9p_device_get_driver(void *object, const char *interface) +{ + QVirtio9PDevice *v_9p = object; + return virtio_9p_get_driver(&v_9p->v9p, interface); +} + +static void *virtio_9p_device_create(void *virtio_dev, + QGuestAllocator *t_alloc, + void *addr) +{ + QVirtio9PDevice *virtio_device = g_new0(QVirtio9PDevice, 1); + QVirtio9P *interface = &virtio_device->v9p; + + interface->vdev = virtio_dev; + alloc = t_alloc; + + virtio_device->obj.destructor = virtio_9p_device_destructor; + virtio_device->obj.get_driver = virtio_9p_device_get_driver; + virtio_device->obj.start_hw = virtio_9p_device_start_hw; + + return &virtio_device->obj; +} + +/* virtio-9p-pci */ +static void virtio_9p_pci_destructor(QOSGraphObject *obj) +{ + QVirtio9PPCI *v9_pci = (QVirtio9PPCI *) obj; + QVirtio9P *interface = &v9_pci->v9p; + QOSGraphObject *pci_vobj = &v9_pci->pci_vdev.obj; + + virtio_9p_cleanup(interface); + qvirtio_pci_destructor(pci_vobj); +} + +static void virtio_9p_pci_start_hw(QOSGraphObject *obj) +{ + QVirtio9PPCI *v9_pci = (QVirtio9PPCI *) obj; + QVirtio9P *interface = &v9_pci->v9p; + QOSGraphObject *pci_vobj = &v9_pci->pci_vdev.obj; + + qvirtio_pci_start_hw(pci_vobj); + virtio_9p_setup(interface); +} + +static void *virtio_9p_pci_get_driver(void *object, const char *interface) +{ + QVirtio9PPCI *v_9p = object; + if (!g_strcmp0(interface, "pci-device")) { + return v_9p->pci_vdev.pdev; + } + return virtio_9p_get_driver(&v_9p->v9p, interface); +} + +static void *virtio_9p_pci_create(void *pci_bus, QGuestAllocator *t_alloc, + void *addr) +{ + QVirtio9PPCI *v9_pci = g_new0(QVirtio9PPCI, 1); + QVirtio9P *interface = &v9_pci->v9p; + QOSGraphObject *obj = &v9_pci->pci_vdev.obj; + + virtio_pci_init(&v9_pci->pci_vdev, pci_bus, addr); + interface->vdev = &v9_pci->pci_vdev.vdev; + alloc = t_alloc; + + g_assert_cmphex(interface->vdev->device_type, ==, VIRTIO_ID_9P); + + obj->destructor = virtio_9p_pci_destructor; + obj->start_hw = virtio_9p_pci_start_hw; + obj->get_driver = virtio_9p_pci_get_driver; + + return obj; +} + +static void virtio_9p_register_nodes(void) +{ + const char *str_simple = "fsdev=fsdev0,mount_tag=" MOUNT_TAG; + const char *str_addr = "fsdev=fsdev0,addr=04.0,mount_tag=" MOUNT_TAG; + + QPCIAddress addr = { + .devfn = QPCI_DEVFN(4, 0), + }; + + QOSGraphEdgeOptions opts = { + .before_cmd_line = "-fsdev synth,id=fsdev0", + }; + + /* virtio-9p-device */ + opts.extra_device_opts = str_simple, + qos_node_create_driver("virtio-9p-device", virtio_9p_device_create); + qos_node_consumes("virtio-9p-device", "virtio-bus", &opts); + qos_node_produces("virtio-9p-device", "virtio"); + qos_node_produces("virtio-9p-device", "virtio-9p"); + + /* virtio-9p-pci */ + opts.extra_device_opts = str_addr; + add_qpci_address(&opts, &addr); + qos_node_create_driver("virtio-9p-pci", virtio_9p_pci_create); + qos_node_consumes("virtio-9p-pci", "pci-bus", &opts); + qos_node_produces("virtio-9p-pci", "pci-device"); + qos_node_produces("virtio-9p-pci", "virtio"); + qos_node_produces("virtio-9p-pci", "virtio-9p"); + +} + +libqos_init(virtio_9p_register_nodes); diff --git a/tests/libqos/virtio-9p.h b/tests/libqos/virtio-9p.h new file mode 100644 index 0000000000..dba22772b5 --- /dev/null +++ b/tests/libqos/virtio-9p.h @@ -0,0 +1,42 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "libqos/qgraph.h" +#include "libqos/virtio.h" +#include "libqos/virtio-pci.h" + +typedef struct QVirtio9P QVirtio9P; +typedef struct QVirtio9PPCI QVirtio9PPCI; +typedef struct QVirtio9PDevice QVirtio9PDevice; + +#define MOUNT_TAG "qtest" + +struct QVirtio9P { + QVirtioDevice *vdev; + QVirtQueue *vq; +}; + +struct QVirtio9PPCI { + QVirtioPCIDevice pci_vdev; + QVirtio9P v9p; +}; + +struct QVirtio9PDevice { + QOSGraphObject obj; + QVirtio9P v9p; +}; From dfbe8b43030d1b37056847b50904642f4aa9c6e1 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Fri, 20 Jul 2018 15:42:11 +0200 Subject: [PATCH 29/57] qos-test: virtio-9p test node Convert tests/virtio-9p-test into a qgraph test node. This test consumes a virtio-9p interface and checks that its functions return the expected values. Note that this test does not allocate any virtio-9p structure, it's all done by the qtest walking graph mechanism Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 5 +- tests/virtio-9p-test.c | 218 +++++++++++++++-------------------------- 2 files changed, 78 insertions(+), 145 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index cb8f0c54ef..8feca79521 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -158,9 +158,6 @@ check-qtest-virtio-y += tests/virtio-balloon-test$(EXESUF) check-qtest-virtio-y += tests/virtio-blk-test$(EXESUF) check-qtest-virtio-y += tests/virtio-rng-test$(EXESUF) check-qtest-virtio-y += tests/virtio-scsi-test$(EXESUF) -ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS)$(CONFIG_PCI),yyy) -check-qtest-virtio-$(CONFIG_VIRTIO_9P) += tests/virtio-9p-test$(EXESUF) -endif check-qtest-pci-y += tests/e1000-test$(EXESUF) check-qtest-pci-$(CONFIG_RTL8139_PCI) += tests/rtl8139-test$(EXESUF) @@ -766,6 +763,7 @@ qos-test-obj-y += tests/e1000e-test.o qos-test-obj-y += tests/pci-test.o qos-test-obj-y += tests/sdhci-test.o qos-test-obj-y += tests/virtio-test.o +qos-test-obj-$(CONFIG_VIRTFS) += tests/virtio-9p-test.o qos-test-obj-y += tests/virtio-serial-test.o check-unit-y += tests/test-qgraph$(EXESUF) @@ -818,7 +816,6 @@ tests/virtio-ccw-test$(EXESUF): tests/virtio-ccw-test.o tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o $(libqos-pc-obj-y) $(libqos-virtio-obj-y) tests/virtio-rng-test$(EXESUF): tests/virtio-rng-test.o $(libqos-pc-obj-y) 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/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-9p-test.c b/tests/virtio-9p-test.c index 8fd74f6ace..16107ad280 100644 --- a/tests/virtio-9p-test.c +++ b/tests/virtio-9p-test.c @@ -9,99 +9,36 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qemu-common.h" -#include "libqos/libqos-pc.h" -#include "libqos/libqos-spapr.h" -#include "libqos/virtio.h" -#include "libqos/virtio-pci.h" -#include "standard-headers/linux/virtio_ids.h" -#include "standard-headers/linux/virtio_pci.h" #include "hw/9pfs/9p.h" #include "hw/9pfs/9p-synth.h" +#include "libqos/virtio-9p.h" +#include "libqos/qgraph.h" #define QVIRTIO_9P_TIMEOUT_US (10 * 1000 * 1000) +static QGuestAllocator *alloc; -static const char mount_tag[] = "qtest"; - -typedef struct { - QVirtioDevice *dev; - QOSState *qs; - QVirtQueue *vq; -} QVirtIO9P; - -static QVirtIO9P *qvirtio_9p_start(const char *driver) +static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc) { - const char *arch = qtest_get_arch(); - const char *cmd = "-fsdev synth,id=fsdev0 " - "-device %s,fsdev=fsdev0,mount_tag=%s"; - QVirtIO9P *v9p = g_new0(QVirtIO9P, 1); - - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - v9p->qs = qtest_pc_boot(cmd, driver, mount_tag); - } else if (strcmp(arch, "ppc64") == 0) { - v9p->qs = qtest_spapr_boot(cmd, driver, mount_tag); - } else { - g_printerr("virtio-9p tests are only available on x86 or ppc64\n"); - exit(EXIT_FAILURE); - } - global_qtest = v9p->qs->qts; - - return v9p; -} - -static void qvirtio_9p_stop(QVirtIO9P *v9p) -{ - qtest_shutdown(v9p->qs); - g_free(v9p); -} - -static QVirtIO9P *qvirtio_9p_pci_start(void) -{ - QVirtIO9P *v9p = qvirtio_9p_start("virtio-9p-pci"); - QVirtioPCIDevice *dev = qvirtio_pci_device_find(v9p->qs->pcibus, - VIRTIO_ID_9P); - g_assert_nonnull(dev); - g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_9P); - v9p->dev = (QVirtioDevice *) dev; - - qvirtio_pci_device_enable(dev); - qvirtio_start_device(v9p->dev); - - v9p->vq = qvirtqueue_setup(v9p->dev, &v9p->qs->alloc, 0); - - qvirtio_set_driver_ok(v9p->dev); - - return v9p; -} - -static void qvirtio_9p_pci_stop(QVirtIO9P *v9p) -{ - qvirtqueue_cleanup(v9p->dev->bus, v9p->vq, &v9p->qs->alloc); - qvirtio_pci_device_disable(container_of(v9p->dev, QVirtioPCIDevice, vdev)); - qvirtio_pci_device_free((QVirtioPCIDevice *)v9p->dev); - qvirtio_9p_stop(v9p); -} - -static void pci_config(QVirtIO9P *v9p) -{ - size_t tag_len = qvirtio_config_readw(v9p->dev, 0); + QVirtio9P *v9p = obj; + alloc = t_alloc; + size_t tag_len = qvirtio_config_readw(v9p->vdev, 0); char *tag; int i; - g_assert_cmpint(tag_len, ==, strlen(mount_tag)); + g_assert_cmpint(tag_len, ==, strlen(MOUNT_TAG)); tag = g_malloc(tag_len); for (i = 0; i < tag_len; i++) { - tag[i] = qvirtio_config_readb(v9p->dev, i + 2); + tag[i] = qvirtio_config_readb(v9p->vdev, i + 2); } - g_assert_cmpmem(tag, tag_len, mount_tag, tag_len); + g_assert_cmpmem(tag, tag_len, MOUNT_TAG, tag_len); g_free(tag); } #define P9_MAX_SIZE 4096 /* Max size of a T-message or R-message */ typedef struct { - QVirtIO9P *v9p; + QVirtio9P *v9p; uint16_t tag; uint64_t t_msg; uint32_t t_size; @@ -204,7 +141,7 @@ static void v9fs_string_read(P9Req *req, uint16_t *len, char **string) uint16_t tag; } QEMU_PACKED P9Hdr; -static P9Req *v9fs_req_init(QVirtIO9P *v9p, uint32_t size, uint8_t id, +static P9Req *v9fs_req_init(QVirtio9P *v9p, uint32_t size, uint8_t id, uint16_t tag) { P9Req *req = g_new0(P9Req, 1); @@ -222,7 +159,7 @@ static P9Req *v9fs_req_init(QVirtIO9P *v9p, uint32_t size, uint8_t id, req->v9p = v9p; req->t_size = total_size; - req->t_msg = guest_alloc(&v9p->qs->alloc, req->t_size); + req->t_msg = guest_alloc(alloc, req->t_size); v9fs_memwrite(req, &hdr, 7); req->tag = tag; return req; @@ -230,13 +167,13 @@ static P9Req *v9fs_req_init(QVirtIO9P *v9p, uint32_t size, uint8_t id, static void v9fs_req_send(P9Req *req) { - QVirtIO9P *v9p = req->v9p; + QVirtio9P *v9p = req->v9p; - req->r_msg = guest_alloc(&v9p->qs->alloc, P9_MAX_SIZE); + req->r_msg = guest_alloc(alloc, P9_MAX_SIZE); req->free_head = qvirtqueue_add(v9p->vq, req->t_msg, req->t_size, false, true); qvirtqueue_add(v9p->vq, req->r_msg, P9_MAX_SIZE, true, false); - qvirtqueue_kick(v9p->dev, v9p->vq, req->free_head); + qvirtqueue_kick(v9p->vdev, v9p->vq, req->free_head); req->t_off = 0; } @@ -255,9 +192,9 @@ static const char *rmessage_name(uint8_t id) static void v9fs_req_wait_for_reply(P9Req *req, uint32_t *len) { - QVirtIO9P *v9p = req->v9p; + QVirtio9P *v9p = req->v9p; - qvirtio_wait_used_elem(v9p->dev, v9p->vq, req->free_head, len, + qvirtio_wait_used_elem(v9p->vdev, v9p->vq, req->free_head, len, QVIRTIO_9P_TIMEOUT_US); } @@ -288,10 +225,8 @@ static void v9fs_req_recv(P9Req *req, uint8_t id) static void v9fs_req_free(P9Req *req) { - QVirtIO9P *v9p = req->v9p; - - guest_free(&v9p->qs->alloc, req->t_msg); - guest_free(&v9p->qs->alloc, req->r_msg); + guest_free(alloc, req->t_msg); + guest_free(alloc, req->r_msg); g_free(req); } @@ -304,7 +239,7 @@ static void v9fs_rlerror(P9Req *req, uint32_t *err) } /* size[4] Tversion tag[2] msize[4] version[s] */ -static P9Req *v9fs_tversion(QVirtIO9P *v9p, uint32_t msize, const char *version, +static P9Req *v9fs_tversion(QVirtio9P *v9p, uint32_t msize, const char *version, uint16_t tag) { P9Req *req; @@ -339,7 +274,7 @@ static void v9fs_rversion(P9Req *req, uint16_t *len, char **version) } /* size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s] n_uname[4] */ -static P9Req *v9fs_tattach(QVirtIO9P *v9p, uint32_t fid, uint32_t n_uname, +static P9Req *v9fs_tattach(QVirtio9P *v9p, uint32_t fid, uint32_t n_uname, uint16_t tag) { const char *uname = ""; /* ignored by QEMU */ @@ -368,7 +303,7 @@ static void v9fs_rattach(P9Req *req, v9fs_qid *qid) } /* size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s]) */ -static P9Req *v9fs_twalk(QVirtIO9P *v9p, uint32_t fid, uint32_t newfid, +static P9Req *v9fs_twalk(QVirtio9P *v9p, uint32_t fid, uint32_t newfid, uint16_t nwname, char *const wnames[], uint16_t tag) { P9Req *req; @@ -410,7 +345,7 @@ static void v9fs_rwalk(P9Req *req, uint16_t *nwqid, v9fs_qid **wqid) } /* size[4] Tlopen tag[2] fid[4] flags[4] */ -static P9Req *v9fs_tlopen(QVirtIO9P *v9p, uint32_t fid, uint32_t flags, +static P9Req *v9fs_tlopen(QVirtio9P *v9p, uint32_t fid, uint32_t flags, uint16_t tag) { P9Req *req; @@ -438,7 +373,7 @@ static void v9fs_rlopen(P9Req *req, v9fs_qid *qid, uint32_t *iounit) } /* size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count] */ -static P9Req *v9fs_twrite(QVirtIO9P *v9p, uint32_t fid, uint64_t offset, +static P9Req *v9fs_twrite(QVirtio9P *v9p, uint32_t fid, uint64_t offset, uint32_t count, const void *data, uint16_t tag) { P9Req *req; @@ -466,7 +401,7 @@ static void v9fs_rwrite(P9Req *req, uint32_t *count) } /* size[4] Tflush tag[2] oldtag[2] */ -static P9Req *v9fs_tflush(QVirtIO9P *v9p, uint16_t oldtag, uint16_t tag) +static P9Req *v9fs_tflush(QVirtio9P *v9p, uint16_t oldtag, uint16_t tag) { P9Req *req; @@ -483,8 +418,10 @@ static void v9fs_rflush(P9Req *req) v9fs_req_free(req); } -static void fs_version(QVirtIO9P *v9p) +static void fs_version(void *obj, void *data, QGuestAllocator *t_alloc) { + QVirtio9P *v9p = obj; + alloc = t_alloc; const char *version = "9P2000.L"; uint16_t server_len; char *server_version; @@ -499,18 +436,22 @@ static void fs_version(QVirtIO9P *v9p) g_free(server_version); } -static void fs_attach(QVirtIO9P *v9p) +static void fs_attach(void *obj, void *data, QGuestAllocator *t_alloc) { + QVirtio9P *v9p = obj; + alloc = t_alloc; P9Req *req; - fs_version(v9p); + fs_version(v9p, NULL, t_alloc); req = v9fs_tattach(v9p, 0, getuid(), 0); v9fs_req_wait_for_reply(req, NULL); v9fs_rattach(req, NULL); } -static void fs_walk(QVirtIO9P *v9p) +static void fs_walk(void *obj, void *data, QGuestAllocator *t_alloc) { + QVirtio9P *v9p = obj; + alloc = t_alloc; char *wnames[P9_MAXWELEM]; uint16_t nwqid; v9fs_qid *wqid; @@ -521,7 +462,7 @@ static void fs_walk(QVirtIO9P *v9p) wnames[i] = g_strdup_printf(QTEST_V9FS_SYNTH_WALK_FILE, i); } - fs_attach(v9p); + fs_attach(v9p, NULL, t_alloc); req = v9fs_twalk(v9p, 0, 1, P9_MAXWELEM, wnames, 0); v9fs_req_wait_for_reply(req, NULL); v9fs_rwalk(req, &nwqid, &wqid); @@ -535,13 +476,15 @@ static void fs_walk(QVirtIO9P *v9p) g_free(wqid); } -static void fs_walk_no_slash(QVirtIO9P *v9p) +static void fs_walk_no_slash(void *obj, void *data, QGuestAllocator *t_alloc) { + QVirtio9P *v9p = obj; + alloc = t_alloc; char *const wnames[] = { g_strdup(" /") }; P9Req *req; uint32_t err; - fs_attach(v9p); + fs_attach(v9p, NULL, t_alloc); req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); v9fs_req_wait_for_reply(req, NULL); v9fs_rlerror(req, &err); @@ -551,13 +494,15 @@ static void fs_walk_no_slash(QVirtIO9P *v9p) g_free(wnames[0]); } -static void fs_walk_dotdot(QVirtIO9P *v9p) +static void fs_walk_dotdot(void *obj, void *data, QGuestAllocator *t_alloc) { + QVirtio9P *v9p = obj; + alloc = t_alloc; char *const wnames[] = { g_strdup("..") }; v9fs_qid root_qid, *wqid; P9Req *req; - fs_version(v9p); + fs_version(v9p, NULL, t_alloc); req = v9fs_tattach(v9p, 0, getuid(), 0); v9fs_req_wait_for_reply(req, NULL); v9fs_rattach(req, &root_qid); @@ -572,12 +517,14 @@ static void fs_walk_dotdot(QVirtIO9P *v9p) g_free(wnames[0]); } -static void fs_lopen(QVirtIO9P *v9p) +static void fs_lopen(void *obj, void *data, QGuestAllocator *t_alloc) { + QVirtio9P *v9p = obj; + alloc = t_alloc; char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_LOPEN_FILE) }; P9Req *req; - fs_attach(v9p); + fs_attach(v9p, NULL, t_alloc); req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); v9fs_req_wait_for_reply(req, NULL); v9fs_rwalk(req, NULL, NULL); @@ -589,15 +536,17 @@ static void fs_lopen(QVirtIO9P *v9p) g_free(wnames[0]); } -static void fs_write(QVirtIO9P *v9p) +static void fs_write(void *obj, void *data, QGuestAllocator *t_alloc) { + QVirtio9P *v9p = obj; + alloc = t_alloc; static const uint32_t write_count = P9_MAX_SIZE / 2; char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_WRITE_FILE) }; char *buf = g_malloc0(write_count); uint32_t count; P9Req *req; - fs_attach(v9p); + fs_attach(v9p, NULL, t_alloc); req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); v9fs_req_wait_for_reply(req, NULL); v9fs_rwalk(req, NULL, NULL); @@ -615,14 +564,16 @@ static void fs_write(QVirtIO9P *v9p) g_free(wnames[0]); } -static void fs_flush_success(QVirtIO9P *v9p) +static void fs_flush_success(void *obj, void *data, QGuestAllocator *t_alloc) { + QVirtio9P *v9p = obj; + alloc = t_alloc; char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) }; P9Req *req, *flush_req; uint32_t reply_len; uint8_t should_block; - fs_attach(v9p); + fs_attach(v9p, NULL, t_alloc); req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); v9fs_req_wait_for_reply(req, NULL); v9fs_rwalk(req, NULL, NULL); @@ -650,14 +601,16 @@ static void fs_flush_success(QVirtIO9P *v9p) g_free(wnames[0]); } -static void fs_flush_ignored(QVirtIO9P *v9p) +static void fs_flush_ignored(void *obj, void *data, QGuestAllocator *t_alloc) { + QVirtio9P *v9p = obj; + alloc = t_alloc; char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) }; P9Req *req, *flush_req; uint32_t count; uint8_t should_block; - fs_attach(v9p); + fs_attach(v9p, NULL, t_alloc); req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); v9fs_req_wait_for_reply(req, NULL); v9fs_rwalk(req, NULL, NULL); @@ -685,39 +638,22 @@ static void fs_flush_ignored(QVirtIO9P *v9p) g_free(wnames[0]); } -typedef void (*v9fs_test_fn)(QVirtIO9P *v9p); - -static void v9fs_run_pci_test(gconstpointer data) +static void register_virtio_9p_test(void) { - v9fs_test_fn fn = data; - QVirtIO9P *v9p = qvirtio_9p_pci_start(); - - if (fn) { - fn(v9p); - } - qvirtio_9p_pci_stop(v9p); + qos_add_test("config", "virtio-9p", pci_config, NULL); + qos_add_test("fs/version/basic", "virtio-9p", fs_version, NULL); + qos_add_test("fs/attach/basic", "virtio-9p", fs_attach, NULL); + qos_add_test("fs/walk/basic", "virtio-9p", fs_walk, NULL); + qos_add_test("fs/walk/no_slash", "virtio-9p", fs_walk_no_slash, + NULL); + qos_add_test("fs/walk/dotdot_from_root", "virtio-9p", + fs_walk_dotdot, NULL); + qos_add_test("fs/lopen/basic", "virtio-9p", fs_lopen, NULL); + qos_add_test("fs/write/basic", "virtio-9p", fs_write, NULL); + qos_add_test("fs/flush/success", "virtio-9p", fs_flush_success, + NULL); + qos_add_test("fs/flush/ignored", "virtio-9p", fs_flush_ignored, + NULL); } -static void v9fs_qtest_pci_add(const char *path, v9fs_test_fn fn) -{ - qtest_add_data_func(path, fn, v9fs_run_pci_test); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - v9fs_qtest_pci_add("/virtio/9p/pci/nop", NULL); - v9fs_qtest_pci_add("/virtio/9p/pci/config", pci_config); - v9fs_qtest_pci_add("/virtio/9p/pci/fs/version/basic", fs_version); - v9fs_qtest_pci_add("/virtio/9p/pci/fs/attach/basic", fs_attach); - v9fs_qtest_pci_add("/virtio/9p/pci/fs/walk/basic", fs_walk); - v9fs_qtest_pci_add("/virtio/9p/pci/fs/walk/no_slash", fs_walk_no_slash); - v9fs_qtest_pci_add("/virtio/9p/pci/fs/walk/dotdot_from_root", - fs_walk_dotdot); - v9fs_qtest_pci_add("/virtio/9p/pci/fs/lopen/basic", fs_lopen); - v9fs_qtest_pci_add("/virtio/9p/pci/fs/write/basic", fs_write); - v9fs_qtest_pci_add("/virtio/9p/pci/fs/flush/success", fs_flush_success); - v9fs_qtest_pci_add("/virtio/9p/pci/fs/flush/ignored", fs_flush_ignored); - - return g_test_run(); -} +libqos_init(register_virtio_9p_test); From 292410679b856e26665b3f734c0690379f3eb944 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Wed, 25 Jul 2018 23:39:13 +0200 Subject: [PATCH 30/57] tests/libqos: virtio-balloon driver and interface nodes Add qgraph nodes for virtio-balloon-pci and virtio-balloon-device. Both nodes produce virtio-balloon, but virtio-balloon-pci receives a pci-bus and uses virtio-pci QOSGraphObject and functions, while virtio-balloon-device receives a virtio and implements its own functions Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 1 + tests/libqos/virtio-balloon.c | 113 ++++++++++++++++++++++++++++++++++ tests/libqos/virtio-balloon.h | 39 ++++++++++++ 3 files changed, 153 insertions(+) create mode 100644 tests/libqos/virtio-balloon.c create mode 100644 tests/libqos/virtio-balloon.h diff --git a/tests/Makefile.include b/tests/Makefile.include index 8feca79521..53794cde02 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -746,6 +746,7 @@ qos-test-obj-y += tests/libqos/e1000e.o qos-test-obj-y += tests/libqos/sdhci.o qos-test-obj-y += $(libqos-virtio-obj-y) qos-test-obj-$(CONFIG_VIRTFS) += tests/libqos/virtio-9p.o +qos-test-obj-y += tests/libqos/virtio-balloon.o qos-test-obj-y += tests/libqos/virtio-serial.o # Machines diff --git a/tests/libqos/virtio-balloon.c b/tests/libqos/virtio-balloon.c new file mode 100644 index 0000000000..7e6e9e9de5 --- /dev/null +++ b/tests/libqos/virtio-balloon.c @@ -0,0 +1,113 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/virtio-balloon.h" + +/* virtio-balloon-device */ +static void *qvirtio_balloon_get_driver(QVirtioBalloon *v_balloon, + const char *interface) +{ + if (!g_strcmp0(interface, "virtio-balloon")) { + return v_balloon; + } + if (!g_strcmp0(interface, "virtio")) { + return v_balloon->vdev; + } + + fprintf(stderr, "%s not present in virtio-balloon-device\n", interface); + g_assert_not_reached(); +} + +static void *qvirtio_balloon_device_get_driver(void *object, + const char *interface) +{ + QVirtioBalloonDevice *v_balloon = object; + return qvirtio_balloon_get_driver(&v_balloon->balloon, interface); +} + +static void *virtio_balloon_device_create(void *virtio_dev, + QGuestAllocator *t_alloc, + void *addr) +{ + QVirtioBalloonDevice *virtio_bdevice = g_new0(QVirtioBalloonDevice, 1); + QVirtioBalloon *interface = &virtio_bdevice->balloon; + + interface->vdev = virtio_dev; + + virtio_bdevice->obj.get_driver = qvirtio_balloon_device_get_driver; + + return &virtio_bdevice->obj; +} + +/* virtio-balloon-pci */ +static void *qvirtio_balloon_pci_get_driver(void *object, + const char *interface) +{ + QVirtioBalloonPCI *v_balloon = object; + if (!g_strcmp0(interface, "pci-device")) { + return v_balloon->pci_vdev.pdev; + } + return qvirtio_balloon_get_driver(&v_balloon->balloon, interface); +} + +static void *virtio_balloon_pci_create(void *pci_bus, QGuestAllocator *t_alloc, + void *addr) +{ + QVirtioBalloonPCI *virtio_bpci = g_new0(QVirtioBalloonPCI, 1); + QVirtioBalloon *interface = &virtio_bpci->balloon; + QOSGraphObject *obj = &virtio_bpci->pci_vdev.obj; + + + virtio_pci_init(&virtio_bpci->pci_vdev, pci_bus, addr); + interface->vdev = &virtio_bpci->pci_vdev.vdev; + + obj->get_driver = qvirtio_balloon_pci_get_driver; + + return obj; +} + +static void virtio_balloon_register_nodes(void) +{ + QPCIAddress addr = { + .devfn = QPCI_DEVFN(4, 0), + }; + + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + + /* virtio-balloon-device */ + qos_node_create_driver("virtio-balloon-device", + virtio_balloon_device_create); + qos_node_consumes("virtio-balloon-device", "virtio-bus", NULL); + qos_node_produces("virtio-balloon-device", "virtio"); + qos_node_produces("virtio-balloon-device", "virtio-balloon"); + + /* virtio-balloon-pci */ + add_qpci_address(&opts, &addr); + qos_node_create_driver("virtio-balloon-pci", virtio_balloon_pci_create); + qos_node_consumes("virtio-balloon-pci", "pci-bus", &opts); + qos_node_produces("virtio-balloon-pci", "pci-device"); + qos_node_produces("virtio-balloon-pci", "virtio"); + qos_node_produces("virtio-balloon-pci", "virtio-balloon"); +} + +libqos_init(virtio_balloon_register_nodes); diff --git a/tests/libqos/virtio-balloon.h b/tests/libqos/virtio-balloon.h new file mode 100644 index 0000000000..e8066c42bb --- /dev/null +++ b/tests/libqos/virtio-balloon.h @@ -0,0 +1,39 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "libqos/qgraph.h" +#include "libqos/virtio.h" +#include "libqos/virtio-pci.h" + +typedef struct QVirtioBalloon QVirtioBalloon; +typedef struct QVirtioBalloonPCI QVirtioBalloonPCI; +typedef struct QVirtioBalloonDevice QVirtioBalloonDevice; + +struct QVirtioBalloon { + QVirtioDevice *vdev; +}; + +struct QVirtioBalloonPCI { + QVirtioPCIDevice pci_vdev; + QVirtioBalloon balloon; +}; + +struct QVirtioBalloonDevice { + QOSGraphObject obj; + QVirtioBalloon balloon; +}; From 6975bf399a00991d84c8bd84847f5f6ee71b10e7 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Wed, 25 Jul 2018 23:39:36 +0200 Subject: [PATCH 31/57] tests/qgraph: remove virtio-balloon-test The tests/virtio-balloon-test is covered by generic virtio tests, so remove it. Signed-off-by: Paolo Bonzini Signed-off-by: Emanuele Giuseppe Esposito --- tests/Makefile.include | 3 --- tests/virtio-balloon-test.c | 33 --------------------------------- 2 files changed, 36 deletions(-) delete mode 100644 tests/virtio-balloon-test.c diff --git a/tests/Makefile.include b/tests/Makefile.include index 53794cde02..f53b7186d2 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -154,7 +154,6 @@ check-qtest-generic-y += tests/cdrom-test$(EXESUF) check-qtest-ipack-y += tests/ipoctal232-test$(EXESUF) check-qtest-virtio-y += tests/virtio-net-test$(EXESUF) -check-qtest-virtio-y += tests/virtio-balloon-test$(EXESUF) check-qtest-virtio-y += tests/virtio-blk-test$(EXESUF) check-qtest-virtio-y += tests/virtio-rng-test$(EXESUF) check-qtest-virtio-y += tests/virtio-scsi-test$(EXESUF) @@ -308,7 +307,6 @@ check-qtest-s390x-$(CONFIG_POSIX) += tests/test-filter-redirector$(EXESUF) check-qtest-s390x-y += tests/drive_del-test$(EXESUF) check-qtest-s390x-y += tests/device-plug-test$(EXESUF) check-qtest-s390x-y += tests/virtio-ccw-test$(EXESUF) -check-qtest-s390x-y += tests/virtio-balloon-test$(EXESUF) check-qtest-s390x-y += tests/cpu-plug-test$(EXESUF) check-qtest-s390x-y += tests/migration-test$(EXESUF) @@ -811,7 +809,6 @@ tests/vmxnet3-test$(EXESUF): tests/vmxnet3-test.o tests/ne2000-test$(EXESUF): tests/ne2000-test.o tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y) -tests/virtio-balloon-test$(EXESUF): tests/virtio-balloon-test.o $(libqos-virtio-obj-y) tests/virtio-blk-test$(EXESUF): tests/virtio-blk-test.o $(libqos-virtio-obj-y) tests/virtio-ccw-test$(EXESUF): tests/virtio-ccw-test.o tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o $(libqos-pc-obj-y) $(libqos-virtio-obj-y) diff --git a/tests/virtio-balloon-test.c b/tests/virtio-balloon-test.c deleted file mode 100644 index 5a1d0ccbb7..0000000000 --- a/tests/virtio-balloon-test.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - * QTest testcase for VirtIO Balloon - * - * Copyright (c) 2014 SUSE LINUX Products GmbH - * - * 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 "qemu/osdep.h" -#include "libqtest.h" -#include "libqos/virtio.h" - -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void balloon_nop(void) -{ -} - -int main(int argc, char **argv) -{ - int ret; - - g_test_init(&argc, &argv, NULL); - qtest_add_func("/virtio/balloon/nop", balloon_nop); - - global_qtest = qtest_initf("-device virtio-balloon-%s", - qvirtio_get_dev_type()); - ret = g_test_run(); - - qtest_end(); - - return ret; -} From d3d400d3df0146575a8af44382978f404d57def9 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Wed, 25 Jul 2018 23:55:38 +0200 Subject: [PATCH 32/57] tests/libqos: virtio-rng driver and interface nodes Add qgraph nodes for virtio-rng-pci and virtio-rng-device. Both nodes produce virtio-rng, but virtio-rng-pci receives a pci-bus and uses virtio-pci QOSGraphObject and functions, while virtio-rng-device receives a virtio and implements its own functions Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 1 + tests/libqos/virtio-rng.c | 110 ++++++++++++++++++++++++++++++++++++++ tests/libqos/virtio-rng.h | 39 ++++++++++++++ 3 files changed, 150 insertions(+) create mode 100644 tests/libqos/virtio-rng.c create mode 100644 tests/libqos/virtio-rng.h diff --git a/tests/Makefile.include b/tests/Makefile.include index f53b7186d2..2d2817e7dc 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -745,6 +745,7 @@ qos-test-obj-y += tests/libqos/sdhci.o qos-test-obj-y += $(libqos-virtio-obj-y) qos-test-obj-$(CONFIG_VIRTFS) += tests/libqos/virtio-9p.o qos-test-obj-y += tests/libqos/virtio-balloon.o +qos-test-obj-y += tests/libqos/virtio-rng.o qos-test-obj-y += tests/libqos/virtio-serial.o # Machines diff --git a/tests/libqos/virtio-rng.c b/tests/libqos/virtio-rng.c new file mode 100644 index 0000000000..a1d2c7671c --- /dev/null +++ b/tests/libqos/virtio-rng.c @@ -0,0 +1,110 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/virtio-rng.h" + +/* virtio-rng-device */ +static void *qvirtio_rng_get_driver(QVirtioRng *v_rng, + const char *interface) +{ + if (!g_strcmp0(interface, "virtio-rng")) { + return v_rng; + } + if (!g_strcmp0(interface, "virtio")) { + return v_rng->vdev; + } + + fprintf(stderr, "%s not present in virtio-rng-device\n", interface); + g_assert_not_reached(); +} + +static void *qvirtio_rng_device_get_driver(void *object, + const char *interface) +{ + QVirtioRngDevice *v_rng = object; + return qvirtio_rng_get_driver(&v_rng->rng, interface); +} + +static void *virtio_rng_device_create(void *virtio_dev, + QGuestAllocator *t_alloc, + void *addr) +{ + QVirtioRngDevice *virtio_rdevice = g_new0(QVirtioRngDevice, 1); + QVirtioRng *interface = &virtio_rdevice->rng; + + interface->vdev = virtio_dev; + + virtio_rdevice->obj.get_driver = qvirtio_rng_device_get_driver; + + return &virtio_rdevice->obj; +} + +/* virtio-rng-pci */ +static void *qvirtio_rng_pci_get_driver(void *object, const char *interface) +{ + QVirtioRngPCI *v_rng = object; + if (!g_strcmp0(interface, "pci-device")) { + return v_rng->pci_vdev.pdev; + } + return qvirtio_rng_get_driver(&v_rng->rng, interface); +} + +static void *virtio_rng_pci_create(void *pci_bus, QGuestAllocator *t_alloc, + void *addr) +{ + QVirtioRngPCI *virtio_rpci = g_new0(QVirtioRngPCI, 1); + QVirtioRng *interface = &virtio_rpci->rng; + QOSGraphObject *obj = &virtio_rpci->pci_vdev.obj; + + virtio_pci_init(&virtio_rpci->pci_vdev, pci_bus, addr); + interface->vdev = &virtio_rpci->pci_vdev.vdev; + + obj->get_driver = qvirtio_rng_pci_get_driver; + + return obj; +} + +static void virtio_rng_register_nodes(void) +{ + QPCIAddress addr = { + .devfn = QPCI_DEVFN(4, 0), + }; + + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + + /* virtio-rng-device */ + qos_node_create_driver("virtio-rng-device", virtio_rng_device_create); + qos_node_consumes("virtio-rng-device", "virtio-bus", NULL); + qos_node_produces("virtio-rng-device", "virtio"); + qos_node_produces("virtio-rng-device", "virtio-rng"); + + /* virtio-rng-pci */ + add_qpci_address(&opts, &addr); + qos_node_create_driver("virtio-rng-pci", virtio_rng_pci_create); + qos_node_consumes("virtio-rng-pci", "pci-bus", &opts); + qos_node_produces("virtio-rng-pci", "pci-device"); + qos_node_produces("virtio-rng-pci", "virtio"); + qos_node_produces("virtio-rng-pci", "virtio-rng"); +} + +libqos_init(virtio_rng_register_nodes); diff --git a/tests/libqos/virtio-rng.h b/tests/libqos/virtio-rng.h new file mode 100644 index 0000000000..fbba988875 --- /dev/null +++ b/tests/libqos/virtio-rng.h @@ -0,0 +1,39 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "libqos/qgraph.h" +#include "libqos/virtio.h" +#include "libqos/virtio-pci.h" + +typedef struct QVirtioRng QVirtioRng; +typedef struct QVirtioRngPCI QVirtioRngPCI; +typedef struct QVirtioRngDevice QVirtioRngDevice; + +struct QVirtioRng { + QVirtioDevice *vdev; +}; + +struct QVirtioRngPCI { + QVirtioPCIDevice pci_vdev; + QVirtioRng rng; +}; + +struct QVirtioRngDevice { + QOSGraphObject obj; + QVirtioRng rng; +}; From a25f56aebec807c9c8f9c7c8c13d59ff4431773a Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Thu, 26 Jul 2018 11:24:50 +0200 Subject: [PATCH 33/57] qos-test: virtio-rng test node Convert tests/virtio-rng-test in qgraph test node, virtio-rng-test. This test consumes a virtio-rng interface and checks that its function return the expected values. Some functions are implemented only for virtio-rng-pci, so they don't consume virtio-rng, but virtio-rng-pci Note that this test does not allocate any virtio-rng structure, it's all done by the qtest walking graph mechanism Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 3 +-- tests/virtio-rng-test.c | 27 +++++++-------------------- 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index 2d2817e7dc..ebf2212286 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -155,7 +155,6 @@ check-qtest-ipack-y += tests/ipoctal232-test$(EXESUF) check-qtest-virtio-y += tests/virtio-net-test$(EXESUF) check-qtest-virtio-y += tests/virtio-blk-test$(EXESUF) -check-qtest-virtio-y += tests/virtio-rng-test$(EXESUF) check-qtest-virtio-y += tests/virtio-scsi-test$(EXESUF) check-qtest-pci-y += tests/e1000-test$(EXESUF) @@ -764,6 +763,7 @@ qos-test-obj-y += tests/pci-test.o qos-test-obj-y += tests/sdhci-test.o qos-test-obj-y += tests/virtio-test.o qos-test-obj-$(CONFIG_VIRTFS) += tests/virtio-9p-test.o +qos-test-obj-y += tests/virtio-rng-test.o qos-test-obj-y += tests/virtio-serial-test.o check-unit-y += tests/test-qgraph$(EXESUF) @@ -813,7 +813,6 @@ tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y) tests/virtio-blk-test$(EXESUF): tests/virtio-blk-test.o $(libqos-virtio-obj-y) tests/virtio-ccw-test$(EXESUF): tests/virtio-ccw-test.o tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o $(libqos-pc-obj-y) $(libqos-virtio-obj-y) -tests/virtio-rng-test$(EXESUF): tests/virtio-rng-test.o $(libqos-pc-obj-y) tests/virtio-scsi-test$(EXESUF): tests/virtio-scsi-test.o $(libqos-virtio-obj-y) tests/tpci200-test$(EXESUF): tests/tpci200-test.o tests/display-vga-test$(EXESUF): tests/display-vga-test.o diff --git a/tests/virtio-rng-test.c b/tests/virtio-rng-test.c index 657d9a4105..5309c7c8ab 100644 --- a/tests/virtio-rng-test.c +++ b/tests/virtio-rng-test.c @@ -9,16 +9,12 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "libqos/pci.h" +#include "libqos/qgraph.h" +#include "libqos/virtio-rng.h" #define PCI_SLOT_HP 0x06 -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void pci_nop(void) -{ -} - -static void hotplug(void) +static void rng_hotplug(void *obj, void *data, QGuestAllocator *alloc) { const char *arch = qtest_get_arch(); @@ -30,18 +26,9 @@ static void hotplug(void) } } -int main(int argc, char **argv) +static void register_virtio_rng_test(void) { - int ret; - - g_test_init(&argc, &argv, NULL); - qtest_add_func("/virtio/rng/pci/nop", pci_nop); - qtest_add_func("/virtio/rng/pci/hotplug", hotplug); - - qtest_start("-device virtio-rng-pci"); - ret = g_test_run(); - - qtest_end(); - - return ret; + qos_add_test("hotplug", "virtio-rng-pci", rng_hotplug, NULL); } + +libqos_init(register_virtio_rng_test); From a3713493036eca53b295b03c26e6b4bd17083b99 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Thu, 26 Jul 2018 11:25:02 +0200 Subject: [PATCH 34/57] tests/libqos: virtio-blk driver and interface nodes Add qgraph nodes for virtio-blk-pci and virtio-blk-device. Both nodes produce virtio-blk, but virtio-blk-pci receives a pci-bus and uses virtio-pci QOSGraphObject and functions, while virtio-blk-device receives a virtio and implements its own functions Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 1 + tests/libqos/virtio-blk.c | 124 ++++++++++++++++++++++++++++++++++++++ tests/libqos/virtio-blk.h | 40 ++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 tests/libqos/virtio-blk.c create mode 100644 tests/libqos/virtio-blk.h diff --git a/tests/Makefile.include b/tests/Makefile.include index ebf2212286..5ef1b9b1cb 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -744,6 +744,7 @@ qos-test-obj-y += tests/libqos/sdhci.o qos-test-obj-y += $(libqos-virtio-obj-y) qos-test-obj-$(CONFIG_VIRTFS) += tests/libqos/virtio-9p.o qos-test-obj-y += tests/libqos/virtio-balloon.o +qos-test-obj-y += tests/libqos/virtio-blk.o qos-test-obj-y += tests/libqos/virtio-rng.o qos-test-obj-y += tests/libqos/virtio-serial.o diff --git a/tests/libqos/virtio-blk.c b/tests/libqos/virtio-blk.c new file mode 100644 index 0000000000..c17bdf4217 --- /dev/null +++ b/tests/libqos/virtio-blk.c @@ -0,0 +1,124 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "standard-headers/linux/virtio_blk.h" +#include "libqos/qgraph.h" +#include "libqos/virtio-blk.h" + +#define PCI_SLOT 0x04 +#define PCI_FN 0x00 + +/* virtio-blk-device */ +static void *qvirtio_blk_get_driver(QVirtioBlk *v_blk, + const char *interface) +{ + if (!g_strcmp0(interface, "virtio-blk")) { + return v_blk; + } + if (!g_strcmp0(interface, "virtio")) { + return v_blk->vdev; + } + + fprintf(stderr, "%s not present in virtio-blk-device\n", interface); + g_assert_not_reached(); +} + +static void *qvirtio_blk_device_get_driver(void *object, + const char *interface) +{ + QVirtioBlkDevice *v_blk = object; + return qvirtio_blk_get_driver(&v_blk->blk, interface); +} + +static void *virtio_blk_device_create(void *virtio_dev, + QGuestAllocator *t_alloc, + void *addr) +{ + QVirtioBlkDevice *virtio_blk = g_new0(QVirtioBlkDevice, 1); + QVirtioBlk *interface = &virtio_blk->blk; + + interface->vdev = virtio_dev; + + virtio_blk->obj.get_driver = qvirtio_blk_device_get_driver; + + return &virtio_blk->obj; +} + +/* virtio-blk-pci */ +static void *qvirtio_blk_pci_get_driver(void *object, const char *interface) +{ + QVirtioBlkPCI *v_blk = object; + if (!g_strcmp0(interface, "pci-device")) { + return v_blk->pci_vdev.pdev; + } + return qvirtio_blk_get_driver(&v_blk->blk, interface); +} + +static void *virtio_blk_pci_create(void *pci_bus, QGuestAllocator *t_alloc, + void *addr) +{ + QVirtioBlkPCI *virtio_blk = g_new0(QVirtioBlkPCI, 1); + QVirtioBlk *interface = &virtio_blk->blk; + QOSGraphObject *obj = &virtio_blk->pci_vdev.obj; + + virtio_pci_init(&virtio_blk->pci_vdev, pci_bus, addr); + interface->vdev = &virtio_blk->pci_vdev.vdev; + + g_assert_cmphex(interface->vdev->device_type, ==, VIRTIO_ID_BLOCK); + + obj->get_driver = qvirtio_blk_pci_get_driver; + + return obj; +} + +static void virtio_blk_register_nodes(void) +{ + /* FIXME: every test using these two nodes needs to setup a + * -drive,id=drive0 otherwise QEMU is not going to start. + * Therefore, we do not include "produces" edge for virtio + * and pci-device yet. + */ + + char *arg = g_strdup_printf("id=drv0,drive=drive0,addr=%x.%x", + PCI_SLOT, PCI_FN); + + QPCIAddress addr = { + .devfn = QPCI_DEVFN(PCI_SLOT, PCI_FN), + }; + + QOSGraphEdgeOptions opts = { }; + + /* virtio-blk-device */ + opts.extra_device_opts = "drive=drive0"; + qos_node_create_driver("virtio-blk-device", virtio_blk_device_create); + qos_node_consumes("virtio-blk-device", "virtio-bus", &opts); + qos_node_produces("virtio-blk-device", "virtio-blk"); + + /* virtio-blk-pci */ + opts.extra_device_opts = arg; + add_qpci_address(&opts, &addr); + qos_node_create_driver("virtio-blk-pci", virtio_blk_pci_create); + qos_node_consumes("virtio-blk-pci", "pci-bus", &opts); + qos_node_produces("virtio-blk-pci", "virtio-blk"); + + g_free(arg); +} + +libqos_init(virtio_blk_register_nodes); diff --git a/tests/libqos/virtio-blk.h b/tests/libqos/virtio-blk.h new file mode 100644 index 0000000000..dc258496ba --- /dev/null +++ b/tests/libqos/virtio-blk.h @@ -0,0 +1,40 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "libqos/qgraph.h" +#include "libqos/virtio.h" +#include "libqos/virtio-pci.h" + +typedef struct QVirtioBlk QVirtioBlk; +typedef struct QVirtioBlkPCI QVirtioBlkPCI; +typedef struct QVirtioBlkDevice QVirtioBlkDevice; + +/* virtqueue is created in each test */ +struct QVirtioBlk { + QVirtioDevice *vdev; +}; + +struct QVirtioBlkPCI { + QVirtioPCIDevice pci_vdev; + QVirtioBlk blk; +}; + +struct QVirtioBlkDevice { + QOSGraphObject obj; + QVirtioBlk blk; +}; From 5cb8f0db0a5eff0206376f2f344cf73fb7d2cf5f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 29 Nov 2018 12:51:08 +0100 Subject: [PATCH 35/57] qos-test: virtio-blk test node Convert tests/virtio-blk-test in qgraph test node, virtio-blk-test. This test consumes a virtio-blk interface and checks that its function return the expected values. Some functions are implemented only for virtio-blk-pci, so they don't consume virtio-blk, but virtio-blk-pci Note that this test does not allocate any virtio-blk structure, it's all done by the qtest walking graph mechanism. The allocator is also provided by qgraph; remove malloc-generic.c and malloc-generic.h which are not used anymore. Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 6 +- tests/libqos/malloc-generic.c | 20 -- tests/libqos/malloc-generic.h | 18 -- tests/libqtest.h | 3 + tests/virtio-blk-test.c | 472 +++++++++++++--------------------- 5 files changed, 188 insertions(+), 331 deletions(-) delete mode 100644 tests/libqos/malloc-generic.c delete mode 100644 tests/libqos/malloc-generic.h diff --git a/tests/Makefile.include b/tests/Makefile.include index 5ef1b9b1cb..1d3133e889 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -154,7 +154,6 @@ check-qtest-generic-y += tests/cdrom-test$(EXESUF) check-qtest-ipack-y += tests/ipoctal232-test$(EXESUF) check-qtest-virtio-y += tests/virtio-net-test$(EXESUF) -check-qtest-virtio-y += tests/virtio-blk-test$(EXESUF) check-qtest-virtio-y += tests/virtio-scsi-test$(EXESUF) check-qtest-pci-y += tests/e1000-test$(EXESUF) @@ -285,7 +284,6 @@ check-qtest-arm-y += tests/pca9552-test$(EXESUF) check-qtest-arm-y += tests/ds1338-test$(EXESUF) check-qtest-arm-y += tests/microbit-test$(EXESUF) check-qtest-arm-y += tests/m25p80-test$(EXESUF) -check-qtest-arm-$(CONFIG_VIRTIO_BLK) += tests/virtio-blk-test$(EXESUF) check-qtest-arm-y += tests/test-arm-mptimer$(EXESUF) check-qtest-arm-y += tests/boot-serial-test$(EXESUF) check-qtest-arm-y += tests/hexloader-test$(EXESUF) @@ -722,7 +720,7 @@ tests/test-crypto-block$(EXESUF): tests/test-crypto-block.o $(test-crypto-obj-y) libqgraph-obj-y = tests/libqos/qgraph.o libqos-obj-y = $(libqgraph-obj-y) tests/libqos/pci.o tests/libqos/fw_cfg.o -libqos-obj-y += tests/libqos/malloc.o tests/libqos/malloc-generic.o +libqos-obj-y += tests/libqos/malloc.o libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o libqos-spapr-obj-y = $(libqos-obj-y) tests/libqos/malloc-spapr.o libqos-spapr-obj-y += tests/libqos/libqos-spapr.o @@ -764,6 +762,7 @@ qos-test-obj-y += tests/pci-test.o qos-test-obj-y += tests/sdhci-test.o qos-test-obj-y += tests/virtio-test.o qos-test-obj-$(CONFIG_VIRTFS) += tests/virtio-9p-test.o +qos-test-obj-y += tests/virtio-blk-test.o qos-test-obj-y += tests/virtio-rng-test.o qos-test-obj-y += tests/virtio-serial-test.o @@ -811,7 +810,6 @@ tests/vmxnet3-test$(EXESUF): tests/vmxnet3-test.o tests/ne2000-test$(EXESUF): tests/ne2000-test.o tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y) -tests/virtio-blk-test$(EXESUF): tests/virtio-blk-test.o $(libqos-virtio-obj-y) tests/virtio-ccw-test$(EXESUF): tests/virtio-ccw-test.o tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o $(libqos-pc-obj-y) $(libqos-virtio-obj-y) tests/virtio-scsi-test$(EXESUF): tests/virtio-scsi-test.o $(libqos-virtio-obj-y) diff --git a/tests/libqos/malloc-generic.c b/tests/libqos/malloc-generic.c deleted file mode 100644 index 94290daffc..0000000000 --- a/tests/libqos/malloc-generic.c +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Basic libqos generic malloc support - * - * Copyright (c) 2014 Marc Marí - * - * 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 "qemu/osdep.h" -#include "libqos/malloc-generic.h" -#include "libqos/malloc.h" - -void generic_alloc_init(QGuestAllocator *s, uint64_t base_addr, - uint64_t size, uint32_t page_size) -{ - uint64_t start = base_addr + (1 << 20); /* Start at 1MB */ - - alloc_init(s, 0, start, start + size, page_size); -} diff --git a/tests/libqos/malloc-generic.h b/tests/libqos/malloc-generic.h deleted file mode 100644 index 40ea058b2d..0000000000 --- a/tests/libqos/malloc-generic.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Basic libqos generic malloc support - * - * Copyright (c) 2014 Marc Marí - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#ifndef LIBQOS_MALLOC_GENERIC_H -#define LIBQOS_MALLOC_GENERIC_H - -#include "libqos/malloc.h" - -void generic_alloc_init(QGuestAllocator *s, uint64_t base_addr, uint64_t size, - uint32_t page_size); - -#endif diff --git a/tests/libqtest.h b/tests/libqtest.h index 1a2f87ef0b..a16acd58a6 100644 --- a/tests/libqtest.h +++ b/tests/libqtest.h @@ -17,6 +17,9 @@ #ifndef LIBQTEST_H #define LIBQTEST_H +#include "qapi/qmp/qobject.h" +#include "qapi/qmp/qdict.h" + typedef struct QTestState QTestState; extern QTestState *global_qtest; diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c index 3a8126f194..b02be0274e 100644 --- a/tests/virtio-blk-test.c +++ b/tests/virtio-blk-test.c @@ -10,20 +10,11 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "libqos/libqos-pc.h" -#include "libqos/libqos-spapr.h" -#include "libqos/virtio.h" -#include "libqos/virtio-pci.h" -#include "libqos/virtio-mmio.h" -#include "libqos/malloc.h" -#include "libqos/malloc-generic.h" -#include "qapi/qmp/qdict.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_blk.h" #include "standard-headers/linux/virtio_pci.h" +#include "libqos/qgraph.h" +#include "libqos/virtio-blk.h" /* TODO actually test the results and get rid of this */ #define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__)) @@ -31,13 +22,6 @@ #define TEST_IMAGE_SIZE (64 * 1024 * 1024) #define QVIRTIO_BLK_TIMEOUT_US (30 * 1000 * 1000) #define PCI_SLOT_HP 0x06 -#define PCI_SLOT 0x04 -#define PCI_FN 0x00 - -#define MMIO_PAGE_SIZE 4096 -#define MMIO_DEV_BASE_ADDR 0x0A003E00 -#define MMIO_RAM_ADDR 0x40000000 -#define MMIO_RAM_SIZE 0x20000000 typedef struct QVirtioBlkReq { uint32_t type; @@ -47,84 +31,34 @@ typedef struct QVirtioBlkReq { uint8_t status; } QVirtioBlkReq; + #ifdef HOST_WORDS_BIGENDIAN const bool host_is_big_endian = true; #else const bool host_is_big_endian; /* false */ #endif +static void drive_destroy(void *path) +{ + unlink(path); + g_free(path); + qos_invalidate_command_line(); +} + static char *drive_create(void) { int fd, ret; - char *tmp_path = g_strdup("/tmp/qtest.XXXXXX"); + char *t_path = g_strdup("/tmp/qtest.XXXXXX"); /* Create a temporary raw image */ - fd = mkstemp(tmp_path); + fd = mkstemp(t_path); g_assert_cmpint(fd, >=, 0); ret = ftruncate(fd, TEST_IMAGE_SIZE); g_assert_cmpint(ret, ==, 0); close(fd); - return tmp_path; -} - -static QOSState *pci_test_start(void) -{ - QOSState *qs; - const char *arch = qtest_get_arch(); - char *tmp_path; - const char *cmd = "-drive if=none,id=drive0,file=%s,format=raw " - "-drive if=none,id=drive1,file=null-co://,format=raw " - "-device virtio-blk-pci,id=drv0,drive=drive0," - "addr=%x.%x"; - - tmp_path = drive_create(); - - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qs = qtest_pc_boot(cmd, tmp_path, PCI_SLOT, PCI_FN); - } else if (strcmp(arch, "ppc64") == 0) { - qs = qtest_spapr_boot(cmd, tmp_path, PCI_SLOT, PCI_FN); - } else { - g_printerr("virtio-blk tests are only available on x86 or ppc64\n"); - exit(EXIT_FAILURE); - } - global_qtest = qs->qts; - unlink(tmp_path); - g_free(tmp_path); - return qs; -} - -static void arm_test_start(void) -{ - char *tmp_path; - - tmp_path = drive_create(); - - global_qtest = qtest_initf("-machine virt " - "-drive if=none,id=drive0,file=%s,format=raw " - "-device virtio-blk-device,drive=drive0", - tmp_path); - unlink(tmp_path); - g_free(tmp_path); -} - -static void test_end(void) -{ - qtest_end(); -} - -static QVirtioPCIDevice *virtio_blk_pci_init(QPCIBus *bus, int slot) -{ - QVirtioPCIDevice *dev; - - dev = qvirtio_pci_device_find_slot(bus, VIRTIO_ID_BLOCK, slot); - g_assert(dev != NULL); - g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK); - g_assert_cmphex(dev->pdev->devfn, ==, ((slot << 3) | PCI_FN)); - - qvirtio_pci_device_enable(dev); - qvirtio_start_device(&dev->vdev); - return dev; + g_test_queue_destroy(drive_destroy, t_path); + return t_path; } static inline void virtio_blk_fix_request(QVirtioDevice *d, QVirtioBlkReq *req) @@ -395,32 +329,21 @@ static void test_basic(QVirtioDevice *dev, QGuestAllocator *alloc, } } -static void pci_basic(void) +static void basic(void *obj, void *data, QGuestAllocator *t_alloc) { - QVirtioPCIDevice *dev; - QOSState *qs; - QVirtQueuePCI *vqpci; + QVirtioBlk *blk_if = obj; + QVirtQueue *vq; + vq = qvirtqueue_setup(blk_if->vdev, t_alloc, 0); + test_basic(blk_if->vdev, t_alloc, vq); + qvirtqueue_cleanup(blk_if->vdev->bus, vq, t_alloc); - qs = pci_test_start(); - dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); - - vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, &qs->alloc, 0); - - test_basic(&dev->vdev, &qs->alloc, &vqpci->vq); - - /* End test */ - qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, &qs->alloc); - qvirtio_pci_device_disable(dev); - qvirtio_pci_device_free(dev); - g_free(dev); - qtest_shutdown(qs); } -static void pci_indirect(void) +static void indirect(void *obj, void *u_data, QGuestAllocator *t_alloc) { - QVirtioPCIDevice *dev; - QVirtQueuePCI *vqpci; - QOSState *qs; + QVirtQueue *vq; + QVirtioBlk *blk_if = obj; + QVirtioDevice *dev = blk_if->vdev; QVirtioBlkReq req; QVRingIndirectDesc *indirect; uint64_t req_addr; @@ -430,22 +353,18 @@ static void pci_indirect(void) uint8_t status; char *data; - qs = pci_test_start(); - - dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); - - capacity = qvirtio_config_readq(&dev->vdev, 0); + capacity = qvirtio_config_readq(dev, 0); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - features = qvirtio_get_features(&dev->vdev); + features = qvirtio_get_features(dev); g_assert_cmphex(features & (1u << VIRTIO_RING_F_INDIRECT_DESC), !=, 0); features = features & ~(QVIRTIO_F_BAD_FEATURE | (1u << VIRTIO_RING_F_EVENT_IDX) | (1u << VIRTIO_BLK_F_SCSI)); - qvirtio_set_features(&dev->vdev, features); + qvirtio_set_features(dev, features); - vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, &qs->alloc, 0); - qvirtio_set_driver_ok(&dev->vdev); + vq = qvirtqueue_setup(dev, t_alloc, 0); + qvirtio_set_driver_ok(dev); /* Write request */ req.type = VIRTIO_BLK_T_OUT; @@ -454,23 +373,23 @@ static void pci_indirect(void) req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(&qs->alloc, &dev->vdev, &req, 512); + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); g_free(req.data); - indirect = qvring_indirect_desc_setup(&dev->vdev, &qs->alloc, 2); + indirect = qvring_indirect_desc_setup(dev, t_alloc, 2); qvring_indirect_desc_add(indirect, req_addr, 528, false); qvring_indirect_desc_add(indirect, req_addr + 528, 1, true); - free_head = qvirtqueue_add_indirect(&vqpci->vq, indirect); - qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); + free_head = qvirtqueue_add_indirect(vq, indirect); + qvirtqueue_kick(dev, vq, free_head); - qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, free_head, NULL, + qvirtio_wait_used_elem(dev, vq, free_head, NULL, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); g_free(indirect); - guest_free(&qs->alloc, req_addr); + guest_free(t_alloc, req_addr); /* Read request */ req.type = VIRTIO_BLK_T_IN; @@ -479,17 +398,17 @@ static void pci_indirect(void) req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(&qs->alloc, &dev->vdev, &req, 512); + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); g_free(req.data); - indirect = qvring_indirect_desc_setup(&dev->vdev, &qs->alloc, 2); + indirect = qvring_indirect_desc_setup(dev, t_alloc, 2); qvring_indirect_desc_add(indirect, req_addr, 16, false); qvring_indirect_desc_add(indirect, req_addr + 16, 513, true); - free_head = qvirtqueue_add_indirect(&vqpci->vq, indirect); - qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); + free_head = qvirtqueue_add_indirect(vq, indirect); + qvirtqueue_kick(dev, vq, free_head); - qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, free_head, NULL, + qvirtio_wait_used_elem(dev, vq, free_head, NULL, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); @@ -500,52 +419,37 @@ static void pci_indirect(void) g_free(data); g_free(indirect); - guest_free(&qs->alloc, req_addr); - - /* End test */ - qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, &qs->alloc); - qvirtio_pci_device_disable(dev); - qvirtio_pci_device_free(dev); - g_free(dev); - qtest_shutdown(qs); + guest_free(t_alloc, req_addr); + qvirtqueue_cleanup(dev->bus, vq, t_alloc); } -static void pci_config(void) +static void config(void *obj, void *data, QGuestAllocator *t_alloc) { - QVirtioPCIDevice *dev; - QOSState *qs; + QVirtioBlk *blk_if = obj; + QVirtioDevice *dev = blk_if->vdev; int n_size = TEST_IMAGE_SIZE / 2; uint64_t capacity; - qs = pci_test_start(); - - dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); - - capacity = qvirtio_config_readq(&dev->vdev, 0); + capacity = qvirtio_config_readq(dev, 0); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - qvirtio_set_driver_ok(&dev->vdev); + qvirtio_set_driver_ok(dev); qmp_discard_response("{ 'execute': 'block_resize', " " 'arguments': { 'device': 'drive0', " " 'size': %d } }", n_size); - qvirtio_wait_config_isr(&dev->vdev, QVIRTIO_BLK_TIMEOUT_US); + qvirtio_wait_config_isr(dev, QVIRTIO_BLK_TIMEOUT_US); - capacity = qvirtio_config_readq(&dev->vdev, 0); + capacity = qvirtio_config_readq(dev, 0); g_assert_cmpint(capacity, ==, n_size / 512); - - qvirtio_pci_device_disable(dev); - qvirtio_pci_device_free(dev); - g_free(dev); - - qtest_shutdown(qs); } -static void pci_msix(void) +static void msix(void *obj, void *u_data, QGuestAllocator *t_alloc) { - QVirtioPCIDevice *dev; - QOSState *qs; - QVirtQueuePCI *vqpci; + QVirtQueue *vq; + QVirtioBlkPCI *blk = obj; + QVirtioPCIDevice *pdev = &blk->pci_vdev; + QVirtioDevice *dev = &pdev->vdev; QVirtioBlkReq req; int n_size = TEST_IMAGE_SIZE / 2; uint64_t req_addr; @@ -554,36 +458,38 @@ static void pci_msix(void) uint32_t free_head; uint8_t status; char *data; + QOSGraphObject *blk_object = obj; + QPCIDevice *pci_dev = blk_object->get_driver(blk_object, "pci-device"); - qs = pci_test_start(); + if (qpci_check_buggy_msi(pci_dev)) { + return; + } - dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); - qpci_msix_enable(dev->pdev); + qpci_msix_enable(pdev->pdev); + qvirtio_pci_set_msix_configuration_vector(pdev, t_alloc, 0); - qvirtio_pci_set_msix_configuration_vector(dev, &qs->alloc, 0); - - capacity = qvirtio_config_readq(&dev->vdev, 0); + capacity = qvirtio_config_readq(dev, 0); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - features = qvirtio_get_features(&dev->vdev); + features = qvirtio_get_features(dev); features = features & ~(QVIRTIO_F_BAD_FEATURE | (1u << VIRTIO_RING_F_INDIRECT_DESC) | (1u << VIRTIO_RING_F_EVENT_IDX) | (1u << VIRTIO_BLK_F_SCSI)); - qvirtio_set_features(&dev->vdev, features); + qvirtio_set_features(dev, features); - vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, &qs->alloc, 0); - qvirtqueue_pci_msix_setup(dev, vqpci, &qs->alloc, 1); + vq = qvirtqueue_setup(dev, t_alloc, 0); + qvirtqueue_pci_msix_setup(pdev, (QVirtQueuePCI *)vq, t_alloc, 1); - qvirtio_set_driver_ok(&dev->vdev); + qvirtio_set_driver_ok(dev); qmp_discard_response("{ 'execute': 'block_resize', " " 'arguments': { 'device': 'drive0', " " 'size': %d } }", n_size); - qvirtio_wait_config_isr(&dev->vdev, QVIRTIO_BLK_TIMEOUT_US); + qvirtio_wait_config_isr(dev, QVIRTIO_BLK_TIMEOUT_US); - capacity = qvirtio_config_readq(&dev->vdev, 0); + capacity = qvirtio_config_readq(dev, 0); g_assert_cmpint(capacity, ==, n_size / 512); /* Write request */ @@ -593,22 +499,22 @@ static void pci_msix(void) req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(&qs->alloc, &dev->vdev, &req, 512); + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); g_free(req.data); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); + free_head = qvirtqueue_add(vq, req_addr, 16, false, true); + qvirtqueue_add(vq, req_addr + 16, 512, false, true); + qvirtqueue_add(vq, req_addr + 528, 1, true, false); + qvirtqueue_kick(dev, vq, free_head); - qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, free_head, NULL, + qvirtio_wait_used_elem(dev, vq, free_head, NULL, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); - guest_free(&qs->alloc, req_addr); + guest_free(t_alloc, req_addr); /* Read request */ req.type = VIRTIO_BLK_T_IN; @@ -616,18 +522,18 @@ static void pci_msix(void) req.sector = 0; req.data = g_malloc0(512); - req_addr = virtio_blk_request(&qs->alloc, &dev->vdev, &req, 512); + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); g_free(req.data); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true); - qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); + free_head = qvirtqueue_add(vq, req_addr, 16, false, true); + qvirtqueue_add(vq, req_addr + 16, 512, true, true); + qvirtqueue_add(vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); + qvirtqueue_kick(dev, vq, free_head); - qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, free_head, NULL, + qvirtio_wait_used_elem(dev, vq, free_head, NULL, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); @@ -638,22 +544,19 @@ static void pci_msix(void) g_assert_cmpstr(data, ==, "TEST"); g_free(data); - guest_free(&qs->alloc, req_addr); + guest_free(t_alloc, req_addr); /* End test */ - qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, &qs->alloc); - qpci_msix_disable(dev->pdev); - qvirtio_pci_device_disable(dev); - qvirtio_pci_device_free(dev); - g_free(dev); - qtest_shutdown(qs); + qpci_msix_disable(pdev->pdev); + qvirtqueue_cleanup(dev->bus, vq, t_alloc); } -static void pci_idx(void) +static void idx(void *obj, void *u_data, QGuestAllocator *t_alloc) { - QVirtioPCIDevice *dev; - QOSState *qs; - QVirtQueuePCI *vqpci; + QVirtQueue *vq; + QVirtioBlkPCI *blk = obj; + QVirtioPCIDevice *pdev = &blk->pci_vdev; + QVirtioDevice *dev = &pdev->vdev; QVirtioBlkReq req; uint64_t req_addr; uint64_t capacity; @@ -663,28 +566,30 @@ static void pci_idx(void) uint32_t desc_idx; uint8_t status; char *data; + QOSGraphObject *blk_object = obj; + QPCIDevice *pci_dev = blk_object->get_driver(blk_object, "pci-device"); - qs = pci_test_start(); + if (qpci_check_buggy_msi(pci_dev)) { + return; + } - dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); - qpci_msix_enable(dev->pdev); + qpci_msix_enable(pdev->pdev); + qvirtio_pci_set_msix_configuration_vector(pdev, t_alloc, 0); - qvirtio_pci_set_msix_configuration_vector(dev, &qs->alloc, 0); - - capacity = qvirtio_config_readq(&dev->vdev, 0); + capacity = qvirtio_config_readq(dev, 0); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - features = qvirtio_get_features(&dev->vdev); + features = qvirtio_get_features(dev); features = features & ~(QVIRTIO_F_BAD_FEATURE | (1u << VIRTIO_RING_F_INDIRECT_DESC) | (1u << VIRTIO_F_NOTIFY_ON_EMPTY) | (1u << VIRTIO_BLK_F_SCSI)); - qvirtio_set_features(&dev->vdev, features); + qvirtio_set_features(dev, features); - vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, &qs->alloc, 0); - qvirtqueue_pci_msix_setup(dev, vqpci, &qs->alloc, 1); + vq = qvirtqueue_setup(dev, t_alloc, 0); + qvirtqueue_pci_msix_setup(pdev, (QVirtQueuePCI *)vq, t_alloc, 1); - qvirtio_set_driver_ok(&dev->vdev); + qvirtio_set_driver_ok(dev); /* Write request */ req.type = VIRTIO_BLK_T_OUT; @@ -693,16 +598,16 @@ static void pci_idx(void) req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(&qs->alloc, &dev->vdev, &req, 512); + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); g_free(req.data); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); + free_head = qvirtqueue_add(vq, req_addr, 16, false, true); + qvirtqueue_add(vq, req_addr + 16, 512, false, true); + qvirtqueue_add(vq, req_addr + 528, 1, true, false); + qvirtqueue_kick(dev, vq, free_head); - qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, free_head, NULL, + qvirtio_wait_used_elem(dev, vq, free_head, NULL, QVIRTIO_BLK_TIMEOUT_US); /* Write request */ @@ -712,25 +617,25 @@ static void pci_idx(void) req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(&qs->alloc, &dev->vdev, &req, 512); + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); g_free(req.data); /* Notify after processing the third request */ - qvirtqueue_set_used_event(&vqpci->vq, 2); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); + qvirtqueue_set_used_event(vq, 2); + free_head = qvirtqueue_add(vq, req_addr, 16, false, true); + qvirtqueue_add(vq, req_addr + 16, 512, false, true); + qvirtqueue_add(vq, req_addr + 528, 1, true, false); + qvirtqueue_kick(dev, vq, free_head); write_head = free_head; /* No notification expected */ - status = qvirtio_wait_status_byte_no_isr(&dev->vdev, - &vqpci->vq, req_addr + 528, + status = qvirtio_wait_status_byte_no_isr(dev, + vq, req_addr + 528, QVIRTIO_BLK_TIMEOUT_US); g_assert_cmpint(status, ==, 0); - guest_free(&qs->alloc, req_addr); + guest_free(t_alloc, req_addr); /* Read request */ req.type = VIRTIO_BLK_T_IN; @@ -738,20 +643,20 @@ static void pci_idx(void) req.sector = 1; req.data = g_malloc0(512); - req_addr = virtio_blk_request(&qs->alloc, &dev->vdev, &req, 512); + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); g_free(req.data); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true); - qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); + free_head = qvirtqueue_add(vq, req_addr, 16, false, true); + qvirtqueue_add(vq, req_addr + 16, 512, true, true); + qvirtqueue_add(vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); + qvirtqueue_kick(dev, vq, free_head); /* We get just one notification for both requests */ - qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, write_head, NULL, + qvirtio_wait_used_elem(dev, vq, write_head, NULL, QVIRTIO_BLK_TIMEOUT_US); - g_assert(qvirtqueue_get_buf(&vqpci->vq, &desc_idx, NULL)); + g_assert(qvirtqueue_get_buf(vq, &desc_idx, NULL)); g_assert_cmpint(desc_idx, ==, free_head); status = readb(req_addr + 528); @@ -762,125 +667,114 @@ static void pci_idx(void) g_assert_cmpstr(data, ==, "TEST"); g_free(data); - guest_free(&qs->alloc, req_addr); + guest_free(t_alloc, req_addr); /* End test */ - qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, &qs->alloc); - qpci_msix_disable(dev->pdev); - qvirtio_pci_device_disable(dev); - qvirtio_pci_device_free(dev); - g_free(dev); - qtest_shutdown(qs); + qpci_msix_disable(pdev->pdev); + + qvirtqueue_cleanup(dev->bus, vq, t_alloc); } -static void pci_hotplug(void) +static void pci_hotplug(void *obj, void *data, QGuestAllocator *t_alloc) { + QVirtioPCIDevice *dev1 = obj; QVirtioPCIDevice *dev; - QOSState *qs; - const char *arch = qtest_get_arch(); - - qs = pci_test_start(); /* plug secondary disk */ qtest_qmp_device_add("virtio-blk-pci", "drv1", "{'addr': %s, 'drive': 'drive1'}", - stringify(PCI_SLOT_HP)); + stringify(PCI_SLOT_HP) ".0"); - dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT_HP); - g_assert(dev); + dev = virtio_pci_new(dev1->pdev->bus, + &(QPCIAddress) { .devfn = QPCI_DEVFN(PCI_SLOT_HP, 0) }); + g_assert_nonnull(dev); + g_assert_cmpint(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK); qvirtio_pci_device_disable(dev); - qvirtio_pci_device_free(dev); - g_free(dev); + qos_object_destroy((QOSGraphObject *)dev); /* unplug secondary disk */ - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qpci_unplug_acpi_device_test("drv1", PCI_SLOT_HP); - } - qtest_shutdown(qs); + qpci_unplug_acpi_device_test("drv1", PCI_SLOT_HP); } /* * Check that setting the vring addr on a non-existent virtqueue does * not crash. */ -static void test_nonexistent_virtqueue(void) +static void test_nonexistent_virtqueue(void *obj, void *data, + QGuestAllocator *t_alloc) { + QVirtioBlkPCI *blk = obj; + QVirtioPCIDevice *pdev = &blk->pci_vdev; QPCIBar bar0; - QOSState *qs; QPCIDevice *dev; - qs = pci_test_start(); - dev = qpci_device_find(qs->pcibus, QPCI_DEVFN(4, 0)); + dev = qpci_device_find(pdev->pdev->bus, QPCI_DEVFN(4, 0)); g_assert(dev != NULL); - qpci_device_enable(dev); + bar0 = qpci_iomap(dev, 0, NULL); qpci_io_writeb(dev, bar0, VIRTIO_PCI_QUEUE_SEL, 2); qpci_io_writel(dev, bar0, VIRTIO_PCI_QUEUE_PFN, 1); + g_free(dev); - qtest_shutdown(qs); } -static void mmio_basic(void) +static void resize(void *obj, void *data, QGuestAllocator *t_alloc) { - QVirtioMMIODevice *dev; - QVirtQueue *vq; - QGuestAllocator alloc; + QVirtioBlk *blk_if = obj; + QVirtioDevice *dev = blk_if->vdev; int n_size = TEST_IMAGE_SIZE / 2; uint64_t capacity; + QVirtQueue *vq; - arm_test_start(); + vq = qvirtqueue_setup(dev, t_alloc, 0); - dev = g_malloc0(sizeof(QVirtioMMIODevice)); - qvirtio_mmio_init_device(dev, global_qtest, MMIO_DEV_BASE_ADDR, MMIO_PAGE_SIZE); - g_assert(dev != NULL); - g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK); - - qvirtio_start_device(&dev->vdev); - - generic_alloc_init(&alloc, MMIO_RAM_ADDR, MMIO_RAM_SIZE, MMIO_PAGE_SIZE); - vq = qvirtqueue_setup(&dev->vdev, &alloc, 0); - - test_basic(&dev->vdev, &alloc, vq); + test_basic(dev, t_alloc, vq); qmp_discard_response("{ 'execute': 'block_resize', " " 'arguments': { 'device': 'drive0', " " 'size': %d } }", n_size); - qvirtio_wait_queue_isr(&dev->vdev, vq, QVIRTIO_BLK_TIMEOUT_US); + qvirtio_wait_queue_isr(dev, vq, QVIRTIO_BLK_TIMEOUT_US); - capacity = qvirtio_config_readq(&dev->vdev, 0); + capacity = qvirtio_config_readq(dev, 0); g_assert_cmpint(capacity, ==, n_size / 512); - /* End test */ - qvirtqueue_cleanup(dev->vdev.bus, vq, &alloc); - g_free(dev); - alloc_destroy(&alloc); - test_end(); + qvirtqueue_cleanup(dev->bus, vq, t_alloc); + } -int main(int argc, char **argv) +static void *virtio_blk_test_setup(GString *cmd_line, void *arg) { - const char *arch = qtest_get_arch(); + char *tmp_path = drive_create(); - g_test_init(&argc, &argv, NULL); + g_string_append_printf(cmd_line, + " -drive if=none,id=drive0,file=%s,format=raw " + "-drive if=none,id=drive1,file=null-co://,format=raw ", + tmp_path); - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0 || - strcmp(arch, "ppc64") == 0) { - qtest_add_func("/virtio/blk/pci/basic", pci_basic); - qtest_add_func("/virtio/blk/pci/indirect", pci_indirect); - qtest_add_func("/virtio/blk/pci/config", pci_config); - qtest_add_func("/virtio/blk/pci/nxvirtq", test_nonexistent_virtqueue); - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qtest_add_func("/virtio/blk/pci/msix", pci_msix); - qtest_add_func("/virtio/blk/pci/idx", pci_idx); - } - qtest_add_func("/virtio/blk/pci/hotplug", pci_hotplug); - } else if (strcmp(arch, "arm") == 0) { - qtest_add_func("/virtio/blk/mmio/basic", mmio_basic); - } - - return g_test_run(); + return arg; } + +static void register_virtio_blk_test(void) +{ + QOSGraphTestOptions opts = { + .before = virtio_blk_test_setup, + }; + + qos_add_test("indirect", "virtio-blk", indirect, &opts); + qos_add_test("config", "virtio-blk", config, &opts); + qos_add_test("basic", "virtio-blk", basic, &opts); + qos_add_test("resize", "virtio-blk", resize, &opts); + + /* tests just for virtio-blk-pci */ + qos_add_test("msix", "virtio-blk-pci", msix, &opts); + qos_add_test("idx", "virtio-blk-pci", idx, &opts); + qos_add_test("nxvirtq", "virtio-blk-pci", + test_nonexistent_virtqueue, &opts); + qos_add_test("hotplug", "virtio-blk-pci", pci_hotplug, &opts); +} + +libqos_init(register_virtio_blk_test); From 583349d1ef8b73968428b8443e39d74c43d127f9 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Mon, 30 Jul 2018 22:21:42 +0200 Subject: [PATCH 36/57] tests/libqos: virtio-net driver and interface nodes Add qgraph nodes for virtio-net-pci and virtio-net-device. Both nodes produce virtio-net, but virtio-net-pci receives a pci-bus and overrides virtio-pci QOSGraphObject and its functions, while virtio-net-device receives a virtio and implements its own functions Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 1 + tests/libqos/virtio-net.c | 182 ++++++++++++++++++++++++++++++++++++++ tests/libqos/virtio-net.h | 41 +++++++++ tests/libqos/virtio.c | 1 + tests/libqos/virtio.h | 1 + 5 files changed, 226 insertions(+) create mode 100644 tests/libqos/virtio-net.c create mode 100644 tests/libqos/virtio-net.h diff --git a/tests/Makefile.include b/tests/Makefile.include index 1d3133e889..ec23364c69 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -743,6 +743,7 @@ qos-test-obj-y += $(libqos-virtio-obj-y) qos-test-obj-$(CONFIG_VIRTFS) += tests/libqos/virtio-9p.o qos-test-obj-y += tests/libqos/virtio-balloon.o qos-test-obj-y += tests/libqos/virtio-blk.o +qos-test-obj-y += tests/libqos/virtio-net.o qos-test-obj-y += tests/libqos/virtio-rng.o qos-test-obj-y += tests/libqos/virtio-serial.o diff --git a/tests/libqos/virtio-net.c b/tests/libqos/virtio-net.c new file mode 100644 index 0000000000..3ddfbdc419 --- /dev/null +++ b/tests/libqos/virtio-net.c @@ -0,0 +1,182 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/virtio-net.h" +#include "hw/virtio/virtio-net.h" + + +static QGuestAllocator *alloc; + +static void virtio_net_cleanup(QVirtioNet *interface) +{ + qvirtqueue_cleanup(interface->vdev->bus, interface->rx, alloc); + qvirtqueue_cleanup(interface->vdev->bus, interface->tx, alloc); +} + +static void virtio_net_setup(QVirtioNet *interface) +{ + QVirtioDevice *vdev = interface->vdev; + uint64_t features; + + features = qvirtio_get_features(vdev); + features &= ~(QVIRTIO_F_BAD_FEATURE | + (1u << VIRTIO_RING_F_INDIRECT_DESC) | + (1u << VIRTIO_RING_F_EVENT_IDX)); + qvirtio_set_features(vdev, features); + + interface->rx = qvirtqueue_setup(vdev, alloc, 0); + interface->tx = qvirtqueue_setup(vdev, alloc, 1); + qvirtio_set_driver_ok(vdev); +} + +/* virtio-net-device */ +static void qvirtio_net_device_destructor(QOSGraphObject *obj) +{ + QVirtioNetDevice *v_net = (QVirtioNetDevice *) obj; + virtio_net_cleanup(&v_net->net); +} + +static void qvirtio_net_device_start_hw(QOSGraphObject *obj) +{ + QVirtioNetDevice *v_net = (QVirtioNetDevice *) obj; + QVirtioNet *interface = &v_net->net; + + virtio_net_setup(interface); +} + +static void *qvirtio_net_get_driver(QVirtioNet *v_net, + const char *interface) +{ + if (!g_strcmp0(interface, "virtio-net")) { + return v_net; + } + if (!g_strcmp0(interface, "virtio")) { + return v_net->vdev; + } + + fprintf(stderr, "%s not present in virtio-net-device\n", interface); + g_assert_not_reached(); +} + +static void *qvirtio_net_device_get_driver(void *object, + const char *interface) +{ + QVirtioNetDevice *v_net = object; + return qvirtio_net_get_driver(&v_net->net, interface); +} + +static void *virtio_net_device_create(void *virtio_dev, + QGuestAllocator *t_alloc, + void *addr) +{ + QVirtioNetDevice *virtio_ndevice = g_new0(QVirtioNetDevice, 1); + QVirtioNet *interface = &virtio_ndevice->net; + + interface->vdev = virtio_dev; + alloc = t_alloc; + + virtio_ndevice->obj.destructor = qvirtio_net_device_destructor; + virtio_ndevice->obj.get_driver = qvirtio_net_device_get_driver; + virtio_ndevice->obj.start_hw = qvirtio_net_device_start_hw; + + return &virtio_ndevice->obj; +} + +/* virtio-net-pci */ +static void qvirtio_net_pci_destructor(QOSGraphObject *obj) +{ + QVirtioNetPCI *v_net = (QVirtioNetPCI *) obj; + QVirtioNet *interface = &v_net->net; + QOSGraphObject *pci_vobj = &v_net->pci_vdev.obj; + + virtio_net_cleanup(interface); + qvirtio_pci_destructor(pci_vobj); +} + +static void qvirtio_net_pci_start_hw(QOSGraphObject *obj) +{ + QVirtioNetPCI *v_net = (QVirtioNetPCI *) obj; + QVirtioNet *interface = &v_net->net; + QOSGraphObject *pci_vobj = &v_net->pci_vdev.obj; + + qvirtio_pci_start_hw(pci_vobj); + virtio_net_setup(interface); +} + +static void *qvirtio_net_pci_get_driver(void *object, + const char *interface) +{ + QVirtioNetPCI *v_net = object; + if (!g_strcmp0(interface, "pci-device")) { + return v_net->pci_vdev.pdev; + } + return qvirtio_net_get_driver(&v_net->net, interface); +} + +static void *virtio_net_pci_create(void *pci_bus, QGuestAllocator *t_alloc, + void *addr) +{ + QVirtioNetPCI *virtio_bpci = g_new0(QVirtioNetPCI, 1); + QVirtioNet *interface = &virtio_bpci->net; + QOSGraphObject *obj = &virtio_bpci->pci_vdev.obj; + + virtio_pci_init(&virtio_bpci->pci_vdev, pci_bus, addr); + interface->vdev = &virtio_bpci->pci_vdev.vdev; + alloc = t_alloc; + + g_assert_cmphex(interface->vdev->device_type, ==, VIRTIO_ID_NET); + + obj->destructor = qvirtio_net_pci_destructor; + obj->start_hw = qvirtio_net_pci_start_hw; + obj->get_driver = qvirtio_net_pci_get_driver; + + return obj; +} + +static void virtio_net_register_nodes(void) +{ + /* FIXME: every test using these nodes needs to setup a + * -netdev socket,id=hs0 otherwise QEMU is not going to start. + * Therefore, we do not include "produces" edge for virtio + * and pci-device yet. + */ + QPCIAddress addr = { + .devfn = QPCI_DEVFN(4, 0), + }; + + QOSGraphEdgeOptions opts = { }; + + /* virtio-net-device */ + opts.extra_device_opts = "netdev=hs0"; + qos_node_create_driver("virtio-net-device", + virtio_net_device_create); + qos_node_consumes("virtio-net-device", "virtio-bus", &opts); + qos_node_produces("virtio-net-device", "virtio-net"); + + /* virtio-net-pci */ + opts.extra_device_opts = "netdev=hs0,addr=04.0"; + add_qpci_address(&opts, &addr); + qos_node_create_driver("virtio-net-pci", virtio_net_pci_create); + qos_node_consumes("virtio-net-pci", "pci-bus", &opts); + qos_node_produces("virtio-net-pci", "virtio-net"); +} + +libqos_init(virtio_net_register_nodes); diff --git a/tests/libqos/virtio-net.h b/tests/libqos/virtio-net.h new file mode 100644 index 0000000000..e6905cd82e --- /dev/null +++ b/tests/libqos/virtio-net.h @@ -0,0 +1,41 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "libqos/qgraph.h" +#include "libqos/virtio.h" +#include "libqos/virtio-pci.h" + +typedef struct QVirtioNet QVirtioNet; +typedef struct QVirtioNetPCI QVirtioNetPCI; +typedef struct QVirtioNetDevice QVirtioNetDevice; + +struct QVirtioNet { + QVirtioDevice *vdev; + QVirtQueue *rx; + QVirtQueue *tx; +}; + +struct QVirtioNetPCI { + QVirtioPCIDevice pci_vdev; + QVirtioNet net; +}; + +struct QVirtioNetDevice { + QOSGraphObject obj; + QVirtioNet net; +}; diff --git a/tests/libqos/virtio.c b/tests/libqos/virtio.c index 9fc8c7ed83..e4925d160b 100644 --- a/tests/libqos/virtio.c +++ b/tests/libqos/virtio.c @@ -40,6 +40,7 @@ uint32_t qvirtio_get_features(QVirtioDevice *d) void qvirtio_set_features(QVirtioDevice *d, uint32_t features) { + d->features = features; d->bus->set_features(d, features); } diff --git a/tests/libqos/virtio.h b/tests/libqos/virtio.h index 7f0f9dd920..3f97d79c28 100644 --- a/tests/libqos/virtio.h +++ b/tests/libqos/virtio.h @@ -21,6 +21,7 @@ typedef struct QVirtioDevice { const QVirtioBus *bus; /* Device type */ uint16_t device_type; + uint64_t features; bool big_endian; } QVirtioDevice; From 6ae333f91b99cc4448e6c0af45f38d87bc952c82 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Mon, 30 Jul 2018 22:22:20 +0200 Subject: [PATCH 37/57] qos-test: virtio-net test node Convert tests/virtio-net-test in qgraph test node, virtio-net-test. This test consumes a virtio-net interface and checks that its function return the expected values. Some functions are implemented only for virtio-net-pci, so they don't consume virtio-net, but virtio-net-pci Note that this test does not allocate any virtio-net structure, it's all done by the qtest walking graph mechanism Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 3 +- tests/virtio-net-test.c | 258 ++++++++++++++-------------------------- 2 files changed, 92 insertions(+), 169 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index ec23364c69..f45d1156f2 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -153,7 +153,6 @@ check-qtest-generic-y += tests/cdrom-test$(EXESUF) check-qtest-ipack-y += tests/ipoctal232-test$(EXESUF) -check-qtest-virtio-y += tests/virtio-net-test$(EXESUF) check-qtest-virtio-y += tests/virtio-scsi-test$(EXESUF) check-qtest-pci-y += tests/e1000-test$(EXESUF) @@ -764,6 +763,7 @@ qos-test-obj-y += tests/sdhci-test.o qos-test-obj-y += tests/virtio-test.o qos-test-obj-$(CONFIG_VIRTFS) += tests/virtio-9p-test.o qos-test-obj-y += tests/virtio-blk-test.o +qos-test-obj-y += tests/virtio-net-test.o qos-test-obj-y += tests/virtio-rng-test.o qos-test-obj-y += tests/virtio-serial-test.o @@ -812,7 +812,6 @@ tests/ne2000-test$(EXESUF): tests/ne2000-test.o tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y) tests/virtio-ccw-test$(EXESUF): tests/virtio-ccw-test.o -tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o $(libqos-pc-obj-y) $(libqos-virtio-obj-y) tests/virtio-scsi-test$(EXESUF): tests/virtio-scsi-test.o $(libqos-virtio-obj-y) tests/tpci200-test$(EXESUF): tests/tpci200-test.o tests/display-vga-test$(EXESUF): tests/display-vga-test.o diff --git a/tests/virtio-net-test.c b/tests/virtio-net-test.c index 653148eeab..2d3630ca79 100644 --- a/tests/virtio-net-test.c +++ b/tests/virtio-net-test.c @@ -9,18 +9,11 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qemu-common.h" -#include "qemu/sockets.h" #include "qemu/iov.h" -#include "libqos/libqos-pc.h" -#include "libqos/libqos-spapr.h" -#include "libqos/virtio.h" -#include "libqos/virtio-pci.h" #include "qapi/qmp/qdict.h" -#include "qemu/bswap.h" #include "hw/virtio/virtio-net.h" -#include "standard-headers/linux/virtio_ids.h" -#include "standard-headers/linux/virtio_ring.h" +#include "libqos/qgraph.h" +#include "libqos/virtio-net.h" #define PCI_SLOT_HP 0x06 #define PCI_SLOT 0x04 @@ -28,63 +21,8 @@ #define QVIRTIO_NET_TIMEOUT_US (30 * 1000 * 1000) #define VNET_HDR_SIZE sizeof(struct virtio_net_hdr_mrg_rxbuf) -static void test_end(void) -{ - qtest_end(); -} - #ifndef _WIN32 -static QVirtioPCIDevice *virtio_net_pci_init(QPCIBus *bus, int slot) -{ - QVirtioPCIDevice *dev; - - dev = qvirtio_pci_device_find(bus, VIRTIO_ID_NET); - g_assert(dev != NULL); - g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_NET); - - qvirtio_pci_device_enable(dev); - qvirtio_start_device(&dev->vdev); - - return dev; -} - -GCC_FMT_ATTR(1, 2) -static QOSState *pci_test_start(const char *cmd, ...) -{ - QOSState *qs; - va_list ap; - const char *arch = qtest_get_arch(); - - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - va_start(ap, cmd); - qs = qtest_pc_vboot(cmd, ap); - va_end(ap); - } else if (strcmp(arch, "ppc64") == 0) { - va_start(ap, cmd); - qs = qtest_spapr_vboot(cmd, ap); - va_end(ap); - } else { - g_printerr("virtio-net tests are only available on x86 or ppc64\n"); - exit(EXIT_FAILURE); - } - global_qtest = qs->qts; - return qs; -} - -static void driver_init(QVirtioDevice *dev) -{ - uint32_t features; - - features = qvirtio_get_features(dev); - features = features & ~(QVIRTIO_F_BAD_FEATURE | - (1u << VIRTIO_RING_F_INDIRECT_DESC) | - (1u << VIRTIO_RING_F_EVENT_IDX)); - qvirtio_set_features(dev, features); - - qvirtio_set_driver_ok(dev); -} - static void rx_test(QVirtioDevice *dev, QGuestAllocator *alloc, QVirtQueue *vq, int socket) @@ -194,128 +132,114 @@ static void rx_stop_cont_test(QVirtioDevice *dev, guest_free(alloc, req_addr); } -static void send_recv_test(QVirtioDevice *dev, - QGuestAllocator *alloc, QVirtQueue *rvq, - QVirtQueue *tvq, int socket) +static void send_recv_test(void *obj, void *data, QGuestAllocator *t_alloc) { - rx_test(dev, alloc, rvq, socket); - tx_test(dev, alloc, tvq, socket); + QVirtioNet *net_if = obj; + QVirtioDevice *dev = net_if->vdev; + QVirtQueue *rx = net_if->rx; + QVirtQueue *tx = net_if->tx; + int *sv = data; + + rx_test(dev, t_alloc, rx, sv[0]); + tx_test(dev, t_alloc, tx, sv[0]); } -static void stop_cont_test(QVirtioDevice *dev, - QGuestAllocator *alloc, QVirtQueue *rvq, - QVirtQueue *tvq, int socket) +static void stop_cont_test(void *obj, void *data, QGuestAllocator *t_alloc) { - rx_stop_cont_test(dev, alloc, rvq, socket); + QVirtioNet *net_if = obj; + QVirtioDevice *dev = net_if->vdev; + QVirtQueue *rx = net_if->rx; + int *sv = data; + + rx_stop_cont_test(dev, t_alloc, rx, sv[0]); } -static void pci_basic(gconstpointer data) -{ - QVirtioPCIDevice *dev; - QOSState *qs; - QVirtQueuePCI *tx, *rx; - void (*func) (QVirtioDevice *dev, - QGuestAllocator *alloc, - QVirtQueue *rvq, - QVirtQueue *tvq, - int socket) = data; - int sv[2], ret; - - ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sv); - g_assert_cmpint(ret, !=, -1); - - qs = pci_test_start("-netdev socket,fd=%d,id=hs0 -device " - "virtio-net-pci,netdev=hs0", sv[1]); - dev = virtio_net_pci_init(qs->pcibus, PCI_SLOT); - - rx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, &qs->alloc, 0); - tx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, &qs->alloc, 1); - - driver_init(&dev->vdev); - func(&dev->vdev, &qs->alloc, &rx->vq, &tx->vq, sv[0]); - - /* End test */ - close(sv[0]); - qvirtqueue_cleanup(dev->vdev.bus, &tx->vq, &qs->alloc); - qvirtqueue_cleanup(dev->vdev.bus, &rx->vq, &qs->alloc); - qvirtio_pci_device_disable(dev); - g_free(dev->pdev); - g_free(dev); - qtest_shutdown(qs); -} - -static void large_tx(gconstpointer data) -{ - QVirtioPCIDevice *dev; - QOSState *qs; - QVirtQueuePCI *tx, *rx; - QVirtQueue *vq; - uint64_t req_addr; - uint32_t free_head; - size_t alloc_size = (size_t)data / 64; - int i; - - qs = pci_test_start("-netdev hubport,id=hp0,hubid=0 " - "-device virtio-net-pci,netdev=hp0"); - dev = virtio_net_pci_init(qs->pcibus, PCI_SLOT); - - rx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); - tx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 1); - - driver_init(&dev->vdev); - vq = &tx->vq; - - /* Bypass the limitation by pointing several descriptors to a single - * smaller area */ - req_addr = guest_alloc(qs->alloc, alloc_size); - free_head = qvirtqueue_add(vq, req_addr, alloc_size, false, true); - - for (i = 0; i < 64; i++) { - qvirtqueue_add(vq, req_addr, alloc_size, false, i != 63); - } - qvirtqueue_kick(&dev->vdev, vq, free_head); - - qvirtio_wait_used_elem(&dev->vdev, vq, free_head, NULL, - QVIRTIO_NET_TIMEOUT_US); - - qvirtqueue_cleanup(dev->vdev.bus, &tx->vq, qs->alloc); - qvirtqueue_cleanup(dev->vdev.bus, &rx->vq, qs->alloc); - qvirtio_pci_device_disable(dev); - g_free(dev->pdev); - g_free(dev); - qtest_shutdown(qs); -} #endif -static void hotplug(void) +static void hotplug(void *obj, void *data, QGuestAllocator *t_alloc) { const char *arch = qtest_get_arch(); - qtest_start("-device virtio-net-pci"); - qtest_qmp_device_add("virtio-net-pci", "net1", "{'addr': %s}", stringify(PCI_SLOT_HP)); if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { qpci_unplug_acpi_device_test("net1", PCI_SLOT_HP); } - - test_end(); } -int main(int argc, char **argv) +static void virtio_net_test_cleanup(void *sockets) { - g_test_init(&argc, &argv, NULL); -#ifndef _WIN32 - qtest_add_data_func("/virtio/net/pci/basic", send_recv_test, pci_basic); - qtest_add_data_func("/virtio/net/pci/rx_stop_cont", - stop_cont_test, pci_basic); - qtest_add_data_func("/virtio/net/pci/large_tx_uint_max", - (gconstpointer)UINT_MAX, large_tx); - qtest_add_data_func("/virtio/net/pci/large_tx_net_bufsize", - (gconstpointer)NET_BUFSIZE, large_tx); -#endif - qtest_add_func("/virtio/net/pci/hotplug", hotplug); + int *sv = sockets; - return g_test_run(); + close(sv[0]); + qos_invalidate_command_line(); + close(sv[1]); + g_free(sv); } + +static void *virtio_net_test_setup(GString *cmd_line, void *arg) +{ + int ret; + int *sv = g_new(int, 2); + + ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sv); + g_assert_cmpint(ret, !=, -1); + + g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ", sv[1]); + + g_test_queue_destroy(virtio_net_test_cleanup, sv); + return sv; +} + +static void large_tx(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtioNet *dev = obj; + QVirtQueue *vq = dev->queues[1]; + uint64_t req_addr; + uint32_t free_head; + size_t alloc_size = (size_t)data / 64; + int i; + + /* Bypass the limitation by pointing several descriptors to a single + * smaller area */ + req_addr = guest_alloc(t_alloc, alloc_size); + free_head = qvirtqueue_add(vq, req_addr, alloc_size, false, true); + + for (i = 0; i < 64; i++) { + qvirtqueue_add(vq, req_addr, alloc_size, false, i != 63); + } + qvirtqueue_kick(dev->vdev, vq, free_head); + + qvirtio_wait_used_elem(dev->vdev, vq, free_head, NULL, + QVIRTIO_NET_TIMEOUT_US); + guest_free(t_alloc, req_addr); +} + +static void *virtio_net_test_setup_nosocket(GString *cmd_line, void *arg) +{ + g_string_append(cmd_line, " -netdev hubport,hubid=0,id=hs0 "); + return arg; +} + +static void register_virtio_net_test(void) +{ + QOSGraphTestOptions opts = { + .before = virtio_net_test_setup, + }; + + qos_add_test("hotplug", "virtio-pci", hotplug, &opts); +#ifndef _WIN32 + qos_add_test("basic", "virtio-net", send_recv_test, &opts); + qos_add_test("rx_stop_cont", "virtio-net", stop_cont_test, &opts); +#endif + + /* These tests do not need a loopback backend. */ + opts.before = virtio_net_test_setup_nosocket; + opts.arg = (gpointer)UINT_MAX; + qos_add_test("large_tx/uint_max", "virtio-net", large_tx, &opts); + opts.arg = (gpointer)NET_BUFSIZE; + qos_add_test("large_tx/net_bufsize", "virtio-net", large_tx, &opts); +} + +libqos_init(register_virtio_net_test); From 6bd4a6d4b867fab1f94f9fe3d1b19d02ac3d684b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 22 Oct 2018 12:37:21 +0200 Subject: [PATCH 38/57] tests/libqos: support multiqueue for virtio-net Initialize the additional virtqueues if they are supported. This is needed to switch vhost-user-test's multiqueue test to the virtio-net qgraph. Signed-off-by: Paolo Bonzini --- tests/libqos/virtio-net.c | 21 +++++++++++++++++---- tests/libqos/virtio-net.h | 4 ++-- tests/virtio-net-test.c | 6 +++--- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/tests/libqos/virtio-net.c b/tests/libqos/virtio-net.c index 3ddfbdc419..61c56170e9 100644 --- a/tests/libqos/virtio-net.c +++ b/tests/libqos/virtio-net.c @@ -27,14 +27,19 @@ static QGuestAllocator *alloc; static void virtio_net_cleanup(QVirtioNet *interface) { - qvirtqueue_cleanup(interface->vdev->bus, interface->rx, alloc); - qvirtqueue_cleanup(interface->vdev->bus, interface->tx, alloc); + int i; + + for (i = 0; i < interface->n_queues; i++) { + qvirtqueue_cleanup(interface->vdev->bus, interface->queues[i], alloc); + } + g_free(interface->queues); } static void virtio_net_setup(QVirtioNet *interface) { QVirtioDevice *vdev = interface->vdev; uint64_t features; + int i; features = qvirtio_get_features(vdev); features &= ~(QVIRTIO_F_BAD_FEATURE | @@ -42,8 +47,16 @@ static void virtio_net_setup(QVirtioNet *interface) (1u << VIRTIO_RING_F_EVENT_IDX)); qvirtio_set_features(vdev, features); - interface->rx = qvirtqueue_setup(vdev, alloc, 0); - interface->tx = qvirtqueue_setup(vdev, alloc, 1); + if (features & (1u << VIRTIO_NET_F_MQ)) { + interface->n_queues = qvirtio_config_readw(vdev, 8) * 2; + } else { + interface->n_queues = 2; + } + + interface->queues = g_new(QVirtQueue *, interface->n_queues); + for (i = 0; i < interface->n_queues; i++) { + interface->queues[i] = qvirtqueue_setup(vdev, alloc, i); + } qvirtio_set_driver_ok(vdev); } diff --git a/tests/libqos/virtio-net.h b/tests/libqos/virtio-net.h index e6905cd82e..28238a1b20 100644 --- a/tests/libqos/virtio-net.h +++ b/tests/libqos/virtio-net.h @@ -26,8 +26,8 @@ typedef struct QVirtioNetDevice QVirtioNetDevice; struct QVirtioNet { QVirtioDevice *vdev; - QVirtQueue *rx; - QVirtQueue *tx; + int n_queues; + QVirtQueue **queues; }; struct QVirtioNetPCI { diff --git a/tests/virtio-net-test.c b/tests/virtio-net-test.c index 2d3630ca79..c58e670e2f 100644 --- a/tests/virtio-net-test.c +++ b/tests/virtio-net-test.c @@ -136,8 +136,8 @@ static void send_recv_test(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtioNet *net_if = obj; QVirtioDevice *dev = net_if->vdev; - QVirtQueue *rx = net_if->rx; - QVirtQueue *tx = net_if->tx; + QVirtQueue *rx = net_if->queues[0]; + QVirtQueue *tx = net_if->queues[1]; int *sv = data; rx_test(dev, t_alloc, rx, sv[0]); @@ -148,7 +148,7 @@ static void stop_cont_test(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtioNet *net_if = obj; QVirtioDevice *dev = net_if->vdev; - QVirtQueue *rx = net_if->rx; + QVirtQueue *rx = net_if->queues[0]; int *sv = data; rx_stop_cont_test(dev, t_alloc, rx, sv[0]); From 0f9fe58009cfecc263d1eaf1146348abae0e40db Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 15 Nov 2018 10:50:15 +0100 Subject: [PATCH 39/57] vhost-user-test: always use 256 MiB of guest memory Some tests are using a small amount of RAM for the guest (2 MiB) in order to save host memory, others are using 512 MiB. However, pSeries machines only support multiples of 256 MiB. Using 256 MiB of memory does not use more host memory than now, even for the migration test that starts two guests, and it allows running the test on pSeries too. This of course is not optimal, it would be nice to let the qgraph machine object judge how much memory to provide. This is left for future work, together with a more generic framework that wraps the QEMU command line. Signed-off-by: Paolo Bonzini --- tests/vhost-user-test.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c index b1414e2b44..2a67a99d50 100644 --- a/tests/vhost-user-test.c +++ b/tests/vhost-user-test.c @@ -704,7 +704,7 @@ static void test_read_guest_mem(const void *arg) "read-guest-memfd" : "read-guest-mem"); test_server_listen(server); - qemu_cmd = get_qemu_cmd(server, 512, memfd, "", ""); + qemu_cmd = get_qemu_cmd(server, 256, memfd, "", ""); s = qtest_start(qemu_cmd); g_free(qemu_cmd); @@ -739,7 +739,7 @@ static void test_migrate(void) test_server_listen(s); test_server_listen(dest); - cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, "", ""); + cmd = get_qemu_cmd(s, 256, TEST_MEMFD_AUTO, "", ""); from = qtest_start(cmd); g_free(cmd); @@ -749,10 +749,10 @@ static void test_migrate(void) } size = get_log_size(s); - g_assert_cmpint(size, ==, (2 * 1024 * 1024) / (VHOST_LOG_PAGE * 8)); + g_assert_cmpint(size, ==, (256 * 1024 * 1024) / (VHOST_LOG_PAGE * 8)); tmp = g_strdup_printf(" -incoming %s", uri); - cmd = get_qemu_cmd(dest, 2, TEST_MEMFD_AUTO, "", tmp); + cmd = get_qemu_cmd(dest, 256, TEST_MEMFD_AUTO, "", tmp); g_free(tmp); to = qtest_init(cmd); g_free(cmd); @@ -863,7 +863,7 @@ static void test_reconnect_subprocess(void) char *cmd; g_thread_new("connect", connect_thread, s); - cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, ",server", ""); + cmd = get_qemu_cmd(s, 256, TEST_MEMFD_AUTO, ",server", ""); qtest_start(cmd); g_free(cmd); @@ -908,7 +908,7 @@ static void test_connect_fail_subprocess(void) s->test_fail = true; g_thread_new("connect", connect_thread, s); - cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, ",server", ""); + cmd = get_qemu_cmd(s, 256, TEST_MEMFD_AUTO, ",server", ""); qtest_start(cmd); g_free(cmd); @@ -941,7 +941,7 @@ static void test_flags_mismatch_subprocess(void) s->test_flags = TEST_FLAGS_DISCONNECT; g_thread_new("connect", connect_thread, s); - cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, ",server", ""); + cmd = get_qemu_cmd(s, 256, TEST_MEMFD_AUTO, ",server", ""); qtest_start(cmd); g_free(cmd); @@ -982,14 +982,14 @@ static void test_multiqueue(void) cmd = g_strdup_printf( QEMU_CMD_MEMFD QEMU_CMD_CHR QEMU_CMD_NETDEV ",queues=%d " "-device virtio-net-pci,netdev=net0,mq=on,vectors=%d", - 512, 512, s->chr_name, + 256, 256, s->chr_name, s->socket_path, "", s->chr_name, s->queues, s->queues * 2 + 2); } else { cmd = g_strdup_printf( QEMU_CMD_MEM QEMU_CMD_CHR QEMU_CMD_NETDEV ",queues=%d " "-device virtio-net-pci,netdev=net0,mq=on,vectors=%d", - 512, 512, s->mem_path, s->chr_name, + 256, 256, s->mem_path, s->chr_name, s->socket_path, "", s->chr_name, s->queues, s->queues * 2 + 2); } From a3ebd6e00aed2945784d24f8764b27b8bbb3fd3a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 15 Nov 2018 10:08:28 +0100 Subject: [PATCH 40/57] qos-test: vhost-user test node Convert tests/virtio-net-test in qgraph test node, virtio-net-test. This test consumes a virtio-net interface and checks that its function return the expected values. Note that this test does not allocate any virtio-net structure, it's all done by the qtest walking graph mechanism. Nevertheless, vhost-user-test is a bit more complex than the other tests, because it requires more complicated setup of back-ends and thus almost each test has a slightly different opts.before function. Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 5 +- tests/vhost-user-test.c | 403 ++++++++++++++++------------------------ 2 files changed, 163 insertions(+), 245 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index f45d1156f2..a0b42c9ca5 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -204,7 +204,6 @@ check-qtest-i386-$(CONFIG_USB_XHCI_NEC) += tests/usb-hcd-xhci-test$(EXESUF) check-qtest-i386-y += tests/cpu-plug-test$(EXESUF) check-qtest-i386-y += tests/q35-test$(EXESUF) check-qtest-i386-y += tests/vmgenid-test$(EXESUF) -check-qtest-i386-$(CONFIG_VHOST_NET_USER) += tests/vhost-user-test$(EXESUF) check-qtest-i386-$(CONFIG_TPM_CRB) += tests/tpm-crb-swtpm-test$(EXESUF) check-qtest-i386-$(CONFIG_TPM_CRB) += tests/tpm-crb-test$(EXESUF) check-qtest-i386-$(CONFIG_TPM_TIS) += tests/tpm-tis-swtpm-test$(EXESUF) @@ -760,6 +759,7 @@ qos-test-obj-y += tests/libqos/x86_64_pc-machine.o qos-test-obj-y += tests/e1000e-test.o qos-test-obj-y += tests/pci-test.o qos-test-obj-y += tests/sdhci-test.o +qos-test-obj-$(CONFIG_VHOST_NET_USER) += tests/vhost-user-test.o $(chardev-obj-y) $(test-io-obj-y) qos-test-obj-y += tests/virtio-test.o qos-test-obj-$(CONFIG_VIRTFS) += tests/virtio-9p-test.o qos-test-obj-y += tests/virtio-blk-test.o @@ -835,9 +835,6 @@ tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o $(libqos-usb-obj-y) tests/cpu-plug-test$(EXESUF): tests/cpu-plug-test.o tests/migration-test$(EXESUF): tests/migration-test.o tests/test-announce-self$(EXESUF): tests/test-announce-self.o -tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o $(test-util-obj-y) \ - $(qtest-obj-y) $(test-io-obj-y) $(libqos-virtio-obj-y) $(libqos-pc-obj-y) \ - $(chardev-obj-y) tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o $(test-util-obj-y) tests/test-keyval$(EXESUF): tests/test-keyval.o $(test-util-obj-y) $(test-qapi-obj-y) diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c index 2a67a99d50..92dfeeddf4 100644 --- a/tests/vhost-user-test.c +++ b/tests/vhost-user-test.c @@ -41,8 +41,7 @@ #define QEMU_CMD_MEMFD " -m %d -object memory-backend-memfd,id=mem,size=%dM," \ " -numa node,memdev=mem" #define QEMU_CMD_CHR " -chardev socket,id=%s,path=%s%s" -#define QEMU_CMD_NETDEV " -netdev vhost-user,id=net0,chardev=%s,vhostforce" -#define QEMU_CMD_NET " -device virtio-net-pci,netdev=net0" +#define QEMU_CMD_NETDEV " -netdev vhost-user,id=hs0,chardev=%s,vhostforce" #define HUGETLBFS_MAGIC 0x958458f6 @@ -136,13 +135,9 @@ enum { }; typedef struct TestServer { - QPCIBus *bus; - QVirtioPCIDevice *dev; - QVirtQueue *vq[VHOST_MAX_VIRTQUEUES]; gchar *socket_path; gchar *mig_path; gchar *chr_name; - const gchar *mem_path; gchar *tmpfs; CharBackend chr; int fds_num; @@ -158,9 +153,9 @@ typedef struct TestServer { bool test_fail; int test_flags; int queues; - QGuestAllocator alloc; } TestServer; +static const char *init_hugepagefs(void); static TestServer *test_server_new(const gchar *name); static void test_server_free(TestServer *server); static void test_server_listen(TestServer *server); @@ -171,66 +166,30 @@ enum test_memfd { TEST_MEMFD_NO, }; -static char *get_qemu_cmd(TestServer *s, - int mem, enum test_memfd memfd, - const char *chr_opts, const char *extra) +static void append_vhost_opts(TestServer *s, GString *cmd_line, + const char *chr_opts) { - if (memfd == TEST_MEMFD_AUTO && qemu_memfd_check(0)) { - memfd = TEST_MEMFD_YES; + g_string_append_printf(cmd_line, QEMU_CMD_CHR QEMU_CMD_NETDEV, + s->chr_name, s->socket_path, + chr_opts, s->chr_name); +} + +static void append_mem_opts(TestServer *server, GString *cmd_line, + int size, enum test_memfd memfd) +{ + if (memfd == TEST_MEMFD_AUTO) { + memfd = qemu_memfd_check(0) ? TEST_MEMFD_YES : TEST_MEMFD_NO; } if (memfd == TEST_MEMFD_YES) { - return g_strdup_printf(QEMU_CMD_MEMFD QEMU_CMD_CHR - QEMU_CMD_NETDEV QEMU_CMD_NET "%s", mem, mem, - s->chr_name, s->socket_path, - chr_opts, s->chr_name, extra); + g_string_append_printf(cmd_line, QEMU_CMD_MEMFD, size, size); } else { - return g_strdup_printf(QEMU_CMD_MEM QEMU_CMD_CHR - QEMU_CMD_NETDEV QEMU_CMD_NET "%s", mem, mem, - s->mem_path, s->chr_name, s->socket_path, - chr_opts, s->chr_name, extra); + const char *root = init_hugepagefs() ? : server->tmpfs; + + g_string_append_printf(cmd_line, QEMU_CMD_MEM, size, size, root); } } -static void init_virtio_dev(QTestState *qts, TestServer *s, uint32_t features_mask) -{ - uint32_t features; - int i; - - s->bus = qpci_new_pc(qts, NULL); - g_assert_nonnull(s->bus); - - s->dev = qvirtio_pci_device_find(s->bus, VIRTIO_ID_NET); - g_assert_nonnull(s->dev); - - qvirtio_pci_device_enable(s->dev); - qvirtio_start_device(&s->dev->vdev); - - pc_alloc_init(&s->alloc, qts, 0); - - for (i = 0; i < s->queues * 2; i++) { - s->vq[i] = qvirtqueue_setup(&s->dev->vdev, &s->alloc, i); - } - - features = qvirtio_get_features(&s->dev->vdev); - features = features & features_mask; - qvirtio_set_features(&s->dev->vdev, features); - - qvirtio_set_driver_ok(&s->dev->vdev); -} - -static void uninit_virtio_dev(TestServer *s) -{ - int i; - - for (i = 0; i < s->queues * 2; i++) { - qvirtqueue_cleanup(s->dev->vdev.bus, s->vq[i], &s->alloc); - } - alloc_destroy(&s->alloc); - - qvirtio_pci_device_free(s->dev); -} - static bool wait_for_fds(TestServer *s) { gint64 end_time; @@ -465,17 +424,21 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) static const char *init_hugepagefs(void) { #ifdef CONFIG_LINUX + static const char *hugepagefs; const char *path = getenv("QTEST_HUGETLBFS_PATH"); struct statfs fs; int ret; + if (hugepagefs) { + return hugepagefs; + } if (!path) { return NULL; } if (access(path, R_OK | W_OK | X_OK)) { g_test_message("access on path (%s): %s\n", path, strerror(errno)); - abort(); + g_test_fail(); return NULL; } @@ -485,17 +448,18 @@ static const char *init_hugepagefs(void) if (ret != 0) { g_test_message("statfs on path (%s): %s\n", path, strerror(errno)); - abort(); + g_test_fail(); return NULL; } if (fs.f_type != HUGETLBFS_MAGIC) { g_test_message("Warning: path not on HugeTLBFS: %s\n", path); - abort(); + g_test_fail(); return NULL; } - return path; + hugepagefs = path; + return hugepagefs; #else return NULL; #endif @@ -520,7 +484,6 @@ static TestServer *test_server_new(const gchar *name) g_assert(tmpfs); server->tmpfs = g_strdup(tmpfs); - server->mem_path = init_hugepagefs() ? : server->tmpfs; server->socket_path = g_strdup_printf("%s/%s.sock", tmpfs, name); server->mig_path = g_strdup_printf("%s/%s.mig", tmpfs, name); server->chr_name = g_strdup_printf("chr-%s", name); @@ -598,8 +561,6 @@ static void test_server_free(TestServer *server) } g_free(server->chr_name); - g_assert(server->bus); - qpci_free_pc(server->bus); g_main_loop_unref(server->loop); g_main_context_unref(server->context); @@ -693,70 +654,79 @@ GSourceFuncs test_migrate_source_funcs = { .check = test_migrate_source_check, }; -static void test_read_guest_mem(const void *arg) +static void vhost_user_test_cleanup(void *s) { - enum test_memfd memfd = GPOINTER_TO_INT(arg); - TestServer *server = NULL; - char *qemu_cmd = NULL; - QTestState *s = NULL; + TestServer *server = s; - server = test_server_new(memfd == TEST_MEMFD_YES ? - "read-guest-memfd" : "read-guest-mem"); - test_server_listen(server); - - qemu_cmd = get_qemu_cmd(server, 256, memfd, "", ""); - - s = qtest_start(qemu_cmd); - g_free(qemu_cmd); - - init_virtio_dev(global_qtest, server, 1u << VIRTIO_NET_F_MAC); - - if (!wait_for_fds(server)) { - goto exit; - } - - read_guest_mem_server(global_qtest, server); - -exit: - uninit_virtio_dev(server); - - qtest_quit(s); + qos_invalidate_command_line(); test_server_free(server); } -static void test_migrate(void) +static void *vhost_user_test_setup(GString *cmd_line, void *arg) { - TestServer *s = test_server_new("src"); + TestServer *server = test_server_new("vhost-user-test"); + test_server_listen(server); + + append_mem_opts(server, cmd_line, 256, TEST_MEMFD_AUTO); + append_vhost_opts(server, cmd_line, ""); + + g_test_queue_destroy(vhost_user_test_cleanup, server); + + return server; +} + +static void *vhost_user_test_setup_memfd(GString *cmd_line, void *arg) +{ + TestServer *server = test_server_new("vhost-user-test"); + test_server_listen(server); + + append_mem_opts(server, cmd_line, 256, TEST_MEMFD_YES); + append_vhost_opts(server, cmd_line, ""); + + g_test_queue_destroy(vhost_user_test_cleanup, server); + + return server; +} + +static void test_read_guest_mem(void *obj, void *arg, QGuestAllocator *alloc) +{ + TestServer *server = arg; + + if (!wait_for_fds(server)) { + return; + } + + read_guest_mem_server(global_qtest, server); +} + +static void test_migrate(void *obj, void *arg, QGuestAllocator *alloc) +{ + TestServer *s = arg; TestServer *dest = test_server_new("dest"); + GString *dest_cmdline = g_string_new(qos_get_current_command_line()); char *uri = g_strdup_printf("%s%s", "unix:", dest->mig_path); - QTestState *from, *to; + QTestState *to; GSource *source; - gchar *cmd, *tmp; QDict *rsp; guint8 *log; guint64 size; - test_server_listen(s); - test_server_listen(dest); - - cmd = get_qemu_cmd(s, 256, TEST_MEMFD_AUTO, "", ""); - from = qtest_start(cmd); - g_free(cmd); - - init_virtio_dev(from, s, 1u << VIRTIO_NET_F_MAC); if (!wait_for_fds(s)) { - goto exit; + return; } size = get_log_size(s); g_assert_cmpint(size, ==, (256 * 1024 * 1024) / (VHOST_LOG_PAGE * 8)); - tmp = g_strdup_printf(" -incoming %s", uri); - cmd = get_qemu_cmd(dest, 256, TEST_MEMFD_AUTO, "", tmp); - g_free(tmp); - to = qtest_init(cmd); - g_free(cmd); - init_virtio_dev(to, dest, 1u << VIRTIO_NET_F_MAC); + test_server_listen(dest); + g_string_append_printf(dest_cmdline, " -incoming %s", uri); + append_mem_opts(dest, dest_cmdline, 256, TEST_MEMFD_AUTO); + append_vhost_opts(dest, dest_cmdline, ""); + to = qtest_init(dest_cmdline->str); + + /* This would be where you call qos_allocate_objects(to, NULL), if you want + * to talk to the QVirtioNet object on the destination. + */ source = g_source_new(&test_migrate_source_funcs, sizeof(TestMigrateSource)); @@ -797,18 +767,11 @@ static void test_migrate(void) g_assert(wait_for_fds(dest)); read_guest_mem_server(to, dest); - uninit_virtio_dev(dest); - qtest_quit(to); - g_source_destroy(source); g_source_unref(source); -exit: - uninit_virtio_dev(s); - + qtest_quit(to); test_server_free(dest); - qtest_quit(from); - test_server_free(s); g_free(uri); } @@ -856,20 +819,26 @@ connect_thread(gpointer data) return NULL; } -static void test_reconnect_subprocess(void) +static void *vhost_user_test_setup_reconnect(GString *cmd_line, void *arg) { TestServer *s = test_server_new("reconnect"); - GSource *src; - char *cmd; g_thread_new("connect", connect_thread, s); - cmd = get_qemu_cmd(s, 256, TEST_MEMFD_AUTO, ",server", ""); - qtest_start(cmd); - g_free(cmd); + append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO); + append_vhost_opts(s, cmd_line, ",server"); + + g_test_queue_destroy(vhost_user_test_cleanup, s); + + return s; +} + +static void test_reconnect(void *obj, void *arg, QGuestAllocator *alloc) +{ + TestServer *s = arg; + GSource *src; - init_virtio_dev(global_qtest, s, 1u << VIRTIO_NET_F_MAC); if (!wait_for_fds(s)) { - goto exit; + return; } wait_for_rings_started(s, 2); @@ -883,159 +852,111 @@ static void test_reconnect_subprocess(void) g_source_unref(src); g_assert(wait_for_fds(s)); wait_for_rings_started(s, 2); - -exit: - uninit_virtio_dev(s); - - qtest_end(); - test_server_free(s); - return; } -static void test_reconnect(void) -{ - gchar *path = g_strdup_printf("/%s/vhost-user/reconnect/subprocess", - qtest_get_arch()); - g_test_trap_subprocess(path, 0, 0); - g_test_trap_assert_passed(); - g_free(path); -} - -static void test_connect_fail_subprocess(void) +static void *vhost_user_test_setup_connect_fail(GString *cmd_line, void *arg) { TestServer *s = test_server_new("connect-fail"); - char *cmd; s->test_fail = true; + g_thread_new("connect", connect_thread, s); - cmd = get_qemu_cmd(s, 256, TEST_MEMFD_AUTO, ",server", ""); - qtest_start(cmd); - g_free(cmd); + append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO); + append_vhost_opts(s, cmd_line, ",server"); - init_virtio_dev(global_qtest, s, 1u << VIRTIO_NET_F_MAC); - if (!wait_for_fds(s)) { - goto exit; - } - wait_for_rings_started(s, 2); + g_test_queue_destroy(vhost_user_test_cleanup, s); -exit: - uninit_virtio_dev(s); - - qtest_end(); - test_server_free(s); + return s; } -static void test_connect_fail(void) -{ - gchar *path = g_strdup_printf("/%s/vhost-user/connect-fail/subprocess", - qtest_get_arch()); - g_test_trap_subprocess(path, 0, 0); - g_test_trap_assert_passed(); - g_free(path); -} - -static void test_flags_mismatch_subprocess(void) +static void *vhost_user_test_setup_flags_mismatch(GString *cmd_line, void *arg) { TestServer *s = test_server_new("flags-mismatch"); - char *cmd; s->test_flags = TEST_FLAGS_DISCONNECT; - g_thread_new("connect", connect_thread, s); - cmd = get_qemu_cmd(s, 256, TEST_MEMFD_AUTO, ",server", ""); - qtest_start(cmd); - g_free(cmd); - init_virtio_dev(global_qtest, s, 1u << VIRTIO_NET_F_MAC); + g_thread_new("connect", connect_thread, s); + append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO); + append_vhost_opts(s, cmd_line, ",server"); + + g_test_queue_destroy(vhost_user_test_cleanup, s); + + return s; +} + +static void test_vhost_user_started(void *obj, void *arg, QGuestAllocator *alloc) +{ + TestServer *s = arg; + if (!wait_for_fds(s)) { - goto exit; + return; } wait_for_rings_started(s, 2); - -exit: - uninit_virtio_dev(s); - - qtest_end(); - test_server_free(s); } -static void test_flags_mismatch(void) +static void *vhost_user_test_setup_multiqueue(GString *cmd_line, void *arg) { - gchar *path = g_strdup_printf("/%s/vhost-user/flags-mismatch/subprocess", - qtest_get_arch()); - g_test_trap_subprocess(path, 0, 0); - g_test_trap_assert_passed(); - g_free(path); -} + TestServer *s = vhost_user_test_setup(cmd_line, arg); - -static void test_multiqueue(void) -{ - TestServer *s = test_server_new("mq"); - char *cmd; - uint32_t features_mask = ~(QVIRTIO_F_BAD_FEATURE | - (1u << VIRTIO_RING_F_INDIRECT_DESC) | - (1u << VIRTIO_RING_F_EVENT_IDX)); s->queues = 2; - test_server_listen(s); + g_string_append_printf(cmd_line, + " -set netdev.hs0.queues=%d" + " -global virtio-net-pci.vectors=%d", + s->queues, s->queues * 2 + 2); - if (qemu_memfd_check(0)) { - cmd = g_strdup_printf( - QEMU_CMD_MEMFD QEMU_CMD_CHR QEMU_CMD_NETDEV ",queues=%d " - "-device virtio-net-pci,netdev=net0,mq=on,vectors=%d", - 256, 256, s->chr_name, - s->socket_path, "", s->chr_name, - s->queues, s->queues * 2 + 2); - } else { - cmd = g_strdup_printf( - QEMU_CMD_MEM QEMU_CMD_CHR QEMU_CMD_NETDEV ",queues=%d " - "-device virtio-net-pci,netdev=net0,mq=on,vectors=%d", - 256, 256, s->mem_path, s->chr_name, - s->socket_path, "", s->chr_name, - s->queues, s->queues * 2 + 2); - } - qtest_start(cmd); - g_free(cmd); + return s; +} - init_virtio_dev(global_qtest, s, features_mask); +static void test_multiqueue(void *obj, void *arg, QGuestAllocator *alloc) +{ + TestServer *s = arg; wait_for_rings_started(s, s->queues * 2); - - uninit_virtio_dev(s); - - qtest_end(); - - test_server_free(s); } -int main(int argc, char **argv) +static void register_vhost_user_test(void) { - g_test_init(&argc, &argv, NULL); + QOSGraphTestOptions opts = { + .before = vhost_user_test_setup, + .subprocess = true, + }; - module_call_init(MODULE_INIT_QOM); qemu_add_opts(&qemu_chardev_opts); + qos_add_test("vhost-user/read-guest-mem/memfile", + "virtio-net", + test_read_guest_mem, &opts); + if (qemu_memfd_check(0)) { - qtest_add_data_func("/vhost-user/read-guest-mem/memfd", - GINT_TO_POINTER(TEST_MEMFD_YES), - test_read_guest_mem); + opts.before = vhost_user_test_setup_memfd; + qos_add_test("vhost-user/read-guest-mem/memfd", + "virtio-net", + test_read_guest_mem, &opts); } - qtest_add_data_func("/vhost-user/read-guest-mem/memfile", - GINT_TO_POINTER(TEST_MEMFD_NO), test_read_guest_mem); - qtest_add_func("/vhost-user/migrate", test_migrate); - qtest_add_func("/vhost-user/multiqueue", test_multiqueue); + + qos_add_test("vhost-user/migrate", + "virtio-net", + test_migrate, &opts); /* keeps failing on build-system since Aug 15 2017 */ if (getenv("QTEST_VHOST_USER_FIXME")) { - qtest_add_func("/vhost-user/reconnect/subprocess", - test_reconnect_subprocess); - qtest_add_func("/vhost-user/reconnect", test_reconnect); - qtest_add_func("/vhost-user/connect-fail/subprocess", - test_connect_fail_subprocess); - qtest_add_func("/vhost-user/connect-fail", test_connect_fail); - qtest_add_func("/vhost-user/flags-mismatch/subprocess", - test_flags_mismatch_subprocess); - qtest_add_func("/vhost-user/flags-mismatch", test_flags_mismatch); + opts.before = vhost_user_test_setup_reconnect; + qos_add_test("vhost-user/reconnect", "virtio-net", + test_reconnect, &opts); + + opts.before = vhost_user_test_setup_connect_fail; + qos_add_test("vhost-user/connect-fail", "virtio-net", + test_vhost_user_started, &opts); + + opts.before = vhost_user_test_setup_flags_mismatch; + qos_add_test("vhost-user/flags-mismatch", "virtio-net", + test_vhost_user_started, &opts); } - return g_test_run(); + opts.before = vhost_user_test_setup_multiqueue; + opts.edge.extra_device_opts = "mq=on"; + qos_add_test("vhost-user/multiqueue", + "virtio-net", + test_multiqueue, &opts); } +libqos_init(register_vhost_user_test); From 8f41a3a8229c1869d8705914cf4ac85846736268 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Tue, 31 Jul 2018 11:17:38 +0200 Subject: [PATCH 41/57] tests/libqos: virtio-scsi driver and interface nodes Add qgraph nodes for virtio-scsi-pci and virtio-scsi-device. Both nodes produce virtio-scsi, but virtio-scsi-pci receives a pci-bus and uses virtio-pci QOSGraphObject and its functions, while virtio-scsi-device receives a virtio and implements its own functions Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 1 + tests/libqos/virtio-scsi.c | 117 +++++++++++++++++++++++++++++++++++++ tests/libqos/virtio-scsi.h | 39 +++++++++++++ 3 files changed, 157 insertions(+) create mode 100644 tests/libqos/virtio-scsi.c create mode 100644 tests/libqos/virtio-scsi.h diff --git a/tests/Makefile.include b/tests/Makefile.include index a0b42c9ca5..48b7cf0b5d 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -743,6 +743,7 @@ qos-test-obj-y += tests/libqos/virtio-balloon.o qos-test-obj-y += tests/libqos/virtio-blk.o qos-test-obj-y += tests/libqos/virtio-net.o qos-test-obj-y += tests/libqos/virtio-rng.o +qos-test-obj-y += tests/libqos/virtio-scsi.o qos-test-obj-y += tests/libqos/virtio-serial.o # Machines diff --git a/tests/libqos/virtio-scsi.c b/tests/libqos/virtio-scsi.c new file mode 100644 index 0000000000..482684d0bc --- /dev/null +++ b/tests/libqos/virtio-scsi.c @@ -0,0 +1,117 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "standard-headers/linux/virtio_ids.h" +#include "libqos/qgraph.h" +#include "libqos/virtio-scsi.h" + +/* virtio-scsi-device */ +static void *qvirtio_scsi_get_driver(QVirtioSCSI *v_scsi, + const char *interface) +{ + if (!g_strcmp0(interface, "virtio-scsi")) { + return v_scsi; + } + if (!g_strcmp0(interface, "virtio")) { + return v_scsi->vdev; + } + + fprintf(stderr, "%s not present in virtio-scsi-device\n", interface); + g_assert_not_reached(); +} + +static void *qvirtio_scsi_device_get_driver(void *object, + const char *interface) +{ + QVirtioSCSIDevice *v_scsi = object; + return qvirtio_scsi_get_driver(&v_scsi->scsi, interface); +} + +static void *virtio_scsi_device_create(void *virtio_dev, + QGuestAllocator *t_alloc, + void *addr) +{ + QVirtioSCSIDevice *virtio_bdevice = g_new0(QVirtioSCSIDevice, 1); + QVirtioSCSI *interface = &virtio_bdevice->scsi; + + interface->vdev = virtio_dev; + + virtio_bdevice->obj.get_driver = qvirtio_scsi_device_get_driver; + + return &virtio_bdevice->obj; +} + +/* virtio-scsi-pci */ +static void *qvirtio_scsi_pci_get_driver(void *object, + const char *interface) +{ + QVirtioSCSIPCI *v_scsi = object; + if (!g_strcmp0(interface, "pci-device")) { + return v_scsi->pci_vdev.pdev; + } + return qvirtio_scsi_get_driver(&v_scsi->scsi, interface); +} + +static void *virtio_scsi_pci_create(void *pci_bus, + QGuestAllocator *t_alloc, + void *addr) +{ + QVirtioSCSIPCI *virtio_spci = g_new0(QVirtioSCSIPCI, 1); + QVirtioSCSI *interface = &virtio_spci->scsi; + QOSGraphObject *obj = &virtio_spci->pci_vdev.obj; + + virtio_pci_init(&virtio_spci->pci_vdev, pci_bus, addr); + interface->vdev = &virtio_spci->pci_vdev.vdev; + + g_assert_cmphex(interface->vdev->device_type, ==, VIRTIO_ID_SCSI); + + obj->get_driver = qvirtio_scsi_pci_get_driver; + + return obj; +} + +static void virtio_scsi_register_nodes(void) +{ + QPCIAddress addr = { + .devfn = QPCI_DEVFN(4, 0), + }; + + QOSGraphEdgeOptions opts = { + .before_cmd_line = "-drive id=drv0,if=none,file=null-co://,format=raw", + .after_cmd_line = "-device scsi-hd,bus=vs0.0,drive=drv0", + }; + + /* virtio-scsi-device */ + opts.extra_device_opts = "id=vs0"; + qos_node_create_driver("virtio-scsi-device", + virtio_scsi_device_create); + qos_node_consumes("virtio-scsi-device", "virtio-bus", &opts); + qos_node_produces("virtio-scsi-device", "virtio-scsi"); + + /* virtio-scsi-pci */ + opts.extra_device_opts = "id=vs0,addr=04.0"; + add_qpci_address(&opts, &addr); + qos_node_create_driver("virtio-scsi-pci", virtio_scsi_pci_create); + qos_node_consumes("virtio-scsi-pci", "pci-bus", &opts); + qos_node_produces("virtio-scsi-pci", "pci-device"); + qos_node_produces("virtio-scsi-pci", "virtio-scsi"); +} + +libqos_init(virtio_scsi_register_nodes); diff --git a/tests/libqos/virtio-scsi.h b/tests/libqos/virtio-scsi.h new file mode 100644 index 0000000000..17a47beddc --- /dev/null +++ b/tests/libqos/virtio-scsi.h @@ -0,0 +1,39 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "libqos/qgraph.h" +#include "libqos/virtio.h" +#include "libqos/virtio-pci.h" + +typedef struct QVirtioSCSI QVirtioSCSI; +typedef struct QVirtioSCSIPCI QVirtioSCSIPCI; +typedef struct QVirtioSCSIDevice QVirtioSCSIDevice; + +struct QVirtioSCSI { + QVirtioDevice *vdev; +}; + +struct QVirtioSCSIPCI { + QVirtioPCIDevice pci_vdev; + QVirtioSCSI scsi; +}; + +struct QVirtioSCSIDevice { + QOSGraphObject obj; + QVirtioSCSI scsi; +}; From 4e20079869f51303c817ebf7a4dd483f6c76905c Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Tue, 31 Jul 2018 11:19:06 +0200 Subject: [PATCH 42/57] qos-test: virtio-scsi test node Convert tests/virtio-scsi-test in qgraph test node, virtio-scsi-test. This test consumes a virtio-scsi interface and checks that its function return the expected values. Some functions are implemented only for virtio-scsi-pci, so they don't consume virtio-scsi, but virtio-scsi-pci Note that this test does not allocate any virtio-scsi structure, it's all done by the qtest walking graph mechanism Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 4 +- tests/virtio-scsi-test.c | 145 ++++++++++++++++----------------------- 2 files changed, 61 insertions(+), 88 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index 48b7cf0b5d..09483d0c39 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -153,8 +153,6 @@ check-qtest-generic-y += tests/cdrom-test$(EXESUF) check-qtest-ipack-y += tests/ipoctal232-test$(EXESUF) -check-qtest-virtio-y += tests/virtio-scsi-test$(EXESUF) - check-qtest-pci-y += tests/e1000-test$(EXESUF) check-qtest-pci-$(CONFIG_RTL8139_PCI) += tests/rtl8139-test$(EXESUF) check-qtest-pci-$(CONFIG_PCNET_PCI) += tests/pcnet-test$(EXESUF) @@ -766,6 +764,7 @@ qos-test-obj-$(CONFIG_VIRTFS) += tests/virtio-9p-test.o qos-test-obj-y += tests/virtio-blk-test.o qos-test-obj-y += tests/virtio-net-test.o qos-test-obj-y += tests/virtio-rng-test.o +qos-test-obj-y += tests/virtio-scsi-test.o qos-test-obj-y += tests/virtio-serial-test.o check-unit-y += tests/test-qgraph$(EXESUF) @@ -813,7 +812,6 @@ tests/ne2000-test$(EXESUF): tests/ne2000-test.o tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y) tests/virtio-ccw-test$(EXESUF): tests/virtio-ccw-test.o -tests/virtio-scsi-test$(EXESUF): tests/virtio-scsi-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-scsi-test.c b/tests/virtio-scsi-test.c index 9298aa8877..162b31c88d 100644 --- a/tests/virtio-scsi-test.c +++ b/tests/virtio-scsi-test.c @@ -18,6 +18,8 @@ #include "standard-headers/linux/virtio_ids.h" #include "standard-headers/linux/virtio_pci.h" #include "standard-headers/linux/virtio_scsi.h" +#include "libqos/virtio-scsi.h" +#include "libqos/qgraph.h" #define PCI_SLOT 0x02 #define PCI_FN 0x00 @@ -27,55 +29,28 @@ typedef struct { QVirtioDevice *dev; - QOSState *qs; int num_queues; QVirtQueue *vq[MAX_NUM_QUEUES + 2]; -} QVirtIOSCSI; +} QVirtioSCSIQueues; -static QOSState *qvirtio_scsi_start(const char *extra_opts) -{ - QOSState *qs; - const char *arch = qtest_get_arch(); - const char *cmd = "-drive id=drv0,if=none,file=null-co://,format=raw " - "-device virtio-scsi-pci,id=vs0 " - "-device scsi-hd,bus=vs0.0,drive=drv0 %s"; +static QGuestAllocator *alloc; - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qs = qtest_pc_boot(cmd, extra_opts ? : ""); - } else if (strcmp(arch, "ppc64") == 0) { - qs = qtest_spapr_boot(cmd, extra_opts ? : ""); - } else { - g_printerr("virtio-scsi tests are only available on x86 or ppc64\n"); - exit(EXIT_FAILURE); - } - global_qtest = qs->qts; - return qs; -} - -static void qvirtio_scsi_stop(QOSState *qs) -{ - qtest_shutdown(qs); -} - -static void qvirtio_scsi_pci_free(QVirtIOSCSI *vs) +static void qvirtio_scsi_pci_free(QVirtioSCSIQueues *vs) { int i; for (i = 0; i < vs->num_queues + 2; i++) { - qvirtqueue_cleanup(vs->dev->bus, vs->vq[i], &vs->qs->alloc); + qvirtqueue_cleanup(vs->dev->bus, vs->vq[i], alloc); } - qvirtio_pci_device_disable(container_of(vs->dev, QVirtioPCIDevice, vdev)); - qvirtio_pci_device_free((QVirtioPCIDevice *)vs->dev); - qvirtio_scsi_stop(vs->qs); g_free(vs); } -static uint64_t qvirtio_scsi_alloc(QVirtIOSCSI *vs, size_t alloc_size, +static uint64_t qvirtio_scsi_alloc(QVirtioSCSIQueues *vs, size_t alloc_size, const void *data) { uint64_t addr; - addr = guest_alloc(&vs->qs->alloc, alloc_size); + addr = guest_alloc(alloc, alloc_size); if (data) { memwrite(addr, data, alloc_size); } @@ -83,7 +58,8 @@ static uint64_t qvirtio_scsi_alloc(QVirtIOSCSI *vs, size_t alloc_size, return addr; } -static uint8_t virtio_scsi_do_command(QVirtIOSCSI *vs, const uint8_t *cdb, +static uint8_t virtio_scsi_do_command(QVirtioSCSIQueues *vs, + const uint8_t *cdb, const uint8_t *data_in, size_t data_in_len, uint8_t *data_out, size_t data_out_len, @@ -133,40 +109,28 @@ static uint8_t virtio_scsi_do_command(QVirtIOSCSI *vs, const uint8_t *cdb, memread(resp_addr, resp_out, sizeof(*resp_out)); } - guest_free(&vs->qs->alloc, req_addr); - guest_free(&vs->qs->alloc, resp_addr); - guest_free(&vs->qs->alloc, data_in_addr); - guest_free(&vs->qs->alloc, data_out_addr); + guest_free(alloc, req_addr); + guest_free(alloc, resp_addr); + guest_free(alloc, data_in_addr); + guest_free(alloc, data_out_addr); return response; } -static QVirtIOSCSI *qvirtio_scsi_pci_init(int slot) +static QVirtioSCSIQueues *qvirtio_scsi_init(QVirtioDevice *dev) { + QVirtioSCSIQueues *vs; const uint8_t test_unit_ready_cdb[VIRTIO_SCSI_CDB_SIZE] = {}; - QVirtIOSCSI *vs; - QVirtioPCIDevice *dev; struct virtio_scsi_cmd_resp resp; int i; - vs = g_new0(QVirtIOSCSI, 1); - - vs->qs = qvirtio_scsi_start("-drive file=blkdebug::null-co://," - "if=none,id=dr1,format=raw,file.align=4k " - "-device scsi-hd,drive=dr1,lun=0,scsi-id=1"); - dev = qvirtio_pci_device_find(vs->qs->pcibus, VIRTIO_ID_SCSI); - vs->dev = (QVirtioDevice *)dev; - g_assert(dev != NULL); - g_assert_cmphex(vs->dev->device_type, ==, VIRTIO_ID_SCSI); - - qvirtio_pci_device_enable(dev); - qvirtio_start_device(vs->dev); - - vs->num_queues = qvirtio_config_readl(vs->dev, 0); + vs = g_new0(QVirtioSCSIQueues, 1); + vs->dev = dev; + vs->num_queues = qvirtio_config_readl(dev, 0); g_assert_cmpint(vs->num_queues, <, MAX_NUM_QUEUES); for (i = 0; i < vs->num_queues + 2; i++) { - vs->vq[i] = qvirtqueue_setup(vs->dev, &vs->qs->alloc, i); + vs->vq[i] = qvirtqueue_setup(dev, alloc, i); } /* Clear the POWER ON OCCURRED unit attention */ @@ -182,30 +146,18 @@ static QVirtIOSCSI *qvirtio_scsi_pci_init(int slot) return vs; } -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void pci_nop(void) +static void hotplug(void *obj, void *data, QGuestAllocator *alloc) { - QOSState *qs; - - qs = qvirtio_scsi_start(NULL); - qvirtio_scsi_stop(qs); -} - -static void hotplug(void) -{ - QOSState *qs; - - qs = qvirtio_scsi_start( - "-drive id=drv1,if=none,file=null-co://,format=raw"); qtest_qmp_device_add("scsi-hd", "scsihd", "{'drive': 'drv1'}"); qtest_qmp_device_del("scsihd"); - qvirtio_scsi_stop(qs); } /* Test WRITE SAME with the lba not aligned */ -static void test_unaligned_write_same(void) +static void test_unaligned_write_same(void *obj, void *data, + QGuestAllocator *t_alloc) { - QVirtIOSCSI *vs; + QVirtioSCSI *scsi = obj; + QVirtioSCSIQueues *vs; uint8_t buf1[512] = { 0 }; uint8_t buf2[512] = { 1 }; const uint8_t write_same_cdb_1[VIRTIO_SCSI_CDB_SIZE] = { @@ -218,27 +170,50 @@ static void test_unaligned_write_same(void) 0x41, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0x00, 0x00 }; - vs = qvirtio_scsi_pci_init(PCI_SLOT); + alloc = t_alloc; + vs = qvirtio_scsi_init(scsi->vdev); g_assert_cmphex(0, ==, - virtio_scsi_do_command(vs, write_same_cdb_1, NULL, 0, buf1, 512, NULL)); + virtio_scsi_do_command(vs, write_same_cdb_1, NULL, 0, buf1, 512, + NULL)); g_assert_cmphex(0, ==, - virtio_scsi_do_command(vs, write_same_cdb_2, NULL, 0, buf2, 512, NULL)); + virtio_scsi_do_command(vs, write_same_cdb_2, NULL, 0, buf2, 512, + NULL)); g_assert_cmphex(0, ==, - virtio_scsi_do_command(vs, write_same_cdb_ndob, NULL, 0, NULL, 0, NULL)); + virtio_scsi_do_command(vs, write_same_cdb_ndob, NULL, 0, NULL, 0, + NULL)); qvirtio_scsi_pci_free(vs); } -int main(int argc, char **argv) +static void *virtio_scsi_hotplug_setup(GString *cmd_line, void *arg) { - g_test_init(&argc, &argv, NULL); - qtest_add_func("/virtio/scsi/pci/nop", pci_nop); - qtest_add_func("/virtio/scsi/pci/hotplug", hotplug); - qtest_add_func("/virtio/scsi/pci/scsi-disk/unaligned-write-same", - test_unaligned_write_same); - - return g_test_run(); + g_string_append(cmd_line, + " -drive id=drv1,if=none,file=null-co://,format=raw"); + return arg; } + +static void *virtio_scsi_setup(GString *cmd_line, void *arg) +{ + g_string_append(cmd_line, + " -drive file=blkdebug::null-co://," + "if=none,id=dr1,format=raw,file.align=4k " + "-device scsi-hd,drive=dr1,lun=0,scsi-id=1"); + return arg; +} + +static void register_virtio_scsi_test(void) +{ + QOSGraphTestOptions opts = { }; + + opts.before = virtio_scsi_hotplug_setup; + qos_add_test("hotplug", "virtio-scsi", hotplug, &opts); + + opts.before = virtio_scsi_setup; + qos_add_test("unaligned-write-same", "virtio-scsi", + test_unaligned_write_same, &opts); +} + +libqos_init(register_virtio_scsi_test); From 6e68204646e1167cf1cdb2166099a66d5576a123 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 15 Nov 2018 12:28:38 +0100 Subject: [PATCH 43/57] tests/libqos: remove pre-qgraph QVirtioPCIDevice API Remove the qvirtio_pci_device_find* and qvirtio_pci_device_free APIs, now that they do not have any users. Signed-off-by: Paolo Bonzini --- tests/libqos/virtio-pci.c | 112 ++++++-------------------------------- tests/libqos/virtio-pci.h | 4 -- 2 files changed, 17 insertions(+), 99 deletions(-) diff --git a/tests/libqos/virtio-pci.c b/tests/libqos/virtio-pci.c index 0b58b74267..993d347830 100644 --- a/tests/libqos/virtio-pci.c +++ b/tests/libqos/virtio-pci.c @@ -35,14 +35,6 @@ * original qvirtio_pci_destructor and qvirtio_pci_start_hw. */ -typedef struct QVirtioPCIForeachData { - void (*func)(QVirtioDevice *d, void *data); - uint16_t device_type; - bool has_slot; - int slot; - void *user_data; -} QVirtioPCIForeachData; - static inline bool qvirtio_pci_is_big_endian(QVirtioPCIDevice *dev) { QPCIBus *bus = dev->pdev->bus; @@ -51,50 +43,6 @@ static inline bool qvirtio_pci_is_big_endian(QVirtioPCIDevice *dev) return qtest_big_endian(bus->qts); } -void qvirtio_pci_device_free(QVirtioPCIDevice *dev) -{ - g_free(dev->pdev); -} - -static void qvirtio_pci_init_from_pcidev(QVirtioPCIDevice *dev, QPCIDevice *pci_dev) -{ - dev->pdev = pci_dev; - dev->vdev.device_type = qpci_config_readw(pci_dev, PCI_SUBSYSTEM_ID); - - dev->config_msix_entry = -1; - - dev->vdev.bus = &qvirtio_pci; - dev->vdev.big_endian = qvirtio_pci_is_big_endian(dev); - - /* each virtio-xxx-pci device should override at least this function */ - dev->obj.get_driver = NULL; - dev->obj.start_hw = qvirtio_pci_start_hw; - dev->obj.destructor = qvirtio_pci_destructor; -} - -static void qvirtio_pci_foreach_callback( - QPCIDevice *dev, int devfn, void *data) -{ - QVirtioPCIForeachData *d = data; - QVirtioPCIDevice *vpcidev = g_new0(QVirtioPCIDevice, 1); - - qvirtio_pci_init_from_pcidev(vpcidev, dev); - if (vpcidev->vdev.device_type == d->device_type && - (!d->has_slot || vpcidev->pdev->devfn == d->slot << 3)) { - d->func(&vpcidev->vdev, d->user_data); - } else { - qvirtio_pci_device_free(vpcidev); - g_free(vpcidev); - } -} - -static void qvirtio_pci_assign_device(QVirtioDevice *d, void *data) -{ - QVirtioPCIDevice **vpcidev = data; - assert(!*vpcidev); - *vpcidev = container_of(d, QVirtioPCIDevice, vdev); -} - #define CONFIG_BASE(dev) (VIRTIO_PCI_CONFIG_OFF((dev)->pdev->msix_enabled)) static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t off) @@ -317,48 +265,6 @@ const QVirtioBus qvirtio_pci = { .virtqueue_kick = qvirtio_pci_virtqueue_kick, }; -/* TODO: delete this once qgraph is completed */ -static void qvirtio_pci_foreach(QPCIBus *bus, uint16_t device_type, - bool has_slot, int slot, - void (*func)(QVirtioDevice *d, void *data), void *data) -{ - QVirtioPCIForeachData d = { .func = func, - .device_type = device_type, - .has_slot = has_slot, - .slot = slot, - .user_data = data }; - - qpci_device_foreach(bus, PCI_VENDOR_ID_REDHAT_QUMRANET, -1, - qvirtio_pci_foreach_callback, &d); -} - -QVirtioPCIDevice *qvirtio_pci_device_find(QPCIBus *bus, uint16_t device_type) -{ - QVirtioPCIDevice *dev = NULL; - - qvirtio_pci_foreach(bus, device_type, false, 0, - qvirtio_pci_assign_device, &dev); - - if (dev) { - dev->vdev.bus = &qvirtio_pci; - } - - return dev; -} - -QVirtioPCIDevice *qvirtio_pci_device_find_slot(QPCIBus *bus, - uint16_t device_type, int slot) -{ - QVirtioPCIDevice *dev = NULL; - - qvirtio_pci_foreach(bus, device_type, true, slot, - qvirtio_pci_assign_device, &dev); - - dev->vdev.bus = &qvirtio_pci; - - return dev; -} - void qvirtio_pci_device_enable(QVirtioPCIDevice *d) { qpci_device_enable(d->pdev); @@ -445,7 +351,7 @@ void qvirtio_pci_destructor(QOSGraphObject *obj) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)obj; qvirtio_pci_device_disable(dev); - qvirtio_pci_device_free(dev); + g_free(dev->pdev); } void qvirtio_pci_start_hw(QOSGraphObject *obj) @@ -455,6 +361,22 @@ void qvirtio_pci_start_hw(QOSGraphObject *obj) qvirtio_start_device(&dev->vdev); } +static void qvirtio_pci_init_from_pcidev(QVirtioPCIDevice *dev, QPCIDevice *pci_dev) +{ + dev->pdev = pci_dev; + dev->vdev.device_type = qpci_config_readw(pci_dev, PCI_SUBSYSTEM_ID); + + dev->config_msix_entry = -1; + + dev->vdev.bus = &qvirtio_pci; + dev->vdev.big_endian = qvirtio_pci_is_big_endian(dev); + + /* each virtio-xxx-pci device should override at least this function */ + dev->obj.get_driver = NULL; + dev->obj.start_hw = qvirtio_pci_start_hw; + dev->obj.destructor = qvirtio_pci_destructor; +} + void virtio_pci_init(QVirtioPCIDevice *dev, QPCIBus *bus, QPCIAddress * addr) { QPCIDevice *pci_dev = qpci_device_find(bus, addr->devfn); diff --git a/tests/libqos/virtio-pci.h b/tests/libqos/virtio-pci.h index 56313527a7..728b4715f1 100644 --- a/tests/libqos/virtio-pci.h +++ b/tests/libqos/virtio-pci.h @@ -35,10 +35,6 @@ extern const QVirtioBus qvirtio_pci; void virtio_pci_init(QVirtioPCIDevice *dev, QPCIBus *bus, QPCIAddress * addr); QVirtioPCIDevice *virtio_pci_new(QPCIBus *bus, QPCIAddress * addr); -QVirtioPCIDevice *qvirtio_pci_device_find(QPCIBus *bus, uint16_t device_type); -QVirtioPCIDevice *qvirtio_pci_device_find_slot(QPCIBus *bus, - uint16_t device_type, int slot); -void qvirtio_pci_device_free(QVirtioPCIDevice *dev); /* virtio-pci object functions available for subclasses that * override the original start_hw and destroy From 19e3d9795c62fa5d91977af45ec8fad5984d874d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 5 Oct 2018 15:02:44 +0200 Subject: [PATCH 44/57] tests: move virtio entirely to qos-test The only remaining test that needs libqos-virtio-obj-y is drive_del-test, which really only needs a function. Move that function to the test and remove libqos-virtio-obj-y. Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 9 ++++----- tests/drive_del-test.c | 25 +++++++++++++++++++------ tests/libqos/virtio.c | 18 ------------------ tests/libqos/virtio.h | 1 - 4 files changed, 23 insertions(+), 30 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index 09483d0c39..b4b53ca919 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -161,7 +161,6 @@ check-qtest-pci-$(CONFIG_NE2000_PCI) += tests/ne2000-test$(EXESUF) check-qtest-pci-$(CONFIG_NVME_PCI) += tests/nvme-test$(EXESUF) check-qtest-pci-$(CONFIG_AC97) += tests/ac97-test$(EXESUF) check-qtest-pci-$(CONFIG_ES1370) += tests/es1370-test$(EXESUF) -check-qtest-pci-$(CONFIG_VIRTIO) += $(check-qtest-virtio-y) check-qtest-pci-$(CONFIG_IPACK) += tests/tpci200-test$(EXESUF) check-qtest-pci-$(CONFIG_IPACK) += $(check-qtest-ipack-y) check-qtest-pci-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) @@ -254,7 +253,6 @@ check-qtest-ppc64-$(CONFIG_SLIRP) += tests/pxe-test$(EXESUF) check-qtest-ppc64-$(CONFIG_USB_OHCI) += tests/usb-hcd-ohci-test$(EXESUF) check-qtest-ppc64-$(CONFIG_USB_UHCI) += tests/usb-hcd-uhci-test$(EXESUF) check-qtest-ppc64-$(CONFIG_USB_XHCI_NEC) += tests/usb-hcd-xhci-test$(EXESUF) -check-qtest-ppc64-$(CONFIG_VIRTIO) += $(check-qtest-virtio-y) check-qtest-ppc64-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF) check-qtest-ppc64-$(CONFIG_POSIX) += tests/test-filter-mirror$(EXESUF) check-qtest-ppc64-$(CONFIG_RTL8139_PCI) += tests/test-filter-redirector$(EXESUF) @@ -728,18 +726,19 @@ libqos-pc-obj-y += tests/libqos/ahci.o libqos-omap-obj-y = $(libqos-obj-y) tests/libqos/i2c-omap.o libqos-imx-obj-y = $(libqos-obj-y) tests/libqos/i2c-imx.o libqos-usb-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/usb.o -libqos-virtio-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o # Devices qos-test-obj-y = tests/qos-test.o $(libqgraph-obj-y) qos-test-obj-y += $(libqos-pc-obj-y) $(libqos-spapr-obj-y) qos-test-obj-y += tests/libqos/e1000e.o qos-test-obj-y += tests/libqos/sdhci.o -qos-test-obj-y += $(libqos-virtio-obj-y) +qos-test-obj-y += tests/libqos/virtio.o qos-test-obj-$(CONFIG_VIRTFS) += tests/libqos/virtio-9p.o qos-test-obj-y += tests/libqos/virtio-balloon.o qos-test-obj-y += tests/libqos/virtio-blk.o +qos-test-obj-y += tests/libqos/virtio-mmio.o qos-test-obj-y += tests/libqos/virtio-net.o +qos-test-obj-y += tests/libqos/virtio-pci.o qos-test-obj-y += tests/libqos/virtio-rng.o qos-test-obj-y += tests/libqos/virtio-scsi.o qos-test-obj-y += tests/libqos/virtio-serial.o @@ -819,7 +818,7 @@ tests/qom-test$(EXESUF): tests/qom-test.o tests/test-hmp$(EXESUF): tests/test-hmp.o tests/machine-none-test$(EXESUF): tests/machine-none-test.o tests/device-plug-test$(EXESUF): tests/device-plug-test.o -tests/drive_del-test$(EXESUF): tests/drive_del-test.o $(libqos-virtio-obj-y) +tests/drive_del-test$(EXESUF): tests/drive_del-test.o tests/nvme-test$(EXESUF): tests/nvme-test.o $(libqos-pc-obj-y) tests/pvpanic-test$(EXESUF): tests/pvpanic-test.o tests/i82801b11-test$(EXESUF): tests/i82801b11-test.o diff --git a/tests/drive_del-test.c b/tests/drive_del-test.c index 4a1a0889c8..2f9474e03c 100644 --- a/tests/drive_del-test.c +++ b/tests/drive_del-test.c @@ -63,6 +63,24 @@ static void test_drive_without_dev(void) qtest_end(); } +/* + * qvirtio_get_dev_type: + * Returns: the preferred virtio bus/device type for the current architecture. + * TODO: delete this + */ +static const char *qvirtio_get_dev_type(void) +{ + const char *arch = qtest_get_arch(); + + if (g_str_equal(arch, "arm") || g_str_equal(arch, "aarch64")) { + return "device"; /* for virtio-mmio */ + } else if (g_str_equal(arch, "s390x")) { + return "ccw"; + } else { + return "pci"; + } +} + static void test_after_failed_device_add(void) { char driver[32]; @@ -119,16 +137,11 @@ static void test_drive_del_device_del(void) int main(int argc, char **argv) { - const char *arch = qtest_get_arch(); - g_test_init(&argc, &argv, NULL); qtest_add_func("/drive_del/without-dev", test_drive_without_dev); - /* TODO I guess any arch with a hot-pluggable virtio bus would do */ - if (!strcmp(arch, "i386") || !strcmp(arch, "x86_64") || - !strcmp(arch, "ppc") || !strcmp(arch, "ppc64") || - !strcmp(arch, "s390x")) { + if (qvirtio_get_dev_type() != NULL) { qtest_add_func("/drive_del/after_failed_device_add", test_after_failed_device_add); qtest_add_func("/blockdev/drive_del_device_del", diff --git a/tests/libqos/virtio.c b/tests/libqos/virtio.c index e4925d160b..5e8f39b4d3 100644 --- a/tests/libqos/virtio.c +++ b/tests/libqos/virtio.c @@ -350,24 +350,6 @@ void qvirtqueue_set_used_event(QVirtQueue *vq, uint16_t idx) writew(vq->avail + 4 + (2 * vq->size), idx); } -/* - * qvirtio_get_dev_type: - * Returns: the preferred virtio bus/device type for the current architecture. - * TODO: delete this - */ -const char *qvirtio_get_dev_type(void) -{ - const char *arch = qtest_get_arch(); - - if (g_str_equal(arch, "arm") || g_str_equal(arch, "aarch64")) { - return "device"; /* for virtio-mmio */ - } else if (g_str_equal(arch, "s390x")) { - return "ccw"; - } else { - return "pci"; - } -} - void qvirtio_start_device(QVirtioDevice *vdev) { qvirtio_reset(vdev); diff --git a/tests/libqos/virtio.h b/tests/libqos/virtio.h index 3f97d79c28..51d2359ace 100644 --- a/tests/libqos/virtio.h +++ b/tests/libqos/virtio.h @@ -142,7 +142,6 @@ bool qvirtqueue_get_buf(QVirtQueue *vq, uint32_t *desc_idx, uint32_t *len); void qvirtqueue_set_used_event(QVirtQueue *vq, uint16_t idx); -const char *qvirtio_get_dev_type(void); void qvirtio_start_device(QVirtioDevice *vdev); #endif From 86dc943f6e00af799432756c39137d663791d8d3 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Fri, 17 Aug 2018 12:01:59 +0200 Subject: [PATCH 45/57] qos-test: ac97 test node Convert tests/ac97-test to a driver node; currently it runs the PCI nop test only, therefore we're not placing it in tests/libqos. Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 3 +-- tests/ac97-test.c | 51 +++++++++++++++++++++++++++++++----------- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index b4b53ca919..954b06babd 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -159,7 +159,6 @@ check-qtest-pci-$(CONFIG_PCNET_PCI) += tests/pcnet-test$(EXESUF) check-qtest-pci-$(CONFIG_EEPRO100_PCI) += tests/eepro100-test$(EXESUF) check-qtest-pci-$(CONFIG_NE2000_PCI) += tests/ne2000-test$(EXESUF) check-qtest-pci-$(CONFIG_NVME_PCI) += tests/nvme-test$(EXESUF) -check-qtest-pci-$(CONFIG_AC97) += tests/ac97-test$(EXESUF) check-qtest-pci-$(CONFIG_ES1370) += tests/es1370-test$(EXESUF) check-qtest-pci-$(CONFIG_IPACK) += tests/tpci200-test$(EXESUF) check-qtest-pci-$(CONFIG_IPACK) += $(check-qtest-ipack-y) @@ -754,6 +753,7 @@ qos-test-obj-y += tests/libqos/ppc64_pseries-machine.o qos-test-obj-y += tests/libqos/x86_64_pc-machine.o # Tests +qos-test-obj-y += tests/ac97-test.o qos-test-obj-y += tests/e1000e-test.o qos-test-obj-y += tests/pci-test.o qos-test-obj-y += tests/sdhci-test.o @@ -822,7 +822,6 @@ tests/drive_del-test$(EXESUF): tests/drive_del-test.o tests/nvme-test$(EXESUF): tests/nvme-test.o $(libqos-pc-obj-y) tests/pvpanic-test$(EXESUF): tests/pvpanic-test.o tests/i82801b11-test$(EXESUF): tests/i82801b11-test.o -tests/ac97-test$(EXESUF): tests/ac97-test.o tests/es1370-test$(EXESUF): tests/es1370-test.o tests/intel-hda-test$(EXESUF): tests/intel-hda-test.o tests/ioh3420-test$(EXESUF): tests/ioh3420-test.o diff --git a/tests/ac97-test.c b/tests/ac97-test.c index e0d177bd9c..532fb1cc98 100644 --- a/tests/ac97-test.c +++ b/tests/ac97-test.c @@ -9,23 +9,48 @@ #include "qemu/osdep.h" #include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void nop(void) +typedef struct QAC97 QAC97; + +struct QAC97 { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static void *ac97_get_driver(void *obj, const char *interface) { + QAC97 *ac97 = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &ac97->dev; + } + + fprintf(stderr, "%s not present in e1000e\n", interface); + g_assert_not_reached(); } -int main(int argc, char **argv) +static void *ac97_create(void *pci_bus, QGuestAllocator *alloc, void *addr) { - int ret; + QAC97 *ac97 = g_new0(QAC97, 1); + QPCIBus *bus = pci_bus; - g_test_init(&argc, &argv, NULL); - qtest_add_func("/ac97/nop", nop); - - qtest_start("-device AC97"); - ret = g_test_run(); - - qtest_end(); - - return ret; + qpci_device_init(&ac97->dev, bus, addr); + ac97->obj.get_driver = ac97_get_driver; + return &ac97->obj; } + +static void ac97_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("AC97", ac97_create); + qos_node_produces("AC97", "pci-device"); + qos_node_consumes("AC97", "pci-bus", &opts); +} + +libqos_init(ac97_register_nodes); From 0793a8897785d7bf14f439b117d79a244c6484fc Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Fri, 17 Aug 2018 14:04:33 +0200 Subject: [PATCH 46/57] qos-test: tpci200 test node Convert tests/tpci200-test to a driver node; currently it runs the PCI nop test only, but it also produces the ipack interface. Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 3 +- tests/libqos/tpci200.c | 65 ++++++++++++++++++++++++++++++++++++++++++ tests/tpci200-test.c | 31 -------------------- 3 files changed, 66 insertions(+), 33 deletions(-) create mode 100644 tests/libqos/tpci200.c delete mode 100644 tests/tpci200-test.c diff --git a/tests/Makefile.include b/tests/Makefile.include index 954b06babd..905a361c69 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -160,7 +160,6 @@ check-qtest-pci-$(CONFIG_EEPRO100_PCI) += tests/eepro100-test$(EXESUF) check-qtest-pci-$(CONFIG_NE2000_PCI) += tests/ne2000-test$(EXESUF) check-qtest-pci-$(CONFIG_NVME_PCI) += tests/nvme-test$(EXESUF) check-qtest-pci-$(CONFIG_ES1370) += tests/es1370-test$(EXESUF) -check-qtest-pci-$(CONFIG_IPACK) += tests/tpci200-test$(EXESUF) check-qtest-pci-$(CONFIG_IPACK) += $(check-qtest-ipack-y) check-qtest-pci-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) check-qtest-pci-$(CONFIG_HDA) += tests/intel-hda-test$(EXESUF) @@ -731,6 +730,7 @@ qos-test-obj-y = tests/qos-test.o $(libqgraph-obj-y) qos-test-obj-y += $(libqos-pc-obj-y) $(libqos-spapr-obj-y) qos-test-obj-y += tests/libqos/e1000e.o qos-test-obj-y += tests/libqos/sdhci.o +qos-test-obj-y += tests/libqos/tpci200.o qos-test-obj-y += tests/libqos/virtio.o qos-test-obj-$(CONFIG_VIRTFS) += tests/libqos/virtio-9p.o qos-test-obj-y += tests/libqos/virtio-balloon.o @@ -811,7 +811,6 @@ tests/ne2000-test$(EXESUF): tests/ne2000-test.o tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y) tests/virtio-ccw-test$(EXESUF): tests/virtio-ccw-test.o -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 tests/qom-test$(EXESUF): tests/qom-test.o diff --git a/tests/libqos/tpci200.c b/tests/libqos/tpci200.c new file mode 100644 index 0000000000..98dc532933 --- /dev/null +++ b/tests/libqos/tpci200.c @@ -0,0 +1,65 @@ +/* + * QTest testcase for tpci200 PCI-IndustryPack bridge + * + * Copyright (c) 2014 SUSE LINUX Products GmbH + * + * 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 "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" + +typedef struct QTpci200 QTpci200; +typedef struct QIpack QIpack; + +struct QIpack { + +}; +struct QTpci200 { + QOSGraphObject obj; + QPCIDevice dev; + QIpack ipack; +}; + +/* tpci200 */ +static void *tpci200_get_driver(void *obj, const char *interface) +{ + QTpci200 *tpci200 = obj; + if (!g_strcmp0(interface, "ipack")) { + return &tpci200->ipack; + } + if (!g_strcmp0(interface, "pci-device")) { + return &tpci200->dev; + } + + fprintf(stderr, "%s not present in tpci200\n", interface); + g_assert_not_reached(); +} + +static void *tpci200_create(void *pci_bus, QGuestAllocator *alloc, void *addr) +{ + QTpci200 *tpci200 = g_new0(QTpci200, 1); + QPCIBus *bus = pci_bus; + + qpci_device_init(&tpci200->dev, bus, addr); + tpci200->obj.get_driver = tpci200_get_driver; + return &tpci200->obj; +} + +static void tpci200_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0,id=ipack0", + }; + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("tpci200", tpci200_create); + qos_node_consumes("tpci200", "pci-bus", &opts); + qos_node_produces("tpci200", "ipack"); + qos_node_produces("tpci200", "pci-device"); +} + +libqos_init(tpci200_register_nodes); diff --git a/tests/tpci200-test.c b/tests/tpci200-test.c deleted file mode 100644 index 0321ec27ec..0000000000 --- a/tests/tpci200-test.c +++ /dev/null @@ -1,31 +0,0 @@ -/* - * QTest testcase for tpci200 PCI-IndustryPack bridge - * - * Copyright (c) 2014 SUSE LINUX Products GmbH - * - * 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 "qemu/osdep.h" -#include "libqtest.h" - -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void nop(void) -{ -} - -int main(int argc, char **argv) -{ - int ret; - - g_test_init(&argc, &argv, NULL); - qtest_add_func("/tpci200/nop", nop); - - qtest_start("-device tpci200"); - ret = g_test_run(); - - qtest_end(); - - return ret; -} From 31692b2ec78c352b87f928c873ac38d6fdf711a7 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Fri, 17 Aug 2018 12:48:27 +0200 Subject: [PATCH 47/57] qos-test: ipoctal232 test node Convert tests/ipoctal232-test to a driver node; currently it runs the PCI nop test only, therefore we're not placing it in tests/libqos. This test creates a tpci200 node that produces an interface ipack consumed by the ipoctal232 device. Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 5 +---- tests/ipoctal232-test.c | 41 +++++++++++++++++++++++++++++------------ 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index 905a361c69..3eaa888735 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -151,8 +151,6 @@ check-qtest-generic-y += tests/qmp-cmd-test$(EXESUF) check-qtest-generic-y += tests/device-introspect-test$(EXESUF) check-qtest-generic-y += tests/cdrom-test$(EXESUF) -check-qtest-ipack-y += tests/ipoctal232-test$(EXESUF) - check-qtest-pci-y += tests/e1000-test$(EXESUF) check-qtest-pci-$(CONFIG_RTL8139_PCI) += tests/rtl8139-test$(EXESUF) check-qtest-pci-$(CONFIG_PCNET_PCI) += tests/pcnet-test$(EXESUF) @@ -160,7 +158,6 @@ check-qtest-pci-$(CONFIG_EEPRO100_PCI) += tests/eepro100-test$(EXESUF) check-qtest-pci-$(CONFIG_NE2000_PCI) += tests/ne2000-test$(EXESUF) check-qtest-pci-$(CONFIG_NVME_PCI) += tests/nvme-test$(EXESUF) check-qtest-pci-$(CONFIG_ES1370) += tests/es1370-test$(EXESUF) -check-qtest-pci-$(CONFIG_IPACK) += $(check-qtest-ipack-y) check-qtest-pci-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) check-qtest-pci-$(CONFIG_HDA) += tests/intel-hda-test$(EXESUF) check-qtest-pci-$(CONFIG_IVSHMEM_DEVICE) += tests/ivshmem-test$(EXESUF) @@ -755,6 +752,7 @@ qos-test-obj-y += tests/libqos/x86_64_pc-machine.o # Tests qos-test-obj-y += tests/ac97-test.o qos-test-obj-y += tests/e1000e-test.o +qos-test-obj-y += tests/ipoctal232-test.o qos-test-obj-y += tests/pci-test.o qos-test-obj-y += tests/sdhci-test.o qos-test-obj-$(CONFIG_VHOST_NET_USER) += tests/vhost-user-test.o $(chardev-obj-y) $(test-io-obj-y) @@ -812,7 +810,6 @@ tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y) tests/virtio-ccw-test$(EXESUF): tests/virtio-ccw-test.o tests/display-vga-test$(EXESUF): tests/display-vga-test.o -tests/ipoctal232-test$(EXESUF): tests/ipoctal232-test.o tests/qom-test$(EXESUF): tests/qom-test.o tests/test-hmp$(EXESUF): tests/test-hmp.o tests/machine-none-test$(EXESUF): tests/machine-none-test.o diff --git a/tests/ipoctal232-test.c b/tests/ipoctal232-test.c index 684914164d..42d53718b8 100644 --- a/tests/ipoctal232-test.c +++ b/tests/ipoctal232-test.c @@ -9,23 +9,40 @@ #include "qemu/osdep.h" #include "libqtest.h" +#include "libqos/qgraph.h" + +typedef struct QIpoctal232 QIpoctal232; + +struct QIpoctal232 { + QOSGraphObject obj; +}; /* Tests only initialization so far. TODO: Replace with functional tests */ -static void nop(void) +static void nop(void *obj, void *data, QGuestAllocator *alloc) { } -int main(int argc, char **argv) +static void *ipoctal232_create(void *pci_bus, QGuestAllocator *alloc, + void *addr) { - int ret; + QIpoctal232 *ipoctal232 = g_new0(QIpoctal232, 1); - g_test_init(&argc, &argv, NULL); - qtest_add_func("/ipoctal232/tpci200/nop", nop); - - qtest_start("-device tpci200,id=ipack0 -device ipoctal232,bus=ipack0.0"); - ret = g_test_run(); - - qtest_end(); - - return ret; + return &ipoctal232->obj; } + +static void ipoctal232_register_nodes(void) +{ + qos_node_create_driver("ipoctal232", ipoctal232_create); + qos_node_consumes("ipoctal232", "ipack", &(QOSGraphEdgeOptions) { + .extra_device_opts = "bus=ipack0.0", + }); +} + +libqos_init(ipoctal232_register_nodes); + +static void register_ipoctal232_test(void) +{ + qos_add_test("nop", "ipoctal232", nop, NULL); +} + +libqos_init(register_ipoctal232_test); From 941e33170916bc5bea8373807f2bfe5dc396d339 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Fri, 17 Aug 2018 13:04:12 +0200 Subject: [PATCH 48/57] qos-test: ne2k_pci test node Convert tests/ne2000-test to a driver node; currently it runs the PCI nop test only, therefore we're not placing it in tests/libqos. The actual device consumed by the test is ne2k_pci. Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 3 +-- tests/ne2000-test.c | 50 ++++++++++++++++++++++++++++++++---------- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index 3eaa888735..0d06490e99 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -155,7 +155,6 @@ check-qtest-pci-y += tests/e1000-test$(EXESUF) check-qtest-pci-$(CONFIG_RTL8139_PCI) += tests/rtl8139-test$(EXESUF) check-qtest-pci-$(CONFIG_PCNET_PCI) += tests/pcnet-test$(EXESUF) check-qtest-pci-$(CONFIG_EEPRO100_PCI) += tests/eepro100-test$(EXESUF) -check-qtest-pci-$(CONFIG_NE2000_PCI) += tests/ne2000-test$(EXESUF) check-qtest-pci-$(CONFIG_NVME_PCI) += tests/nvme-test$(EXESUF) check-qtest-pci-$(CONFIG_ES1370) += tests/es1370-test$(EXESUF) check-qtest-pci-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) @@ -753,6 +752,7 @@ qos-test-obj-y += tests/libqos/x86_64_pc-machine.o qos-test-obj-y += tests/ac97-test.o qos-test-obj-y += tests/e1000e-test.o qos-test-obj-y += tests/ipoctal232-test.o +qos-test-obj-y += tests/ne2000-test.o qos-test-obj-y += tests/pci-test.o qos-test-obj-y += tests/sdhci-test.o qos-test-obj-$(CONFIG_VHOST_NET_USER) += tests/vhost-user-test.o $(chardev-obj-y) $(test-io-obj-y) @@ -805,7 +805,6 @@ tests/pcnet-test$(EXESUF): tests/pcnet-test.o tests/pnv-xscom-test$(EXESUF): tests/pnv-xscom-test.o tests/eepro100-test$(EXESUF): tests/eepro100-test.o tests/vmxnet3-test$(EXESUF): tests/vmxnet3-test.o -tests/ne2000-test$(EXESUF): tests/ne2000-test.o tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y) tests/virtio-ccw-test$(EXESUF): tests/virtio-ccw-test.o diff --git a/tests/ne2000-test.c b/tests/ne2000-test.c index b7cf3dd2f5..097c2eec6c 100644 --- a/tests/ne2000-test.c +++ b/tests/ne2000-test.c @@ -9,23 +9,49 @@ #include "qemu/osdep.h" #include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void pci_nop(void) +typedef struct QNe2k_pci QNe2k_pci; + +struct QNe2k_pci { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static void *ne2k_pci_get_driver(void *obj, const char *interface) { + QNe2k_pci *ne2k_pci = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &ne2k_pci->dev; + } + + fprintf(stderr, "%s not present in ne2k_pci\n", interface); + g_assert_not_reached(); } -int main(int argc, char **argv) +static void *ne2k_pci_create(void *pci_bus, QGuestAllocator *alloc, void *addr) { - int ret; + QNe2k_pci *ne2k_pci = g_new0(QNe2k_pci, 1); + QPCIBus *bus = pci_bus; - g_test_init(&argc, &argv, NULL); - qtest_add_func("/ne2000/pci/nop", pci_nop); + qpci_device_init(&ne2k_pci->dev, bus, addr); + ne2k_pci->obj.get_driver = ne2k_pci_get_driver; - qtest_start("-device ne2k_pci"); - ret = g_test_run(); - - qtest_end(); - - return ret; + return &ne2k_pci->obj; } + +static void ne2000_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("ne2k_pci", ne2k_pci_create); + qos_node_consumes("ne2k_pci", "pci-bus", &opts); + qos_node_produces("ne2k_pci", "pci-device"); +} + +libqos_init(ne2000_register_nodes); From 3157ed330ed4df2a598fe533dced1ea566810bcb Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Fri, 17 Aug 2018 13:16:07 +0200 Subject: [PATCH 49/57] qos-test: nvme test node Convert tests/nvme-test to a driver node; the code to discover the PCI device is replaced by generic qgraph code, therefore we're not placing it in tests/libqos. Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 3 +- tests/nvme-test.c | 80 +++++++++++++++++++++++------------------- 2 files changed, 44 insertions(+), 39 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index 0d06490e99..46f8c346be 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -155,7 +155,6 @@ check-qtest-pci-y += tests/e1000-test$(EXESUF) check-qtest-pci-$(CONFIG_RTL8139_PCI) += tests/rtl8139-test$(EXESUF) check-qtest-pci-$(CONFIG_PCNET_PCI) += tests/pcnet-test$(EXESUF) check-qtest-pci-$(CONFIG_EEPRO100_PCI) += tests/eepro100-test$(EXESUF) -check-qtest-pci-$(CONFIG_NVME_PCI) += tests/nvme-test$(EXESUF) check-qtest-pci-$(CONFIG_ES1370) += tests/es1370-test$(EXESUF) check-qtest-pci-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) check-qtest-pci-$(CONFIG_HDA) += tests/intel-hda-test$(EXESUF) @@ -753,6 +752,7 @@ qos-test-obj-y += tests/ac97-test.o qos-test-obj-y += tests/e1000e-test.o qos-test-obj-y += tests/ipoctal232-test.o qos-test-obj-y += tests/ne2000-test.o +qos-test-obj-y += tests/nvme-test.o qos-test-obj-y += tests/pci-test.o qos-test-obj-y += tests/sdhci-test.o qos-test-obj-$(CONFIG_VHOST_NET_USER) += tests/vhost-user-test.o $(chardev-obj-y) $(test-io-obj-y) @@ -814,7 +814,6 @@ tests/test-hmp$(EXESUF): tests/test-hmp.o tests/machine-none-test$(EXESUF): tests/machine-none-test.o tests/device-plug-test$(EXESUF): tests/device-plug-test.o tests/drive_del-test$(EXESUF): tests/drive_del-test.o -tests/nvme-test$(EXESUF): tests/nvme-test.o $(libqos-pc-obj-y) tests/pvpanic-test$(EXESUF): tests/pvpanic-test.o tests/i82801b11-test$(EXESUF): tests/i82801b11-test.o tests/es1370-test$(EXESUF): tests/es1370-test.o diff --git a/tests/nvme-test.c b/tests/nvme-test.c index 2700ba838a..b48d3a24b9 100644 --- a/tests/nvme-test.c +++ b/tests/nvme-test.c @@ -10,49 +10,47 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "libqtest.h" -#include "libqos/libqos-pc.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" -static QOSState *qnvme_start(const char *extra_opts) +typedef struct QNvme QNvme; + +struct QNvme { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static void *nvme_get_driver(void *obj, const char *interface) { - QOSState *qs; - const char *arch = qtest_get_arch(); - const char *cmd = "-drive id=drv0,if=none,file=null-co://,format=raw " - "-device nvme,addr=0x4.0,serial=foo,drive=drv0 %s"; + QNvme *nvme = obj; - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qs = qtest_pc_boot(cmd, extra_opts ? : ""); - global_qtest = qs->qts; - return qs; + if (!g_strcmp0(interface, "pci-device")) { + return &nvme->dev; } - g_printerr("nvme tests are only available on x86\n"); - exit(EXIT_FAILURE); + fprintf(stderr, "%s not present in nvme\n", interface); + g_assert_not_reached(); } -static void qnvme_stop(QOSState *qs) +static void *nvme_create(void *pci_bus, QGuestAllocator *alloc, void *addr) { - qtest_shutdown(qs); + QNvme *nvme = g_new0(QNvme, 1); + QPCIBus *bus = pci_bus; + + qpci_device_init(&nvme->dev, bus, addr); + nvme->obj.get_driver = nvme_get_driver; + + return &nvme->obj; } -static void nop(void) -{ - QOSState *qs; - - qs = qnvme_start(NULL); - qnvme_stop(qs); -} - -static void nvmetest_cmb_test(void) +/* This used to cause a NULL pointer dereference. */ +static void nvmetest_oob_cmb_test(void *obj, void *data, QGuestAllocator *alloc) { const int cmb_bar_size = 2 * MiB; - QOSState *qs; - QPCIDevice *pdev; + QNvme *nvme = obj; + QPCIDevice *pdev = &nvme->dev; QPCIBar bar; - qs = qnvme_start("-global nvme.cmb_size_mb=2"); - pdev = qpci_device_find(qs->pcibus, QPCI_DEVFN(4,0)); - g_assert(pdev != NULL); - qpci_device_enable(pdev); bar = qpci_iomap(pdev, 2, NULL); @@ -65,16 +63,24 @@ static void nvmetest_cmb_test(void) g_assert_cmpint(qpci_io_readb(pdev, bar, cmb_bar_size - 1), ==, 0x11); g_assert_cmpint(qpci_io_readw(pdev, bar, cmb_bar_size - 1), !=, 0x2211); g_assert_cmpint(qpci_io_readl(pdev, bar, cmb_bar_size - 1), !=, 0x44332211); - g_free(pdev); - - qnvme_stop(qs); } -int main(int argc, char **argv) +static void nvme_register_nodes(void) { - g_test_init(&argc, &argv, NULL); - qtest_add_func("/nvme/nop", nop); - qtest_add_func("/nvme/cmb_test", nvmetest_cmb_test); + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0,drive=drv0,serial=foo", + .before_cmd_line = "-drive id=drv0,if=none,file=null-co://,format=raw", + }; - return g_test_run(); + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("nvme", nvme_create); + qos_node_consumes("nvme", "pci-bus", &opts); + qos_node_produces("nvme", "pci-device"); + + qos_add_test("oob-cmb-access", "nvme", nvmetest_oob_cmb_test, &(QOSGraphTestOptions) { + .edge.extra_device_opts = "cmb_size_mb=2" + }); } + +libqos_init(nvme_register_nodes); From 96900e1610a6429ee5c1a84b663543a12c9ecae1 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Fri, 17 Aug 2018 13:22:12 +0200 Subject: [PATCH 50/57] qos-test: pcnet test node Convert tests/pcnet-test to a driver node; currently it runs the PCI nop test only, therefore we're not placing it in tests/libqos. Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 3 +-- tests/pcnet-test.c | 50 ++++++++++++++++++++++++++++++++---------- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index 46f8c346be..0442be8c9b 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -153,7 +153,6 @@ check-qtest-generic-y += tests/cdrom-test$(EXESUF) check-qtest-pci-y += tests/e1000-test$(EXESUF) check-qtest-pci-$(CONFIG_RTL8139_PCI) += tests/rtl8139-test$(EXESUF) -check-qtest-pci-$(CONFIG_PCNET_PCI) += tests/pcnet-test$(EXESUF) check-qtest-pci-$(CONFIG_EEPRO100_PCI) += tests/eepro100-test$(EXESUF) check-qtest-pci-$(CONFIG_ES1370) += tests/es1370-test$(EXESUF) check-qtest-pci-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) @@ -754,6 +753,7 @@ qos-test-obj-y += tests/ipoctal232-test.o qos-test-obj-y += tests/ne2000-test.o qos-test-obj-y += tests/nvme-test.o qos-test-obj-y += tests/pci-test.o +qos-test-obj-y += tests/pcnet-test.o qos-test-obj-y += tests/sdhci-test.o qos-test-obj-$(CONFIG_VHOST_NET_USER) += tests/vhost-user-test.o $(chardev-obj-y) $(test-io-obj-y) qos-test-obj-y += tests/virtio-test.o @@ -801,7 +801,6 @@ tests/q35-test$(EXESUF): tests/q35-test.o $(libqos-pc-obj-y) tests/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y) tests/e1000-test$(EXESUF): tests/e1000-test.o tests/rtl8139-test$(EXESUF): tests/rtl8139-test.o $(libqos-pc-obj-y) -tests/pcnet-test$(EXESUF): tests/pcnet-test.o tests/pnv-xscom-test$(EXESUF): tests/pnv-xscom-test.o tests/eepro100-test$(EXESUF): tests/eepro100-test.o tests/vmxnet3-test$(EXESUF): tests/vmxnet3-test.o diff --git a/tests/pcnet-test.c b/tests/pcnet-test.c index efb1ef44e9..484448cc64 100644 --- a/tests/pcnet-test.c +++ b/tests/pcnet-test.c @@ -9,23 +9,49 @@ #include "qemu/osdep.h" #include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void pci_nop(void) +typedef struct QPCNet QPCNet; + +struct QPCNet { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static void *pcnet_get_driver(void *obj, const char *interface) { + QPCNet *pcnet = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &pcnet->dev; + } + + fprintf(stderr, "%s not present in pcnet\n", interface); + g_assert_not_reached(); } -int main(int argc, char **argv) +static void *pcnet_create(void *pci_bus, QGuestAllocator *alloc, void *addr) { - int ret; + QPCNet *pcnet = g_new0(QPCNet, 1); + QPCIBus *bus = pci_bus; - g_test_init(&argc, &argv, NULL); - qtest_add_func("/pcnet/pci/nop", pci_nop); + qpci_device_init(&pcnet->dev, bus, addr); + pcnet->obj.get_driver = pcnet_get_driver; - qtest_start("-device pcnet"); - ret = g_test_run(); - - qtest_end(); - - return ret; + return &pcnet->obj; } + +static void pcnet_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("pcnet", pcnet_create); + qos_node_consumes("pcnet", "pci-bus", &opts); + qos_node_produces("pcnet", "pci-device"); +} + +libqos_init(pcnet_register_nodes); From dca063065a5687cbcc2118f5d5bce8f7654cae54 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Fri, 17 Aug 2018 13:35:12 +0200 Subject: [PATCH 51/57] qos-test: spapr-phb test node Convert tests/spapr-phb-test to a qgraph test node, spapr-phb-test. This test adds another spapr-pci-host-bridge device in the ppc64/pseries machine Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 3 +-- tests/spapr-phb-test.c | 32 ++++++++++++++------------------ 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index 0442be8c9b..f257b50806 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -235,7 +235,6 @@ check-qtest-ppc-y += tests/boot-serial-test$(EXESUF) check-qtest-ppc-$(CONFIG_M48T59) += tests/m48t59-test$(EXESUF) check-qtest-ppc64-y += $(check-qtest-ppc-y) -check-qtest-ppc64-$(CONFIG_PSERIES) += tests/spapr-phb-test$(EXESUF) check-qtest-ppc64-$(CONFIG_PSERIES) += tests/device-plug-test$(EXESUF) check-qtest-ppc64-$(CONFIG_POWERNV) += tests/pnv-xscom-test$(EXESUF) check-qtest-ppc64-y += tests/migration-test$(EXESUF) @@ -755,6 +754,7 @@ qos-test-obj-y += tests/nvme-test.o qos-test-obj-y += tests/pci-test.o qos-test-obj-y += tests/pcnet-test.o qos-test-obj-y += tests/sdhci-test.o +qos-test-obj-y += tests/spapr-phb-test.o qos-test-obj-$(CONFIG_VHOST_NET_USER) += tests/vhost-user-test.o $(chardev-obj-y) $(test-io-obj-y) qos-test-obj-y += tests/virtio-test.o qos-test-obj-$(CONFIG_VIRTFS) += tests/virtio-9p-test.o @@ -777,7 +777,6 @@ tests/rtc-test$(EXESUF): tests/rtc-test.o tests/m48t59-test$(EXESUF): tests/m48t59-test.o tests/hexloader-test$(EXESUF): tests/hexloader-test.o tests/endianness-test$(EXESUF): tests/endianness-test.o -tests/spapr-phb-test$(EXESUF): tests/spapr-phb-test.o $(libqos-obj-y) tests/prom-env-test$(EXESUF): tests/prom-env-test.o $(libqos-obj-y) tests/rtas-test$(EXESUF): tests/rtas-test.o $(libqos-spapr-obj-y) tests/fdc-test$(EXESUF): tests/fdc-test.o diff --git a/tests/spapr-phb-test.c b/tests/spapr-phb-test.c index d3522ea093..39b5766710 100644 --- a/tests/spapr-phb-test.c +++ b/tests/spapr-phb-test.c @@ -7,29 +7,25 @@ * 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 "qemu/osdep.h" - #include "libqtest.h" +#include "libqos/qgraph.h" -#define TYPE_SPAPR_PCI_HOST_BRIDGE "spapr-pci-host-bridge" - -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void test_phb_device(void) +/* Tests only initialization so far. TODO: Replace with functional tests, + * for example by producing pci-bus. + */ +static void test_phb_device(void *obj, void *data, QGuestAllocator *alloc) { } -int main(int argc, char **argv) +static void register_phb_test(void) { - int ret; - - g_test_init(&argc, &argv, NULL); - qtest_add_func("/spapr-phb/device", test_phb_device); - - qtest_start("-device " TYPE_SPAPR_PCI_HOST_BRIDGE ",index=30"); - - ret = g_test_run(); - - qtest_end(); - - return ret; + qos_add_test("spapr-phb-test", "ppc64/pseries", + test_phb_device, &(QOSGraphTestOptions) { + .edge.before_cmd_line = "-device spapr-pci-host-bridge" + ",index=30", + }); } + +libqos_init(register_phb_test); From fb7e0b48bde03a3bf07c9682bebdf57f3e448e2a Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Fri, 17 Aug 2018 15:47:17 +0200 Subject: [PATCH 52/57] qos-test: usb-hcd-ohci test node Convert tests/usb-hcd-ohci-test to a driver node; currently it runs the PCI nop test only, therefore we're not placing it in tests/libqos. Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 4 +-- tests/usb-hcd-ohci-test.c | 60 ++++++++++++++++++++++++++++----------- 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index f257b50806..8ba8e27bce 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -184,7 +184,6 @@ check-qtest-i386-$(CONFIG_VMXNET3_PCI) += tests/vmxnet3-test$(EXESUF) check-qtest-i386-$(CONFIG_PVPANIC) += tests/pvpanic-test$(EXESUF) check-qtest-i386-$(CONFIG_I82801B11) += tests/i82801b11-test$(EXESUF) check-qtest-i386-$(CONFIG_IOH3420) += tests/ioh3420-test$(EXESUF) -check-qtest-i386-$(CONFIG_USB_OHCI) += tests/usb-hcd-ohci-test$(EXESUF) check-qtest-i386-$(CONFIG_USB_UHCI) += tests/usb-hcd-uhci-test$(EXESUF) ifeq ($(CONFIG_USB_ECHI)$(CONFIG_USB_UHCI),yy) check-qtest-i386-y += tests/usb-hcd-ehci-test$(EXESUF) @@ -241,7 +240,6 @@ check-qtest-ppc64-y += tests/migration-test$(EXESUF) check-qtest-ppc64-y += tests/test-announce-self$(EXESUF) check-qtest-ppc64-$(CONFIG_PSERIES) += tests/rtas-test$(EXESUF) check-qtest-ppc64-$(CONFIG_SLIRP) += tests/pxe-test$(EXESUF) -check-qtest-ppc64-$(CONFIG_USB_OHCI) += tests/usb-hcd-ohci-test$(EXESUF) check-qtest-ppc64-$(CONFIG_USB_UHCI) += tests/usb-hcd-uhci-test$(EXESUF) check-qtest-ppc64-$(CONFIG_USB_XHCI_NEC) += tests/usb-hcd-xhci-test$(EXESUF) check-qtest-ppc64-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF) @@ -755,6 +753,7 @@ qos-test-obj-y += tests/pci-test.o qos-test-obj-y += tests/pcnet-test.o qos-test-obj-y += tests/sdhci-test.o qos-test-obj-y += tests/spapr-phb-test.o +qos-test-obj-y += tests/usb-hcd-ohci-test.o $(libqos-usb-obj-y) qos-test-obj-$(CONFIG_VHOST_NET_USER) += tests/vhost-user-test.o $(chardev-obj-y) $(test-io-obj-y) qos-test-obj-y += tests/virtio-test.o qos-test-obj-$(CONFIG_VIRTFS) += tests/virtio-9p-test.o @@ -817,7 +816,6 @@ tests/i82801b11-test$(EXESUF): tests/i82801b11-test.o tests/es1370-test$(EXESUF): tests/es1370-test.o tests/intel-hda-test$(EXESUF): tests/intel-hda-test.o tests/ioh3420-test$(EXESUF): tests/ioh3420-test.o -tests/usb-hcd-ohci-test$(EXESUF): tests/usb-hcd-ohci-test.o $(libqos-usb-obj-y) tests/usb-hcd-uhci-test$(EXESUF): tests/usb-hcd-uhci-test.o $(libqos-usb-obj-y) tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-usb-obj-y) tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o $(libqos-usb-obj-y) diff --git a/tests/usb-hcd-ohci-test.c b/tests/usb-hcd-ohci-test.c index 48ddbfd26d..98af02e898 100644 --- a/tests/usb-hcd-ohci-test.c +++ b/tests/usb-hcd-ohci-test.c @@ -10,30 +10,58 @@ #include "qemu/osdep.h" #include "libqtest.h" #include "libqos/usb.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" +typedef struct QOHCI_PCI QOHCI_PCI; -static void test_ohci_init(void) -{ +struct QOHCI_PCI { + QOSGraphObject obj; + QPCIDevice dev; +}; -} - -static void test_ohci_hotplug(void) +static void test_ohci_hotplug(void *obj, void *data, QGuestAllocator *alloc) { usb_test_hotplug("ohci", "1", NULL); } -int main(int argc, char **argv) +static void *ohci_pci_get_driver(void *obj, const char *interface) { - int ret; + QOHCI_PCI *ohci_pci = obj; - g_test_init(&argc, &argv, NULL); + if (!g_strcmp0(interface, "pci-device")) { + return &ohci_pci->dev; + } - qtest_add_func("/ohci/pci/init", test_ohci_init); - qtest_add_func("/ohci/pci/hotplug", test_ohci_hotplug); - - qtest_start("-device pci-ohci,id=ohci"); - ret = g_test_run(); - qtest_end(); - - return ret; + fprintf(stderr, "%s not present in pci-ohci\n", interface); + g_assert_not_reached(); } + +static void *ohci_pci_create(void *pci_bus, QGuestAllocator *alloc, void *addr) +{ + QOHCI_PCI *ohci_pci = g_new0(QOHCI_PCI, 1); + ohci_pci->obj.get_driver = ohci_pci_get_driver; + + return &ohci_pci->obj; +} + +static void ohci_pci_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0,id=ohci", + }; + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("pci-ohci", ohci_pci_create); + qos_node_consumes("pci-ohci", "pci-bus", &opts); + qos_node_produces("pci-ohci", "pci-device"); +} + +libqos_init(ohci_pci_register_nodes); + +static void register_ohci_pci_test(void) +{ + qos_add_test("ohci_pci-test-hotplug", "pci-ohci", test_ohci_hotplug, NULL); +} + +libqos_init(register_ohci_pci_test); From 53bfb4290f8ab01537921629b91185d2f137439a Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Fri, 17 Aug 2018 15:52:52 +0200 Subject: [PATCH 53/57] qos-test: vmxnet3 test node Convert tests/vmxnet3-test to a driver node; currently it runs the PCI nop test only, therefore we're not placing it in tests/libqos. Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 3 +-- tests/vmxnet3-test.c | 50 ++++++++++++++++++++++++++++++++---------- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index 8ba8e27bce..5be98d7368 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -180,7 +180,6 @@ check-qtest-i386-y += tests/drive_del-test$(EXESUF) check-qtest-i386-$(CONFIG_WDT_IB700) += tests/wdt_ib700-test$(EXESUF) check-qtest-i386-y += tests/tco-test$(EXESUF) check-qtest-i386-y += $(check-qtest-pci-y) -check-qtest-i386-$(CONFIG_VMXNET3_PCI) += tests/vmxnet3-test$(EXESUF) check-qtest-i386-$(CONFIG_PVPANIC) += tests/pvpanic-test$(EXESUF) check-qtest-i386-$(CONFIG_I82801B11) += tests/i82801b11-test$(EXESUF) check-qtest-i386-$(CONFIG_IOH3420) += tests/ioh3420-test$(EXESUF) @@ -762,6 +761,7 @@ qos-test-obj-y += tests/virtio-net-test.o qos-test-obj-y += tests/virtio-rng-test.o qos-test-obj-y += tests/virtio-scsi-test.o qos-test-obj-y += tests/virtio-serial-test.o +qos-test-obj-y += tests/vmxnet3-test.o check-unit-y += tests/test-qgraph$(EXESUF) tests/test-qgraph$(EXESUF): tests/test-qgraph.o $(libqgraph-obj-y) @@ -801,7 +801,6 @@ tests/e1000-test$(EXESUF): tests/e1000-test.o tests/rtl8139-test$(EXESUF): tests/rtl8139-test.o $(libqos-pc-obj-y) tests/pnv-xscom-test$(EXESUF): tests/pnv-xscom-test.o tests/eepro100-test$(EXESUF): tests/eepro100-test.o -tests/vmxnet3-test$(EXESUF): tests/vmxnet3-test.o tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y) tests/virtio-ccw-test$(EXESUF): tests/virtio-ccw-test.o diff --git a/tests/vmxnet3-test.c b/tests/vmxnet3-test.c index 159c0ad728..35cdea939b 100644 --- a/tests/vmxnet3-test.c +++ b/tests/vmxnet3-test.c @@ -9,23 +9,49 @@ #include "qemu/osdep.h" #include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void nop(void) +typedef struct QVmxnet3 QVmxnet3; + +struct QVmxnet3 { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static void *vmxnet3_get_driver(void *obj, const char *interface) { + QVmxnet3 *vmxnet3 = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &vmxnet3->dev; + } + + fprintf(stderr, "%s not present in vmxnet3\n", interface); + g_assert_not_reached(); } -int main(int argc, char **argv) +static void *vmxnet3_create(void *pci_bus, QGuestAllocator *alloc, void *addr) { - int ret; + QVmxnet3 *vmxnet3 = g_new0(QVmxnet3, 1); + QPCIBus *bus = pci_bus; - g_test_init(&argc, &argv, NULL); - qtest_add_func("/vmxnet3/nop", nop); + qpci_device_init(&vmxnet3->dev, bus, addr); + vmxnet3->obj.get_driver = vmxnet3_get_driver; - qtest_start("-device vmxnet3"); - ret = g_test_run(); - - qtest_end(); - - return ret; + return &vmxnet3->obj; } + +static void vmxnet3_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("vmxnet3", vmxnet3_create); + qos_node_consumes("vmxnet3", "pci-bus", &opts); + qos_node_produces("vmxnet3", "pci-device"); +} + +libqos_init(vmxnet3_register_nodes); From 7f4090a56d455881f261817be47c96f6d278da81 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Fri, 17 Aug 2018 16:03:36 +0200 Subject: [PATCH 54/57] qos-test: es1370 test node Convert tests/es1370-test to a driver node; currently it runs the PCI nop test only, therefore we're not placing it in tests/libqos. Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 3 +-- tests/es1370-test.c | 50 ++++++++++++++++++++++++++++++++---------- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index 5be98d7368..4a387b6269 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -154,7 +154,6 @@ check-qtest-generic-y += tests/cdrom-test$(EXESUF) check-qtest-pci-y += tests/e1000-test$(EXESUF) check-qtest-pci-$(CONFIG_RTL8139_PCI) += tests/rtl8139-test$(EXESUF) check-qtest-pci-$(CONFIG_EEPRO100_PCI) += tests/eepro100-test$(EXESUF) -check-qtest-pci-$(CONFIG_ES1370) += tests/es1370-test$(EXESUF) check-qtest-pci-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) check-qtest-pci-$(CONFIG_HDA) += tests/intel-hda-test$(EXESUF) check-qtest-pci-$(CONFIG_IVSHMEM_DEVICE) += tests/ivshmem-test$(EXESUF) @@ -745,6 +744,7 @@ qos-test-obj-y += tests/libqos/x86_64_pc-machine.o # Tests qos-test-obj-y += tests/ac97-test.o qos-test-obj-y += tests/e1000e-test.o +qos-test-obj-y += tests/es1370-test.o qos-test-obj-y += tests/ipoctal232-test.o qos-test-obj-y += tests/ne2000-test.o qos-test-obj-y += tests/nvme-test.o @@ -812,7 +812,6 @@ tests/device-plug-test$(EXESUF): tests/device-plug-test.o tests/drive_del-test$(EXESUF): tests/drive_del-test.o tests/pvpanic-test$(EXESUF): tests/pvpanic-test.o tests/i82801b11-test$(EXESUF): tests/i82801b11-test.o -tests/es1370-test$(EXESUF): tests/es1370-test.o tests/intel-hda-test$(EXESUF): tests/intel-hda-test.o tests/ioh3420-test$(EXESUF): tests/ioh3420-test.o tests/usb-hcd-uhci-test$(EXESUF): tests/usb-hcd-uhci-test.o $(libqos-usb-obj-y) diff --git a/tests/es1370-test.c b/tests/es1370-test.c index 199fe193ce..d845cd06f8 100644 --- a/tests/es1370-test.c +++ b/tests/es1370-test.c @@ -9,23 +9,49 @@ #include "qemu/osdep.h" #include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void nop(void) +typedef struct QES1370 QES1370; + +struct QES1370 { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static void *es1370_get_driver(void *obj, const char *interface) { + QES1370 *es1370 = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &es1370->dev; + } + + fprintf(stderr, "%s not present in e1000e\n", interface); + g_assert_not_reached(); } -int main(int argc, char **argv) +static void *es1370_create(void *pci_bus, QGuestAllocator *alloc, void *addr) { - int ret; + QES1370 *es1370 = g_new0(QES1370, 1); + QPCIBus *bus = pci_bus; - g_test_init(&argc, &argv, NULL); - qtest_add_func("/es1370/nop", nop); + qpci_device_init(&es1370->dev, bus, addr); + es1370->obj.get_driver = es1370_get_driver; - qtest_start("-device ES1370"); - ret = g_test_run(); - - qtest_end(); - - return ret; + return &es1370->obj; } + +static void es1370_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("ES1370", es1370_create); + qos_node_consumes("ES1370", "pci-bus", &opts); + qos_node_produces("ES1370", "pci-device"); +} + +libqos_init(es1370_register_nodes); From 0b477302343b8a981a9a7e01c1c1e7da323b991f Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Fri, 17 Aug 2018 16:13:25 +0200 Subject: [PATCH 55/57] qos-test: eepro100 test node Convert tests/eepro100-test to a driver node; currently it runs the PCI nop test only, therefore we're not placing it in tests/libqos. For now, all nodes share the same constructor and destructor. Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 3 +- tests/eepro100-test.c | 67 ++++++++++++++++++++++++++---------------- 2 files changed, 42 insertions(+), 28 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index 4a387b6269..befeb8fe4a 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -153,7 +153,6 @@ check-qtest-generic-y += tests/cdrom-test$(EXESUF) check-qtest-pci-y += tests/e1000-test$(EXESUF) check-qtest-pci-$(CONFIG_RTL8139_PCI) += tests/rtl8139-test$(EXESUF) -check-qtest-pci-$(CONFIG_EEPRO100_PCI) += tests/eepro100-test$(EXESUF) check-qtest-pci-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) check-qtest-pci-$(CONFIG_HDA) += tests/intel-hda-test$(EXESUF) check-qtest-pci-$(CONFIG_IVSHMEM_DEVICE) += tests/ivshmem-test$(EXESUF) @@ -744,6 +743,7 @@ qos-test-obj-y += tests/libqos/x86_64_pc-machine.o # Tests qos-test-obj-y += tests/ac97-test.o qos-test-obj-y += tests/e1000e-test.o +qos-test-obj-y += tests/eepro100-test.o qos-test-obj-y += tests/es1370-test.o qos-test-obj-y += tests/ipoctal232-test.o qos-test-obj-y += tests/ne2000-test.o @@ -800,7 +800,6 @@ tests/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y) tests/e1000-test$(EXESUF): tests/e1000-test.o tests/rtl8139-test$(EXESUF): tests/rtl8139-test.o $(libqos-pc-obj-y) tests/pnv-xscom-test$(EXESUF): tests/pnv-xscom-test.o -tests/eepro100-test$(EXESUF): tests/eepro100-test.o tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y) tests/virtio-ccw-test$(EXESUF): tests/virtio-ccw-test.o diff --git a/tests/eepro100-test.c b/tests/eepro100-test.c index bdc8a67d57..90b5c1afd9 100644 --- a/tests/eepro100-test.c +++ b/tests/eepro100-test.c @@ -9,23 +9,15 @@ #include "qemu/osdep.h" #include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" -static void test_device(gconstpointer data) -{ - const char *model = data; - QTestState *s; - char *args; +typedef struct QEEPRO100 QEEPRO100; - args = g_strdup_printf("-device %s", model); - s = qtest_start(args); - - /* Tests only initialization so far. TODO: Implement functional tests */ - - if (s) { - qtest_quit(s); - } - g_free(args); -} +struct QEEPRO100 { + QOSGraphObject obj; + QPCIDevice dev; +}; static const char *models[] = { "i82550", @@ -43,19 +35,42 @@ static const char *models[] = { "i82801", }; -int main(int argc, char **argv) +static void *eepro100_get_driver(void *obj, const char *interface) { - int i; + QEEPRO100 *eepro100 = obj; - g_test_init(&argc, &argv, NULL); - - for (i = 0; i < ARRAY_SIZE(models); i++) { - char *path; - - path = g_strdup_printf("eepro100/%s", models[i]); - qtest_add_data_func(path, models[i], test_device); - g_free(path); + if (!g_strcmp0(interface, "pci-device")) { + return &eepro100->dev; } - return g_test_run(); + fprintf(stderr, "%s not present in eepro100\n", interface); + g_assert_not_reached(); } + +static void *eepro100_create(void *pci_bus, QGuestAllocator *alloc, void *addr) +{ + QEEPRO100 *eepro100 = g_new0(QEEPRO100, 1); + QPCIBus *bus = pci_bus; + + qpci_device_init(&eepro100->dev, bus, addr); + eepro100->obj.get_driver = eepro100_get_driver; + + return &eepro100->obj; +} + +static void eepro100_register_nodes(void) +{ + int i; + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + for (i = 0; i < ARRAY_SIZE(models); i++) { + qos_node_create_driver(models[i], eepro100_create); + qos_node_consumes(models[i], "pci-bus", &opts); + qos_node_produces(models[i], "pci-device"); + } +} + +libqos_init(eepro100_register_nodes); From a138f26623e15ec721489f3164fbd7eb03a6dc98 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Fri, 17 Aug 2018 16:16:22 +0200 Subject: [PATCH 56/57] qos-test: e1000 test node Convert tests/e1000-test to a driver node; currently it runs the PCI nop test only, therefore we're not placing it in tests/libqos. Signed-off-by: Emanuele Giuseppe Esposito Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 3 +- tests/e1000-test.c | 66 ++++++++++++++++++++++++++---------------- 2 files changed, 42 insertions(+), 27 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index befeb8fe4a..5bc6d636dc 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -151,7 +151,6 @@ check-qtest-generic-y += tests/qmp-cmd-test$(EXESUF) check-qtest-generic-y += tests/device-introspect-test$(EXESUF) check-qtest-generic-y += tests/cdrom-test$(EXESUF) -check-qtest-pci-y += tests/e1000-test$(EXESUF) check-qtest-pci-$(CONFIG_RTL8139_PCI) += tests/rtl8139-test$(EXESUF) check-qtest-pci-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) check-qtest-pci-$(CONFIG_HDA) += tests/intel-hda-test$(EXESUF) @@ -742,6 +741,7 @@ qos-test-obj-y += tests/libqos/x86_64_pc-machine.o # Tests qos-test-obj-y += tests/ac97-test.o +qos-test-obj-y += tests/e1000-test.o qos-test-obj-y += tests/e1000e-test.o qos-test-obj-y += tests/eepro100-test.o qos-test-obj-y += tests/es1370-test.o @@ -797,7 +797,6 @@ tests/m25p80-test$(EXESUF): tests/m25p80-test.o tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y) tests/q35-test$(EXESUF): tests/q35-test.o $(libqos-pc-obj-y) tests/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y) -tests/e1000-test$(EXESUF): tests/e1000-test.o tests/rtl8139-test$(EXESUF): tests/rtl8139-test.o $(libqos-pc-obj-y) tests/pnv-xscom-test$(EXESUF): tests/pnv-xscom-test.o tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o diff --git a/tests/e1000-test.c b/tests/e1000-test.c index 0c5fcdcc44..9e67916169 100644 --- a/tests/e1000-test.c +++ b/tests/e1000-test.c @@ -9,22 +9,15 @@ #include "qemu/osdep.h" #include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void test_device(gconstpointer data) -{ - const char *model = data; - QTestState *s; - char *args; +typedef struct QE1000 QE1000; - args = g_strdup_printf("-device %s", model); - s = qtest_start(args); - - if (s) { - qtest_quit(s); - } - g_free(args); -} +struct QE1000 { + QOSGraphObject obj; + QPCIDevice dev; +}; static const char *models[] = { "e1000", @@ -33,19 +26,42 @@ static const char *models[] = { "e1000-82545em", }; -int main(int argc, char **argv) +static void *e1000_get_driver(void *obj, const char *interface) { - int i; + QE1000 *e1000 = obj; - g_test_init(&argc, &argv, NULL); - - for (i = 0; i < ARRAY_SIZE(models); i++) { - char *path; - - path = g_strdup_printf("e1000/%s", models[i]); - qtest_add_data_func(path, models[i], test_device); - g_free(path); + if (!g_strcmp0(interface, "pci-device")) { + return &e1000->dev; } - return g_test_run(); + fprintf(stderr, "%s not present in e1000e\n", interface); + g_assert_not_reached(); } + +static void *e1000_create(void *pci_bus, QGuestAllocator *alloc, void *addr) +{ + QE1000 *e1000 = g_new0(QE1000, 1); + QPCIBus *bus = pci_bus; + + qpci_device_init(&e1000->dev, bus, addr); + e1000->obj.get_driver = e1000_get_driver; + + return &e1000->obj; +} + +static void e1000_register_nodes(void) +{ + int i; + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + for (i = 0; i < ARRAY_SIZE(models); i++) { + qos_node_create_driver(models[i], e1000_create); + qos_node_consumes(models[i], "pci-bus", &opts); + qos_node_produces(models[i], "pci-device"); + } +} + +libqos_init(e1000_register_nodes); From 469bb49b3e131b5f641939d4fa6a4b09e6da47f8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 29 Nov 2018 11:35:13 +0100 Subject: [PATCH 57/57] qos-test: megasas test node Convert tests/megasas-test to a driver node; the code to discover the PCI device is replaced by generic qgraph code. Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 3 +- tests/megasas-test.c | 78 +++++++++++++++++++++--------------------- 2 files changed, 40 insertions(+), 41 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index 5bc6d636dc..44af41515b 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -155,7 +155,6 @@ check-qtest-pci-$(CONFIG_RTL8139_PCI) += tests/rtl8139-test$(EXESUF) check-qtest-pci-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) check-qtest-pci-$(CONFIG_HDA) += tests/intel-hda-test$(EXESUF) check-qtest-pci-$(CONFIG_IVSHMEM_DEVICE) += tests/ivshmem-test$(EXESUF) -check-qtest-pci-$(CONFIG_MEGASAS_SCSI_PCI) += tests/megasas-test$(EXESUF) check-qtest-i386-$(CONFIG_ISA_TESTDEV) = tests/endianness-test$(EXESUF) check-qtest-i386-y += tests/fdc-test$(EXESUF) @@ -746,6 +745,7 @@ qos-test-obj-y += tests/e1000e-test.o qos-test-obj-y += tests/eepro100-test.o qos-test-obj-y += tests/es1370-test.o qos-test-obj-y += tests/ipoctal232-test.o +qos-test-obj-y += tests/megasas-test.o qos-test-obj-y += tests/ne2000-test.o qos-test-obj-y += tests/nvme-test.o qos-test-obj-y += tests/pci-test.o @@ -827,7 +827,6 @@ tests/test-filter-mirror$(EXESUF): tests/test-filter-mirror.o $(qtest-obj-y) tests/test-filter-redirector$(EXESUF): tests/test-filter-redirector.o $(qtest-obj-y) tests/test-x86-cpuid-compat$(EXESUF): tests/test-x86-cpuid-compat.o $(qtest-obj-y) tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y) $(libqos-spapr-obj-y) -tests/megasas-test$(EXESUF): tests/megasas-test.o $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o $(test-util-obj-y) libvhost-user.a tests/test-uuid$(EXESUF): tests/test-uuid.o $(test-util-obj-y) tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o diff --git a/tests/megasas-test.c b/tests/megasas-test.c index 81837e14af..33aa97042c 100644 --- a/tests/megasas-test.c +++ b/tests/megasas-test.c @@ -10,55 +10,49 @@ #include "qemu/osdep.h" #include "libqtest.h" #include "qemu/bswap.h" -#include "libqos/libqos-pc.h" -#include "libqos/libqos-spapr.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" -static QOSState *qmegasas_start(const char *extra_opts) +typedef struct QMegasas QMegasas; + +struct QMegasas { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static void *megasas_get_driver(void *obj, const char *interface) { - QOSState *qs; - const char *arch = qtest_get_arch(); - const char *cmd = "-drive id=hd0,if=none,file=null-co://,format=raw " - "-device megasas,id=scsi0,addr=04.0 " - "-device scsi-hd,bus=scsi0.0,drive=hd0 %s"; + QMegasas *megasas = obj; - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qs = qtest_pc_boot(cmd, extra_opts ? : ""); - global_qtest = qs->qts; - return qs; + if (!g_strcmp0(interface, "pci-device")) { + return &megasas->dev; } - g_printerr("virtio-scsi tests are only available on x86 or ppc64\n"); - exit(EXIT_FAILURE); + fprintf(stderr, "%s not present in megasas\n", interface); + g_assert_not_reached(); } -static void qmegasas_stop(QOSState *qs) +static void *megasas_create(void *pci_bus, QGuestAllocator *alloc, void *addr) { - qtest_shutdown(qs); -} + QMegasas *megasas = g_new0(QMegasas, 1); + QPCIBus *bus = pci_bus; -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void pci_nop(void) -{ - QOSState *qs; + qpci_device_init(&megasas->dev, bus, addr); + megasas->obj.get_driver = megasas_get_driver; - qs = qmegasas_start(NULL); - qmegasas_stop(qs); + return &megasas->obj; } /* This used to cause a NULL pointer dereference. */ -static void megasas_pd_get_info_fuzz(void) +static void megasas_pd_get_info_fuzz(void *obj, void *data, QGuestAllocator *alloc) { - QPCIDevice *dev; - QOSState *qs; + QMegasas *megasas = obj; + QPCIDevice *dev = &megasas->dev; QPCIBar bar; uint32_t context[256]; uint64_t context_pa; int i; - qs = qmegasas_start(NULL); - dev = qpci_device_find(qs->pcibus, QPCI_DEVFN(4,0)); - g_assert(dev != NULL); - qpci_device_enable(dev); bar = qpci_iomap(dev, 0, NULL); @@ -71,19 +65,25 @@ static void megasas_pd_get_info_fuzz(void) context[6] = cpu_to_le32(0x02020000); context[7] = cpu_to_le32(0); - context_pa = qmalloc(qs, sizeof(context)); + context_pa = guest_alloc(alloc, sizeof(context)); memwrite(context_pa, context, sizeof(context)); qpci_io_writel(dev, bar, 0x40, context_pa); - - g_free(dev); - qmegasas_stop(qs); } -int main(int argc, char **argv) +static void megasas_register_nodes(void) { - g_test_init(&argc, &argv, NULL); - qtest_add_func("/megasas/pci/nop", pci_nop); - qtest_add_func("/megasas/dcmd/pd-get-info/fuzz", megasas_pd_get_info_fuzz); + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0,id=scsi0", + .before_cmd_line = "-drive id=drv0,if=none,file=null-co://,format=raw", + .after_cmd_line = "-device scsi-hd,bus=scsi0.0,drive=drv0", + }; - return g_test_run(); + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("megasas", megasas_create); + qos_node_consumes("megasas", "pci-bus", &opts); + qos_node_produces("megasas", "pci-device"); + + qos_add_test("dcmd/pd-get-info/fuzz", "megasas", megasas_pd_get_info_fuzz, NULL); } +libqos_init(megasas_register_nodes);