diff mbox series

[v2,15/15] tests/qtest: enable tests for virtio-gpio

Message ID 20220524154056.2896913-16-alex.bennee@linaro.org
State Superseded
Headers show
Series virtio-gpio and various virtio cleanups | expand

Commit Message

Alex Bennée May 24, 2022, 3:40 p.m. UTC
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.

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: <20220408155704.2777166-1-alex.bennee@linaro.org>

---
v2
  - add more of the missing boilerplate
  - don't request LOG_SHMD
  - use get_features op
  - report VIRTIO_F_VERSION_1
  - more comments
---
 tests/qtest/libqos/virtio-gpio.h |  35 +++++++
 tests/qtest/libqos/virtio-gpio.c | 171 +++++++++++++++++++++++++++++++
 tests/qtest/libqos/virtio.c      |   2 +-
 tests/qtest/vhost-user-test.c    |  66 ++++++++++++
 tests/qtest/libqos/meson.build   |   1 +
 5 files changed, 274 insertions(+), 1 deletion(-)
 create mode 100644 tests/qtest/libqos/virtio-gpio.h
 create mode 100644 tests/qtest/libqos/virtio-gpio.c

Comments

Stefan Hajnoczi May 25, 2022, 4:17 p.m. UTC | #1
On Tue, May 24, 2022 at 04:40:56PM +0100, Alex Bennée wrote:
> 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.
> 
> 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: <20220408155704.2777166-1-alex.bennee@linaro.org>
> 
> ---
> v2
>   - add more of the missing boilerplate
>   - don't request LOG_SHMD
>   - use get_features op
>   - report VIRTIO_F_VERSION_1
>   - more comments
> ---
>  tests/qtest/libqos/virtio-gpio.h |  35 +++++++
>  tests/qtest/libqos/virtio-gpio.c | 171 +++++++++++++++++++++++++++++++
>  tests/qtest/libqos/virtio.c      |   2 +-
>  tests/qtest/vhost-user-test.c    |  66 ++++++++++++
>  tests/qtest/libqos/meson.build   |   1 +
>  5 files changed, 274 insertions(+), 1 deletion(-)
>  create mode 100644 tests/qtest/libqos/virtio-gpio.h
>  create mode 100644 tests/qtest/libqos/virtio-gpio.c
> 
> diff --git a/tests/qtest/libqos/virtio-gpio.h b/tests/qtest/libqos/virtio-gpio.h
> new file mode 100644
> index 0000000000..f11d41bd19
> --- /dev/null
> +++ b/tests/qtest/libqos/virtio-gpio.h
> @@ -0,0 +1,35 @@
> +/*
> + * 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;
> +    QVirtQueue **queues;
> +};
> +
> +struct QVhostUserGPIOPCI {
> +    QVirtioPCIDevice pci_vdev;
> +    QVhostUserGPIO gpio;
> +};
> +
> +struct QVhostUserGPIODevice {
> +    QOSGraphObject obj;
> +    QVhostUserGPIO gpio;
> +};
> +
> +#endif
> diff --git a/tests/qtest/libqos/virtio-gpio.c b/tests/qtest/libqos/virtio-gpio.c
> new file mode 100644
> index 0000000000..762aa6695b
> --- /dev/null
> +++ b/tests/qtest/libqos/virtio-gpio.c
> @@ -0,0 +1,171 @@
> +/*
> + * virtio-gpio nodes for testing
> + *
> + * Copyright (c) 2022 Linaro Ltd
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "standard-headers/linux/virtio_config.h"
> +#include "../libqtest.h"
> +#include "qemu/module.h"
> +#include "qgraph.h"
> +#include "virtio-gpio.h"
> +
> +static QGuestAllocator *alloc;
> +
> +static void virtio_gpio_cleanup(QVhostUserGPIO *gpio)
> +{
> +    QVirtioDevice *vdev = gpio->vdev;
> +    int i;
> +
> +    for (i = 0; i < 2; i++) {
> +        qvirtqueue_cleanup(vdev->bus, gpio->queues[i], alloc);
> +    }
> +    g_free(gpio->queues);
> +}
> +
> +/*
> + * This handles the VirtIO setup from the point of view of the driver
> + * frontend and therefor doesn't present any vhost specific features
> + * and in fact masks of the re-used bit.
> + */
> +static void virtio_gpio_setup(QVhostUserGPIO *gpio)
> +{
> +    QVirtioDevice *vdev = gpio->vdev;
> +    uint64_t features;
> +    int i;
> +
> +    features = qvirtio_get_features(vdev);
> +    features &= ~QVIRTIO_F_BAD_FEATURE;

This looks questionable. qvirtio_get_features() should return VIRTIO
feature bits. Is QVIRTIO_F_BAD_FEATURE masked out here because
qvirtio_get_features() is returning raw vhost-user feature bits instead
and you want to get rid of VHOST_USER_F_PROTOCOL_FEATURES?
Alex Bennée May 25, 2022, 10:35 p.m. UTC | #2
Stefan Hajnoczi <stefanha@redhat.com> writes:

> [[PGP Signed Part:Undecided]]
> On Tue, May 24, 2022 at 04:40:56PM +0100, Alex Bennée wrote:
>> 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.
>> 
>> 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: <20220408155704.2777166-1-alex.bennee@linaro.org>
>> 
>> ---
>> v2
>>   - add more of the missing boilerplate
>>   - don't request LOG_SHMD
>>   - use get_features op
>>   - report VIRTIO_F_VERSION_1
>>   - more comments
>> ---
>>  tests/qtest/libqos/virtio-gpio.h |  35 +++++++
>>  tests/qtest/libqos/virtio-gpio.c | 171 +++++++++++++++++++++++++++++++
>>  tests/qtest/libqos/virtio.c      |   2 +-
>>  tests/qtest/vhost-user-test.c    |  66 ++++++++++++
>>  tests/qtest/libqos/meson.build   |   1 +
>>  5 files changed, 274 insertions(+), 1 deletion(-)
>>  create mode 100644 tests/qtest/libqos/virtio-gpio.h
>>  create mode 100644 tests/qtest/libqos/virtio-gpio.c
>> 
>> diff --git a/tests/qtest/libqos/virtio-gpio.h b/tests/qtest/libqos/virtio-gpio.h
>> new file mode 100644
>> index 0000000000..f11d41bd19
>> --- /dev/null
>> +++ b/tests/qtest/libqos/virtio-gpio.h
>> @@ -0,0 +1,35 @@
>> +/*
>> + * 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;
>> +    QVirtQueue **queues;
>> +};
>> +
>> +struct QVhostUserGPIOPCI {
>> +    QVirtioPCIDevice pci_vdev;
>> +    QVhostUserGPIO gpio;
>> +};
>> +
>> +struct QVhostUserGPIODevice {
>> +    QOSGraphObject obj;
>> +    QVhostUserGPIO gpio;
>> +};
>> +
>> +#endif
>> diff --git a/tests/qtest/libqos/virtio-gpio.c b/tests/qtest/libqos/virtio-gpio.c
>> new file mode 100644
>> index 0000000000..762aa6695b
>> --- /dev/null
>> +++ b/tests/qtest/libqos/virtio-gpio.c
>> @@ -0,0 +1,171 @@
>> +/*
>> + * virtio-gpio nodes for testing
>> + *
>> + * Copyright (c) 2022 Linaro Ltd
>> + *
>> + * SPDX-License-Identifier: GPL-2.0-or-later
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "standard-headers/linux/virtio_config.h"
>> +#include "../libqtest.h"
>> +#include "qemu/module.h"
>> +#include "qgraph.h"
>> +#include "virtio-gpio.h"
>> +
>> +static QGuestAllocator *alloc;
>> +
>> +static void virtio_gpio_cleanup(QVhostUserGPIO *gpio)
>> +{
>> +    QVirtioDevice *vdev = gpio->vdev;
>> +    int i;
>> +
>> +    for (i = 0; i < 2; i++) {
>> +        qvirtqueue_cleanup(vdev->bus, gpio->queues[i], alloc);
>> +    }
>> +    g_free(gpio->queues);
>> +}
>> +
>> +/*
>> + * This handles the VirtIO setup from the point of view of the driver
>> + * frontend and therefor doesn't present any vhost specific features
>> + * and in fact masks of the re-used bit.
>> + */
>> +static void virtio_gpio_setup(QVhostUserGPIO *gpio)
>> +{
>> +    QVirtioDevice *vdev = gpio->vdev;
>> +    uint64_t features;
>> +    int i;
>> +
>> +    features = qvirtio_get_features(vdev);
>> +    features &= ~QVIRTIO_F_BAD_FEATURE;
>
> This looks questionable. qvirtio_get_features() should return VIRTIO
> feature bits. Is QVIRTIO_F_BAD_FEATURE masked out here because
> qvirtio_get_features() is returning raw vhost-user feature bits instead
> and you want to get rid of VHOST_USER_F_PROTOCOL_FEATURES?

Well it's an invalid bit for the driver/frontend<->hw/backend path -
although maybe we should error if we saw it?

>
> [[End of PGP Signed Part]]
Stefan Hajnoczi May 26, 2022, 8:06 a.m. UTC | #3
On Wed, May 25, 2022 at 11:35:53PM +0100, Alex Bennée wrote:
> 
> Stefan Hajnoczi <stefanha@redhat.com> writes:
> 
> > [[PGP Signed Part:Undecided]]
> > On Tue, May 24, 2022 at 04:40:56PM +0100, Alex Bennée wrote:
> >> 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.
> >> 
> >> 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: <20220408155704.2777166-1-alex.bennee@linaro.org>
> >> 
> >> ---
> >> v2
> >>   - add more of the missing boilerplate
> >>   - don't request LOG_SHMD
> >>   - use get_features op
> >>   - report VIRTIO_F_VERSION_1
> >>   - more comments
> >> ---
> >>  tests/qtest/libqos/virtio-gpio.h |  35 +++++++
> >>  tests/qtest/libqos/virtio-gpio.c | 171 +++++++++++++++++++++++++++++++
> >>  tests/qtest/libqos/virtio.c      |   2 +-
> >>  tests/qtest/vhost-user-test.c    |  66 ++++++++++++
> >>  tests/qtest/libqos/meson.build   |   1 +
> >>  5 files changed, 274 insertions(+), 1 deletion(-)
> >>  create mode 100644 tests/qtest/libqos/virtio-gpio.h
> >>  create mode 100644 tests/qtest/libqos/virtio-gpio.c
> >> 
> >> diff --git a/tests/qtest/libqos/virtio-gpio.h b/tests/qtest/libqos/virtio-gpio.h
> >> new file mode 100644
> >> index 0000000000..f11d41bd19
> >> --- /dev/null
> >> +++ b/tests/qtest/libqos/virtio-gpio.h
> >> @@ -0,0 +1,35 @@
> >> +/*
> >> + * 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;
> >> +    QVirtQueue **queues;
> >> +};
> >> +
> >> +struct QVhostUserGPIOPCI {
> >> +    QVirtioPCIDevice pci_vdev;
> >> +    QVhostUserGPIO gpio;
> >> +};
> >> +
> >> +struct QVhostUserGPIODevice {
> >> +    QOSGraphObject obj;
> >> +    QVhostUserGPIO gpio;
> >> +};
> >> +
> >> +#endif
> >> diff --git a/tests/qtest/libqos/virtio-gpio.c b/tests/qtest/libqos/virtio-gpio.c
> >> new file mode 100644
> >> index 0000000000..762aa6695b
> >> --- /dev/null
> >> +++ b/tests/qtest/libqos/virtio-gpio.c
> >> @@ -0,0 +1,171 @@
> >> +/*
> >> + * virtio-gpio nodes for testing
> >> + *
> >> + * Copyright (c) 2022 Linaro Ltd
> >> + *
> >> + * SPDX-License-Identifier: GPL-2.0-or-later
> >> + */
> >> +
> >> +#include "qemu/osdep.h"
> >> +#include "standard-headers/linux/virtio_config.h"
> >> +#include "../libqtest.h"
> >> +#include "qemu/module.h"
> >> +#include "qgraph.h"
> >> +#include "virtio-gpio.h"
> >> +
> >> +static QGuestAllocator *alloc;
> >> +
> >> +static void virtio_gpio_cleanup(QVhostUserGPIO *gpio)
> >> +{
> >> +    QVirtioDevice *vdev = gpio->vdev;
> >> +    int i;
> >> +
> >> +    for (i = 0; i < 2; i++) {
> >> +        qvirtqueue_cleanup(vdev->bus, gpio->queues[i], alloc);
> >> +    }
> >> +    g_free(gpio->queues);
> >> +}
> >> +
> >> +/*
> >> + * This handles the VirtIO setup from the point of view of the driver
> >> + * frontend and therefor doesn't present any vhost specific features
> >> + * and in fact masks of the re-used bit.
> >> + */
> >> +static void virtio_gpio_setup(QVhostUserGPIO *gpio)
> >> +{
> >> +    QVirtioDevice *vdev = gpio->vdev;
> >> +    uint64_t features;
> >> +    int i;
> >> +
> >> +    features = qvirtio_get_features(vdev);
> >> +    features &= ~QVIRTIO_F_BAD_FEATURE;
> >
> > This looks questionable. qvirtio_get_features() should return VIRTIO
> > feature bits. Is QVIRTIO_F_BAD_FEATURE masked out here because
> > qvirtio_get_features() is returning raw vhost-user feature bits instead
> > and you want to get rid of VHOST_USER_F_PROTOCOL_FEATURES?
> 
> Well it's an invalid bit for the driver/frontend<->hw/backend path -
> although maybe we should error if we saw it?

Thinking about it again, there's an argument for keeping "features &=
~QVIRTIO_F_BAD_FEATURE;". It means the code can be reused with
virtio-pci virtio-gpio devices.

I was just afraid that may the vhost-user implementation is exposing
VHOST_USER_F_PROTOCOL_FEATURES and we're working around that by masking
QVIRTIO_F_BAD_FEATURE here. An assertion/error would protect against
that.

Either way is fine by me.

Stefan
diff mbox series

Patch

diff --git a/tests/qtest/libqos/virtio-gpio.h b/tests/qtest/libqos/virtio-gpio.h
new file mode 100644
index 0000000000..f11d41bd19
--- /dev/null
+++ b/tests/qtest/libqos/virtio-gpio.h
@@ -0,0 +1,35 @@ 
+/*
+ * 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;
+    QVirtQueue **queues;
+};
+
+struct QVhostUserGPIOPCI {
+    QVirtioPCIDevice pci_vdev;
+    QVhostUserGPIO gpio;
+};
+
+struct QVhostUserGPIODevice {
+    QOSGraphObject obj;
+    QVhostUserGPIO gpio;
+};
+
+#endif
diff --git a/tests/qtest/libqos/virtio-gpio.c b/tests/qtest/libqos/virtio-gpio.c
new file mode 100644
index 0000000000..762aa6695b
--- /dev/null
+++ b/tests/qtest/libqos/virtio-gpio.c
@@ -0,0 +1,171 @@ 
+/*
+ * virtio-gpio nodes for testing
+ *
+ * Copyright (c) 2022 Linaro Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "standard-headers/linux/virtio_config.h"
+#include "../libqtest.h"
+#include "qemu/module.h"
+#include "qgraph.h"
+#include "virtio-gpio.h"
+
+static QGuestAllocator *alloc;
+
+static void virtio_gpio_cleanup(QVhostUserGPIO *gpio)
+{
+    QVirtioDevice *vdev = gpio->vdev;
+    int i;
+
+    for (i = 0; i < 2; i++) {
+        qvirtqueue_cleanup(vdev->bus, gpio->queues[i], alloc);
+    }
+    g_free(gpio->queues);
+}
+
+/*
+ * This handles the VirtIO setup from the point of view of the driver
+ * frontend and therefor doesn't present any vhost specific features
+ * and in fact masks of the re-used bit.
+ */
+static void virtio_gpio_setup(QVhostUserGPIO *gpio)
+{
+    QVirtioDevice *vdev = gpio->vdev;
+    uint64_t features;
+    int i;
+
+    features = qvirtio_get_features(vdev);
+    features &= ~QVIRTIO_F_BAD_FEATURE;
+    qvirtio_set_features(vdev, features);
+
+    gpio->queues = g_new(QVirtQueue *, 2);
+    for (i = 0; i < 2; i++) {
+        gpio->queues[i] = qvirtqueue_setup(vdev, alloc, i);
+    }
+    qvirtio_set_driver_ok(vdev);
+}
+
+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;
+    }
+
+    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);
+}
+
+/* virtio-gpio (mmio) */
+static void qvirtio_gpio_device_destructor(QOSGraphObject *obj)
+{
+    QVhostUserGPIODevice *gpio_dev = (QVhostUserGPIODevice *) obj;
+    virtio_gpio_cleanup(&gpio_dev->gpio);
+}
+
+static void qvirtio_gpio_device_start_hw(QOSGraphObject *obj)
+{
+    QVhostUserGPIODevice *gpio_dev = (QVhostUserGPIODevice *) obj;
+    virtio_gpio_setup(&gpio_dev->gpio);
+}
+
+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;
+    alloc = t_alloc;
+
+    virtio_device->obj.get_driver = qvirtio_gpio_device_get_driver;
+    virtio_device->obj.start_hw = qvirtio_gpio_device_start_hw;
+    virtio_device->obj.destructor = qvirtio_gpio_device_destructor;
+
+    return &virtio_device->obj;
+}
+
+/* virtio-gpio-pci */
+static void qvirtio_gpio_pci_destructor(QOSGraphObject *obj)
+{
+    QVhostUserGPIOPCI *gpio_pci = (QVhostUserGPIOPCI *) obj;
+    QOSGraphObject *pci_vobj =  &gpio_pci->pci_vdev.obj;
+
+    virtio_gpio_cleanup(&gpio_pci->gpio);
+    qvirtio_pci_destructor(pci_vobj);
+}
+
+static void qvirtio_gpio_pci_start_hw(QOSGraphObject *obj)
+{
+    QVhostUserGPIOPCI *gpio_pci = (QVhostUserGPIOPCI *) obj;
+    QOSGraphObject *pci_vobj =  &gpio_pci->pci_vdev.obj;
+
+    qvirtio_pci_start_hw(pci_vobj);
+    virtio_gpio_setup(&gpio_pci->gpio);
+}
+
+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;
+    alloc = t_alloc;
+
+    obj->get_driver = qvirtio_gpio_pci_get_driver;
+    obj->start_hw = qvirtio_gpio_pci_start_hw;
+    obj->destructor = qvirtio_gpio_pci_destructor;
+
+    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);
diff --git a/tests/qtest/libqos/virtio.c b/tests/qtest/libqos/virtio.c
index 09ec09b655..d6fc4f5a29 100644
--- a/tests/qtest/libqos/virtio.c
+++ b/tests/qtest/libqos/virtio.c
@@ -108,7 +108,7 @@  void qvirtio_set_features(QVirtioDevice *d, uint64_t features)
      * This could be a separate function for drivers that want to access
      * configuration space before setting FEATURES_OK, but no existing users
      * need that and it's less code for callers if this is done implicitly.
