new file mode 100644
@@ -0,0 +1,34 @@
+/*
+ * virtio-gpio structures
+ *
+ * Copyright (c) 2022 Linaro Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef TESTS_LIBQOS_VIRTIO_GPIO_H
+#define TESTS_LIBQOS_VIRTIO_GPIO_H
+
+#include "qgraph.h"
+#include "virtio.h"
+#include "virtio-pci.h"
+
+typedef struct QVhostUserGPIO QVhostUserGPIO;
+typedef struct QVhostUserGPIOPCI QVhostUserGPIOPCI;
+typedef struct QVhostUserGPIODevice QVhostUserGPIODevice;
+
+struct QVhostUserGPIO {
+ QVirtioDevice *vdev;
+};
+
+struct QVhostUserGPIOPCI {
+ QVirtioPCIDevice pci_vdev;
+ QVhostUserGPIO gpio;
+};
+
+struct QVhostUserGPIODevice {
+ QOSGraphObject obj;
+ QVhostUserGPIO gpio;
+};
+
+#endif
new file mode 100644
@@ -0,0 +1,98 @@
+/*
+ * virtio-gpio nodes for testing
+ *
+ * Copyright (c) 2022 Linaro Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qemu/module.h"
+#include "qgraph.h"
+#include "virtio-gpio.h"
+
+static void *qvirtio_gpio_get_driver(QVhostUserGPIO *v_gpio,
+ const char *interface)
+{
+ if (!g_strcmp0(interface, "vhost-user-gpio")) {
+ return v_gpio;
+ }
+ if (!g_strcmp0(interface, "virtio")) {
+ return v_gpio->vdev;
+ }
+
+ fprintf(stderr, "%s not present in virtio-gpio-device\n", interface);
+ g_assert_not_reached();
+}
+
+static void *qvirtio_gpio_device_get_driver(void *object,
+ const char *interface)
+{
+ QVhostUserGPIODevice *v_gpio = object;
+ return qvirtio_gpio_get_driver(&v_gpio->gpio, interface);
+}
+
+static void *virtio_gpio_device_create(void *virtio_dev,
+ QGuestAllocator *t_alloc,
+ void *addr)
+{
+ QVhostUserGPIODevice *virtio_device = g_new0(QVhostUserGPIODevice, 1);
+ QVhostUserGPIO *interface = &virtio_device->gpio;
+
+ interface->vdev = virtio_dev;
+
+ virtio_device->obj.get_driver = qvirtio_gpio_device_get_driver;
+
+ return &virtio_device->obj;
+}
+
+/* virtio-gpio-pci */
+static void *qvirtio_gpio_pci_get_driver(void *object, const char *interface)
+{
+ QVhostUserGPIOPCI *v_gpio = object;
+ if (!g_strcmp0(interface, "pci-device")) {
+ return v_gpio->pci_vdev.pdev;
+ }
+ return qvirtio_gpio_get_driver(&v_gpio->gpio, interface);
+}
+
+static void *virtio_gpio_pci_create(void *pci_bus, QGuestAllocator *t_alloc,
+ void *addr)
+{
+ QVhostUserGPIOPCI *virtio_spci = g_new0(QVhostUserGPIOPCI, 1);
+ QVhostUserGPIO *interface = &virtio_spci->gpio;
+ 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_gpio_pci_get_driver;
+
+ return obj;
+}
+
+static void virtio_gpio_register_nodes(void)
+{
+ QPCIAddress addr = {
+ .devfn = QPCI_DEVFN(4, 0),
+ };
+
+ QOSGraphEdgeOptions edge_opts = { };
+
+ /* vhost-user-gpio-device */
+ edge_opts.extra_device_opts = "id=gpio0,chardev=chr-vhost-user-test";
+ qos_node_create_driver("vhost-user-gpio-device",
+ virtio_gpio_device_create);
+ qos_node_consumes("vhost-user-gpio-device", "virtio-bus", &edge_opts);
+ qos_node_produces("vhost-user-gpio-device", "vhost-user-gpio");
+
+ /* virtio-gpio-pci */
+ edge_opts.extra_device_opts = "id=gpio0,addr=04.0,chardev=chr-vhost-user-test";
+ add_qpci_address(&edge_opts, &addr);
+ qos_node_create_driver("vhost-user-gpio-pci", virtio_gpio_pci_create);
+ qos_node_consumes("vhost-user-gpio-pci", "pci-bus", &edge_opts);
+ qos_node_produces("vhost-user-gpio-pci", "vhost-user-gpio");
+}
+
+libqos_init(virtio_gpio_register_nodes);
@@ -55,6 +55,7 @@
#define VHOST_USER_PROTOCOL_F_MQ 0
#define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1
#define VHOST_USER_PROTOCOL_F_CROSS_ENDIAN 6
+#define VHOST_USER_PROTOCOL_F_CONFIG 9
#define VHOST_LOG_PAGE 0x1000
@@ -78,6 +79,8 @@ typedef enum VhostUserRequest {
VHOST_USER_SET_PROTOCOL_FEATURES = 16,
VHOST_USER_GET_QUEUE_NUM = 17,
VHOST_USER_SET_VRING_ENABLE = 18,
+ VHOST_USER_GET_CONFIG = 24,
+ VHOST_USER_SET_CONFIG = 25,
VHOST_USER_MAX
} VhostUserRequest;
@@ -137,6 +140,7 @@ enum {
enum {
VHOST_USER_NET,
+ VHOST_USER_GPIO,
};
typedef struct TestServer {
@@ -194,6 +198,19 @@ static void append_vhost_net_opts(TestServer *s, GString *cmd_line,
chr_opts, s->chr_name);
}
+/*
+ * For GPIO there are no other magic devices we need to add (like
+ * block or netdev) so all we need to worry about is the vhost-user
+ * chardev socket.
+ */
+static void append_vhost_gpio_opts(TestServer *s, GString *cmd_line,
+ const char *chr_opts)
+{
+ g_string_append_printf(cmd_line, QEMU_CMD_CHR,
+ s->chr_name, s->socket_path,
+ chr_opts);
+}
+
static void append_mem_opts(TestServer *server, GString *cmd_line,
int size, enum test_memfd memfd)
{
@@ -426,6 +443,8 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
break;
default:
+ g_test_message("unhandled vhost-user message: %d", msg.request);
+ g_test_fail();
break;
}
@@ -1014,3 +1033,44 @@ static void register_vhost_user_test(void)
test_multiqueue, &opts);
}
libqos_init(register_vhost_user_test);
+
+/*
+ * This stub can't handle all the message types but we should reply
+ * that we support VHOST_USER_PROTOCOL_F_CONFIG as gpio would use it
+ * talking to a read vhost-user daemon.
+ */
+static void vu_gpio_get_protocol_features(TestServer *s, CharBackend *chr,
+ VhostUserMsg *msg)
+{
+ /* send back features to qemu */
+ msg->flags |= VHOST_USER_REPLY_MASK;
+ msg->size = sizeof(m.payload.u64);
+ msg->payload.u64 = 1ULL << VHOST_USER_PROTOCOL_F_LOG_SHMFD;
+ msg->payload.u64 |= 1ULL << VHOST_USER_PROTOCOL_F_CONFIG;
+
+ qemu_chr_fe_write_all(chr, (uint8_t *)msg, VHOST_USER_HDR_SIZE + msg->size);
+}
+
+static struct vhost_user_ops g_vu_gpio_ops = {
+ .type = VHOST_USER_GPIO,
+
+ .append_opts = append_vhost_gpio_opts,
+
+ .set_features = vu_net_set_features,
+ .get_protocol_features = vu_gpio_get_protocol_features,
+};
+
+static void register_vhost_gpio_test(void)
+{
+ QOSGraphTestOptions opts = {
+ .before = vhost_user_test_setup,
+ .subprocess = true,
+ .arg = &g_vu_gpio_ops,
+ };
+
+ qemu_add_opts(&qemu_chardev_opts);
+
+ qos_add_test("read-guest-mem/memfile",
+ "vhost-user-gpio", test_read_guest_mem, &opts);
+}
+libqos_init(register_vhost_gpio_test);
@@ -42,6 +42,7 @@ libqos_srcs = files('../libqtest.c',
'virtio-scsi.c',
'virtio-serial.c',
'virtio-iommu.c',
+ 'virtio-gpio.c',
'generic-pcihost.c',
# qgraph machines:
We don't have a virtio-gpio implementation in QEMU and only support a vhost-user backend. The QEMU side of the code is minimal so it should be enough to instantiate the device and pass some vhost-user messages over the control socket. To do this we hook into the existing vhost-user-test code and just add the bits required for gpio. Based-on: 20220210145254.157790-1-eric.auger@redhat.com [+ my fix] Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Cc: Viresh Kumar <viresh.kumar@linaro.org> Cc: Paolo Bonzini <pbonzini@redhat.com> Cc: Eric Auger <eric.auger@redhat.com> Message-Id: <20220121151534.3654562-1-alex.bennee@linaro.org> --- v2 - made progress, now starts up cleanly - fix application of extra arguments (causing dupe ids) - added (partial) F_CONFIG support - however seems to hang qos-test which never sees the get_config msg - it should have failed the test in chr_read() --- tests/qtest/libqos/virtio-gpio.h | 34 +++++++++++ tests/qtest/libqos/virtio-gpio.c | 98 ++++++++++++++++++++++++++++++++ tests/qtest/vhost-user-test.c | 60 +++++++++++++++++++ tests/qtest/libqos/meson.build | 1 + 4 files changed, 193 insertions(+) create mode 100644 tests/qtest/libqos/virtio-gpio.h create mode 100644 tests/qtest/libqos/virtio-gpio.c