The previous test cases for virtio-serial only tested initialization of the device. I've included four new test cases: rx for virtconsole, tx for virtconsole, rx for virtserialport, tx for virtserialport. It follows the general pattern of virtio-net (i.e. chardev file descriptor backend with a socketpair connected via fork-exec).
Signed-off-by: Daniel Hoffman <dhoff...@gmail.com> --- tests/qtest/libqos/virtio-serial.c | 51 +++++++++ tests/qtest/libqos/virtio-serial.h | 2 + tests/qtest/virtio-serial-test.c | 177 ++++++++++++++++++++++++++++- 3 files changed, 228 insertions(+), 2 deletions(-) diff --git a/tests/qtest/libqos/virtio-serial.c b/tests/qtest/libqos/virtio-serial.c index 1d689c3e38..8723bffe1b 100644 --- a/tests/qtest/libqos/virtio-serial.c +++ b/tests/qtest/libqos/virtio-serial.c @@ -22,6 +22,10 @@ #include "qgraph.h" #include "virtio-serial.h" +#include "qemu/iov.h" + +static QGuestAllocator *alloc; + static void *qvirtio_serial_get_driver(QVirtioSerial *v_serial, const char *interface) { @@ -43,6 +47,33 @@ static void *qvirtio_serial_device_get_driver(void *object, return qvirtio_serial_get_driver(&v_serial->serial, interface); } +static void virtio_serial_setup(QVirtioSerial *interface) +{ + QVirtioDevice *vdev = interface->vdev; + qvirtio_set_features(vdev, (1ULL << 1) | (1ULL << 32)); + + interface->n_queues = 6; + interface->queues = g_new(QVirtQueue*, interface->n_queues); + + for (int i = 0; i < interface->n_queues; i++) { + interface->queues[i] = qvirtqueue_setup(interface->vdev, alloc, i); + } + + qvirtio_set_driver_ok(vdev); +} + +static void qvirtio_serial_device_destructor(QOSGraphObject *obj) +{ +} + +static void qvirtio_serial_device_start_hw(QOSGraphObject *obj) +{ + QVirtioSerialDevice *v_serial = (QVirtioSerialDevice *)obj; + QVirtioSerial *interface = &v_serial->serial; + + virtio_serial_setup(interface); +} + static void *virtio_serial_device_create(void *virtio_dev, QGuestAllocator *t_alloc, void *addr) @@ -51,13 +82,30 @@ static void *virtio_serial_device_create(void *virtio_dev, QVirtioSerial *interface = &virtio_device->serial; interface->vdev = virtio_dev; + alloc = t_alloc; + virtio_device->obj.destructor = qvirtio_serial_device_destructor; + virtio_device->obj.start_hw = qvirtio_serial_device_start_hw; virtio_device->obj.get_driver = qvirtio_serial_device_get_driver; return &virtio_device->obj; } /* virtio-serial-pci */ +static void qvirtio_serial_pci_destructor(QOSGraphObject *obj) +{ +} + +static void qvirtio_serial_pci_start_hw(QOSGraphObject *obj) +{ + QVirtioSerialPCI *v_serial = (QVirtioSerialPCI *) obj; + QVirtioSerial *interface = &v_serial->serial; + QOSGraphObject *pci_vobj = &v_serial->pci_vdev.obj; + + qvirtio_pci_start_hw(pci_vobj); + virtio_serial_setup(interface); +} + static void *qvirtio_serial_pci_get_driver(void *object, const char *interface) { QVirtioSerialPCI *v_serial = object; @@ -76,7 +124,10 @@ static void *virtio_serial_pci_create(void *pci_bus, QGuestAllocator *t_alloc, virtio_pci_init(&virtio_spci->pci_vdev, pci_bus, addr); interface->vdev = &virtio_spci->pci_vdev.vdev; + alloc = t_alloc; + obj->destructor = qvirtio_serial_pci_destructor; + obj->start_hw = qvirtio_serial_pci_start_hw; obj->get_driver = qvirtio_serial_pci_get_driver; return obj; diff --git a/tests/qtest/libqos/virtio-serial.h b/tests/qtest/libqos/virtio-serial.h index 3db43b2bb8..ce6ae164cb 100644 --- a/tests/qtest/libqos/virtio-serial.h +++ b/tests/qtest/libqos/virtio-serial.h @@ -29,6 +29,8 @@ typedef struct QVirtioSerialDevice QVirtioSerialDevice; struct QVirtioSerial { QVirtioDevice *vdev; + int n_queues; + QVirtQueue **queues; }; struct QVirtioSerialPCI { diff --git a/tests/qtest/virtio-serial-test.c b/tests/qtest/virtio-serial-test.c index 2541034822..190075d6f5 100644 --- a/tests/qtest/virtio-serial-test.c +++ b/tests/qtest/virtio-serial-test.c @@ -11,6 +11,36 @@ #include "libqtest-single.h" #include "qemu/module.h" #include "libqos/virtio-serial.h" +#include "standard-headers/linux/virtio_console.h" +#include "qemu/iov.h" + +static void virtio_serial_test_cleanup(void *sockets) +{ + int *sv = sockets; + + close(sv[0]); + qos_invalidate_command_line(); + close(sv[1]); + g_free(sv); +} + +static void *virtio_serial_test_setup(GString *cmd_line, void *arg) +{ + int ret; + int *sv = g_new(int, 3); + + ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sv); + g_assert_cmpint(ret, !=, -1); + + g_string_append_printf( + cmd_line, + " -chardev socket,fd=%d,id=virtioserial0", + sv[1]); + + sv[2] = arg ? 1 : 0; + g_test_queue_destroy(virtio_serial_test_cleanup, sv); + return sv; +} /* Tests only initialization so far. TODO: Replace with functional tests */ static void virtio_serial_nop(void *obj, void *data, QGuestAllocator *alloc) @@ -18,6 +48,132 @@ static void virtio_serial_nop(void *obj, void *data, QGuestAllocator *alloc) /* no operation */ } +static void tx_test( + QVirtioDevice *dev, + QGuestAllocator *alloc, + QVirtQueue *vq, + int socket) +{ + QTestState *qts = global_qtest; + uint64_t req_addr; + uint64_t free_head; + char test[] = "TEST"; + char buffer[5]; + struct iovec iov[] = { + { + .iov_base = buffer, + .iov_len = strlen(test) + } + }; + int ret; + + req_addr = guest_alloc(alloc, 4); + qtest_memwrite(qts, req_addr, test, strlen(test)); + + free_head = qvirtqueue_add(qts, vq, req_addr, 4, false, false); + qvirtqueue_kick(qts, dev, vq, free_head); + + ret = iov_recv(socket, iov, 1, 0, strlen(test)); + g_assert_cmpint(ret, ==, strlen(test)); + + buffer[strlen(test)] = '\0'; + g_assert_cmpstr(buffer, ==, test); + + guest_free(alloc, req_addr); +} + +static void rx_test( + QVirtioDevice *dev, + QGuestAllocator *alloc, + QVirtQueue *vq, + int socket) +{ + QTestState *qts = global_qtest; + uint64_t req_addr; + uint64_t free_head; + char test[] = "TEST"; + char buffer[5]; + struct iovec iov[] = { + { + .iov_base = test, + .iov_len = strlen(test) + } + }; + int ret; + + req_addr = guest_alloc(alloc, 4); + + free_head = qvirtqueue_add(qts, vq, req_addr, 4, true, false); + qvirtqueue_kick(qts, dev, vq, free_head); + + ret = iov_send(socket, iov, 1, 0, strlen(test)); + g_assert_cmpint(ret, ==, strlen(test)); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, 5 * 1000 * 1000); + qtest_memread(qts, req_addr, buffer, strlen(test)); + + buffer[strlen(test)] = '\0'; + g_assert_cmpstr(buffer, ==, test); + + guest_free(alloc, req_addr); +} + +static void send_recv_test(void *obj, void *data, QGuestAllocator *alloc) +{ + QVirtioSerial *serial_if = obj; + QVirtioDevice *dev = serial_if->vdev; + uint32_t port_open_addr, port_open_free_head; + int *sv = data; + + /* + * the first port is always virtconsole due to backwards compatibility + * consideraitons so we must use the multiport feature to add the correct + * port + */ + QVirtQueue *rx = serial_if->queues[sv[2] == 0 ? 0 : 4]; + QVirtQueue *tx = serial_if->queues[sv[2] == 0 ? 1 : 5]; + QVirtQueue *control_tx = serial_if->queues[3]; + + port_open_addr = guest_alloc(alloc, 8); + + qtest_writel(global_qtest, port_open_addr + 0, sv[2]); + qtest_writew(global_qtest, port_open_addr + 4, VIRTIO_CONSOLE_PORT_READY); + qtest_writew(global_qtest, port_open_addr + 6, 1); + port_open_free_head = qvirtqueue_add( + global_qtest, + control_tx, + port_open_addr, + 8, + false, + false); + qvirtqueue_kick( + global_qtest, + dev, + control_tx, + port_open_free_head); + + qtest_writel(global_qtest, port_open_addr + 0, sv[2]); + qtest_writew(global_qtest, port_open_addr + 4, VIRTIO_CONSOLE_PORT_OPEN); + qtest_writew(global_qtest, port_open_addr + 6, 1); + port_open_free_head = qvirtqueue_add( + global_qtest, + control_tx, + port_open_addr, + 8, + false, + false); + qvirtqueue_kick( + global_qtest, + dev, + control_tx, + port_open_free_head); + + guest_free(alloc, port_open_addr); + + tx_test(dev, alloc, tx, sv[0]); + rx_test(dev, alloc, rx, sv[0]); +} + static void serial_hotplug(void *obj, void *data, QGuestAllocator *alloc) { qtest_qmp_device_add(global_qtest, "virtserialport", "hp-port", "{}"); @@ -28,12 +184,29 @@ static void register_virtio_serial_test(void) { QOSGraphTestOptions opts = { }; - opts.edge.before_cmd_line = "-device virtconsole,bus=vser0.0"; + opts.before = virtio_serial_test_setup; + + opts.arg = (gpointer)0; + opts.edge.before_cmd_line = + "-device virtconsole,bus=vser0.0,chardev=virtioserial0"; qos_add_test("console-nop", "virtio-serial", virtio_serial_nop, &opts); + qos_add_test( + "console-send-recv", + "virtio-serial", + send_recv_test, + &opts); - opts.edge.before_cmd_line = "-device virtserialport,bus=vser0.0"; + opts.arg = (gpointer)1; + opts.edge.before_cmd_line = + "-device virtserialport,bus=vser0.0,chardev=virtioserial0"; qos_add_test("serialport-nop", "virtio-serial", virtio_serial_nop, &opts); + qos_add_test( + "serialport-send-recv", + "virtio-serial", + send_recv_test, + &opts); + qos_add_test("hotplug", "virtio-serial", serial_hotplug, NULL); } libqos_init(register_virtio_serial_test); -- 2.34.1