-    */
+     */
     if (features & (1ull << VIRTIO_F_VERSION_1)) {
         uint8_t status = d->bus->get_status(d) |
                          VIRTIO_CONFIG_S_FEATURES_OK;
diff --git a/tests/qtest/vhost-user-test.c b/tests/qtest/vhost-user-test.c
index 28b4cf28ec..04950e8458 100644
--- a/tests/qtest/vhost-user-test.c
+++ b/tests/qtest/vhost-user-test.c
@@ -32,6 +32,7 @@ 
 #include "standard-headers/linux/vhost_types.h"
 #include "standard-headers/linux/virtio_ids.h"
 #include "standard-headers/linux/virtio_net.h"
+#include "standard-headers/linux/virtio_gpio.h"
 
 #ifdef CONFIG_LINUX
 #include <sys/vfs.h>
@@ -53,9 +54,12 @@ 
 #define VHOST_MAX_VIRTQUEUES    0x100
 
 #define VHOST_USER_F_PROTOCOL_FEATURES 30
+#define VIRTIO_F_VERSION_1 32
+
 #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
 
@@ -140,6 +144,7 @@  enum {
 
 enum {
     VHOST_USER_NET,
+    VHOST_USER_GPIO,
 };
 
 typedef struct TestServer {
@@ -198,6 +203,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)
 {
@@ -1085,3 +1103,51 @@  static void register_vhost_user_test(void)
                  test_multiqueue, &opts);
 }
 libqos_init(register_vhost_user_test);
+
+static uint64_t vu_gpio_get_features(TestServer *s)
+{
+    return 0x1ULL << VIRTIO_F_VERSION_1 |
+        0x1ULL << VIRTIO_GPIO_F_IRQ |
+        0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES;
+}
+
+/*
+ * 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_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,
+
+    .get_features = vu_gpio_get_features,
+    .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);
diff --git a/tests/qtest/libqos/meson.build b/tests/qtest/libqos/meson.build
index fd5d6e5ae1..9dc815ddd4 100644
--- a/tests/qtest/libqos/meson.build
+++ b/tests/qtest/libqos/meson.build
@@ -45,6 +45,7 @@  libqos_srcs = files(
         'virtio-scsi.c',
         'virtio-serial.c',
         'virtio-iommu.c',
+        'virtio-gpio.c',
         'generic-pcihost.c',
 
         # qgraph machines: