qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v1 0/5] virtio: Add the virtio-msg transport
@ 2025-10-28 15:23 Edgar E. Iglesias
  2025-10-28 15:23 ` [PATCH v1 1/5] virtio: Introduce notify_queue Edgar E. Iglesias
                   ` (5 more replies)
  0 siblings, 6 replies; 10+ messages in thread
From: Edgar E. Iglesias @ 2025-10-28 15:23 UTC (permalink / raw)
  To: qemu-devel; +Cc: mst, alex.bennee, bill.mills, edgar.iglesias

From: "Edgar E. Iglesias" <edgar.iglesias@amd.com>

This adds virtio-msg, a new virtio transport. Virtio-msg works by
exchanging messages over a bus and doesn't rely on trapping and emulation
making it a good fit for a number of applications such as AMP, real-time
and safety applications.

Together with the new transport, this series adds a PCI device that
implements an AMP setup much like it could look if two SoC's would
use virtio-msg across a PCI link.

Current gaps and limitations:
Shared memory FIFO layout likely to change in the future.
Temporarily uses PCI Vendor Xilinx / Device 0x9039.
Documentation.

The virtio-msg draft spec:
https://github.com/Linaro/virtio-msg-spec/

QEMU with these patches:
https://github.com/edgarigl/qemu/tree/edgar/virtio-msg-rfc

Linux with virtio-msg support:
https://github.com/edgarigl/linux/tree/edgari/virtio-msg-6.17

To try it, first build Linux with the following enabled:
CONFIG_VIRTIO_MSG=y
CONFIG_VIRTIO_MSG_AMP=y
CONFIG_VIRTIO_MSG_AMP_PCI=y

Boot linux on QEMU with a virtio-msg-amp-pci device, in this example
with a virtio-net device attached to it:

 -device virtio-msg-amp-pci
 -device virtio-net-device,netdev=n0
 -netdev user,id=n0

If you're running on a QEMU machine that has virtio-mmio enabled, e.g 
ARM virt, you'll need to specify the virtio-bus for the device, otherwise
QEMU will connect it to virtio-mmio. For ARM virt machines:

 -device virtio-msg-amp-pci
 -device virtio-net-device,netdev=n0,bus=/gpex-pcihost/pcie.0/virtio-msg-amp-pci/fifo0/virtio-msg/bus0/virtio-msg-dev
 -netdev user,id=n0

Cheers,
Edgar

Edgar E. Iglesias (5):
  virtio: Introduce notify_queue
  virtio: Add virtio_queue_get_rings
  virtio: Add the virtio-msg transport
  hw/misc: Add generic virtio-msg AMP PCI device
  docs: Describe virtio-msg-amp-pci

 .../devices/virtio/virtio-msg-amp-pci.rst     |  70 ++
 hw/misc/Kconfig                               |   7 +
 hw/misc/meson.build                           |   1 +
 hw/misc/virtio-msg-amp-pci.c                  | 328 +++++++
 hw/virtio/Kconfig                             |   4 +
 hw/virtio/meson.build                         |   5 +
 hw/virtio/virtio-msg-bus.c                    |  89 ++
 hw/virtio/virtio-msg.c                        | 695 ++++++++++++++
 hw/virtio/virtio.c                            |  23 +
 include/hw/virtio/spsc_queue.h                | 213 +++++
 include/hw/virtio/virtio-bus.h                |   1 +
 include/hw/virtio/virtio-msg-bus.h            |  95 ++
 include/hw/virtio/virtio-msg-prot.h           | 846 ++++++++++++++++++
 include/hw/virtio/virtio-msg.h                |  56 ++
 include/hw/virtio/virtio.h                    |   2 +
 15 files changed, 2435 insertions(+)
 create mode 100644 docs/system/devices/virtio/virtio-msg-amp-pci.rst
 create mode 100644 hw/misc/virtio-msg-amp-pci.c
 create mode 100644 hw/virtio/virtio-msg-bus.c
 create mode 100644 hw/virtio/virtio-msg.c
 create mode 100644 include/hw/virtio/spsc_queue.h
 create mode 100644 include/hw/virtio/virtio-msg-bus.h
 create mode 100644 include/hw/virtio/virtio-msg-prot.h
 create mode 100644 include/hw/virtio/virtio-msg.h

-- 
2.43.0



^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v1 1/5] virtio: Introduce notify_queue
  2025-10-28 15:23 [PATCH v1 0/5] virtio: Add the virtio-msg transport Edgar E. Iglesias
@ 2025-10-28 15:23 ` Edgar E. Iglesias
  2025-11-05 12:38   ` Alex Bennée
  2025-10-28 15:23 ` [PATCH v1 2/5] virtio: Add virtio_queue_get_rings Edgar E. Iglesias
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 10+ messages in thread
From: Edgar E. Iglesias @ 2025-10-28 15:23 UTC (permalink / raw)
  To: qemu-devel, Michael S. Tsirkin; +Cc: alex.bennee, bill.mills, edgar.iglesias

From: "Edgar E. Iglesias" <edgar.iglesias@amd.com>

Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com>
---
 hw/virtio/virtio.c             | 7 +++++++
 include/hw/virtio/virtio-bus.h | 1 +
 2 files changed, 8 insertions(+)

diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index 153ee0a0cf..8a53fb5f93 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -2700,12 +2700,19 @@ static void virtio_irq(VirtQueue *vq)
 
 void virtio_notify(VirtIODevice *vdev, VirtQueue *vq)
 {
+    BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
+    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
+
     WITH_RCU_READ_LOCK_GUARD() {
         if (!virtio_should_notify(vdev, vq)) {
             return;
         }
     }
 
+    if (k->notify_queue) {
+        k->notify_queue(qbus->parent, virtio_get_queue_index(vq));
+    }
+
     trace_virtio_notify(vdev, vq);
     virtio_irq(vq);
 }
diff --git a/include/hw/virtio/virtio-bus.h b/include/hw/virtio/virtio-bus.h
index 7ab8c9dab0..043dbeb4cf 100644
--- a/include/hw/virtio/virtio-bus.h
+++ b/include/hw/virtio/virtio-bus.h
@@ -39,6 +39,7 @@ DECLARE_OBJ_CHECKERS(VirtioBusState, VirtioBusClass,
 struct VirtioBusClass {
     /* This is what a VirtioBus must implement */
     BusClass parent;
+    void (*notify_queue)(DeviceState *d, uint16_t index);
     void (*notify)(DeviceState *d, uint16_t vector);
     void (*save_config)(DeviceState *d, QEMUFile *f);
     void (*save_queue)(DeviceState *d, int n, QEMUFile *f);
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v1 2/5] virtio: Add virtio_queue_get_rings
  2025-10-28 15:23 [PATCH v1 0/5] virtio: Add the virtio-msg transport Edgar E. Iglesias
  2025-10-28 15:23 ` [PATCH v1 1/5] virtio: Introduce notify_queue Edgar E. Iglesias
@ 2025-10-28 15:23 ` Edgar E. Iglesias
  2025-11-05 12:39   ` Alex Bennée
  2025-10-28 15:23 ` [PATCH v1 3/5] virtio: Add the virtio-msg transport Edgar E. Iglesias
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 10+ messages in thread
From: Edgar E. Iglesias @ 2025-10-28 15:23 UTC (permalink / raw)
  To: qemu-devel, Michael S. Tsirkin; +Cc: alex.bennee, bill.mills, edgar.iglesias

From: "Edgar E. Iglesias" <edgar.iglesias@amd.com>

Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com>
---
 hw/virtio/virtio.c         | 16 ++++++++++++++++
 include/hw/virtio/virtio.h |  2 ++
 2 files changed, 18 insertions(+)

diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index 8a53fb5f93..fe7c635390 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -2379,6 +2379,22 @@ void virtio_queue_set_rings(VirtIODevice *vdev, int n, hwaddr desc,
     virtio_init_region_cache(vdev, n);
 }
 
+void virtio_queue_get_rings(VirtIODevice *vdev, int n, hwaddr *desc,
+                            hwaddr *avail, hwaddr *used)
+{
+    assert(vdev->vq[n].vring.num);
+
+    if (desc) {
+        *desc = vdev->vq[n].vring.desc;
+    }
+    if (avail) {
+        *avail = vdev->vq[n].vring.avail;
+    }
+    if (used) {
+        *used = vdev->vq[n].vring.used;
+    }
+}
+
 void virtio_queue_set_num(VirtIODevice *vdev, int n, int num)
 {
     /* Don't allow guest to flip queue between existent and
diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
index d97529c3f1..8bceb115a3 100644
--- a/include/hw/virtio/virtio.h
+++ b/include/hw/virtio/virtio.h
@@ -361,6 +361,8 @@ int virtio_queue_get_max_num(VirtIODevice *vdev, int n);
 int virtio_get_num_queues(VirtIODevice *vdev);
 void virtio_queue_set_rings(VirtIODevice *vdev, int n, hwaddr desc,
                             hwaddr avail, hwaddr used);
+void virtio_queue_get_rings(VirtIODevice *vdev, int n, hwaddr *desc,
+                            hwaddr *avail, hwaddr *used);
 void virtio_queue_update_rings(VirtIODevice *vdev, int n);
 void virtio_init_region_cache(VirtIODevice *vdev, int n);
 void virtio_queue_set_align(VirtIODevice *vdev, int n, int align);
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v1 3/5] virtio: Add the virtio-msg transport
  2025-10-28 15:23 [PATCH v1 0/5] virtio: Add the virtio-msg transport Edgar E. Iglesias
  2025-10-28 15:23 ` [PATCH v1 1/5] virtio: Introduce notify_queue Edgar E. Iglesias
  2025-10-28 15:23 ` [PATCH v1 2/5] virtio: Add virtio_queue_get_rings Edgar E. Iglesias
@ 2025-10-28 15:23 ` Edgar E. Iglesias
  2025-10-28 15:23 ` [PATCH v1 4/5] hw/misc: Add generic virtio-msg AMP PCI device Edgar E. Iglesias
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Edgar E. Iglesias @ 2025-10-28 15:23 UTC (permalink / raw)
  To: qemu-devel, Paolo Bonzini, Michael S. Tsirkin
  Cc: alex.bennee, bill.mills, edgar.iglesias

From: "Edgar E. Iglesias" <edgar.iglesias@amd.com>

Add the new virtio-msg transport together with a framework
for virtio-msg buses.

Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com>
---
 hw/virtio/Kconfig                   |   4 +
 hw/virtio/meson.build               |   5 +
 hw/virtio/virtio-msg-bus.c          |  89 +++
 hw/virtio/virtio-msg.c              | 695 +++++++++++++++++++++++
 include/hw/virtio/virtio-msg-bus.h  |  95 ++++
 include/hw/virtio/virtio-msg-prot.h | 846 ++++++++++++++++++++++++++++
 include/hw/virtio/virtio-msg.h      |  56 ++
 7 files changed, 1790 insertions(+)
 create mode 100644 hw/virtio/virtio-msg-bus.c
 create mode 100644 hw/virtio/virtio-msg.c
 create mode 100644 include/hw/virtio/virtio-msg-bus.h
 create mode 100644 include/hw/virtio/virtio-msg-prot.h
 create mode 100644 include/hw/virtio/virtio-msg.h

diff --git a/hw/virtio/Kconfig b/hw/virtio/Kconfig
index 10f5c53ac0..d2d8c475b2 100644
--- a/hw/virtio/Kconfig
+++ b/hw/virtio/Kconfig
@@ -26,6 +26,10 @@ config VIRTIO_MMIO
     bool
     select VIRTIO
 
+config VIRTIO_MSG
+    bool
+    select VIRTIO
+
 config VIRTIO_CCW
     bool
     select VIRTIO
diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build
index affd66887d..7944bbb652 100644
--- a/hw/virtio/meson.build
+++ b/hw/virtio/meson.build
@@ -13,6 +13,11 @@ specific_virtio_ss = ss.source_set()
 specific_virtio_ss.add(files('virtio.c'))
 specific_virtio_ss.add(files('virtio-qmp.c'))
 
+specific_virtio_ss.add(when: 'CONFIG_VIRTIO_MSG', if_true: files(
+    'virtio-msg.c',
+    'virtio-msg-bus.c',
+))
+
 if have_vhost
   system_virtio_ss.add(files('vhost.c'))
   system_virtio_ss.add(files('vhost-backend.c', 'vhost-iova-tree.c'))
diff --git a/hw/virtio/virtio-msg-bus.c b/hw/virtio/virtio-msg-bus.c
new file mode 100644
index 0000000000..e907fd64ec
--- /dev/null
+++ b/hw/virtio/virtio-msg-bus.c
@@ -0,0 +1,89 @@
+/*
+ * VirtIO MSG bus.
+ *
+ * Copyright (c) 2024 Advanced Micro Devices, Inc.
+ * Written by Edgar E. Iglesias <edgar.iglesias@amd.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "hw/virtio/virtio-msg-bus.h"
+
+bool virtio_msg_bus_connect(BusState *bus,
+                            const VirtIOMSGBusPort *port,
+                            void *opaque)
+{
+    VirtIOMSGBusDevice *bd = virtio_msg_bus_get_device(bus);
+
+    if (!bd) {
+        /* Nothing connected to this virtio-msg device. Ignore. */
+        return false;
+    }
+
+    bd->peer = port;
+    bd->opaque = opaque;
+    return true;
+}
+
+void virtio_msg_bus_process(VirtIOMSGBusDevice *bd)
+{
+    VirtIOMSGBusDeviceClass *bdc;
+    bdc = VIRTIO_MSG_BUS_DEVICE_CLASS(object_get_class(OBJECT(bd)));
+
+    bdc->process(bd);
+}
+
+int virtio_msg_bus_send(BusState *bus, VirtIOMSG *msg_req)
+{
+    VirtIOMSGBusDevice *bd = virtio_msg_bus_get_device(bus);
+    VirtIOMSGBusDeviceClass *bdc;
+    int r = VIRTIO_MSG_NO_ERROR;
+
+    bdc = VIRTIO_MSG_BUS_DEVICE_CLASS(object_get_class(OBJECT(bd)));
+
+    if (bdc->send) {
+        r = bdc->send(bd, msg_req);
+    }
+    return r;
+}
+
+static void virtio_msg_bus_class_init(ObjectClass *klass, const void *data)
+{
+    BusClass *bc = BUS_CLASS(klass);
+
+    bc->max_dev = 1;
+}
+
+static const TypeInfo virtio_msg_bus_info = {
+    .name = TYPE_VIRTIO_MSG_BUS,
+    .parent = TYPE_BUS,
+    .instance_size = sizeof(BusState),
+    .class_init = virtio_msg_bus_class_init,
+};
+
+static void virtio_msg_bus_device_class_init(ObjectClass *klass,
+                                             const void *data)
+{
+    DeviceClass *k = DEVICE_CLASS(klass);
+
+    k->bus_type = TYPE_VIRTIO_MSG_BUS;
+}
+
+static const TypeInfo virtio_msg_bus_device_type_info = {
+    .name = TYPE_VIRTIO_MSG_BUS_DEVICE,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(VirtIOMSGBusDevice),
+    .abstract = true,
+    .class_size = sizeof(VirtIOMSGBusDeviceClass),
+    .class_init = virtio_msg_bus_device_class_init,
+};
+
+static void virtio_msg_bus_register_types(void)
+{
+    type_register_static(&virtio_msg_bus_info);
+    type_register_static(&virtio_msg_bus_device_type_info);
+}
+
+type_init(virtio_msg_bus_register_types)
diff --git a/hw/virtio/virtio-msg.c b/hw/virtio/virtio-msg.c
new file mode 100644
index 0000000000..a84da1714f
--- /dev/null
+++ b/hw/virtio/virtio-msg.c
@@ -0,0 +1,695 @@
+/*
+ * Virtio MSG bindings
+ *
+ * Copyright (c) 2024 Advanced Micro Devices, Inc.
+ * Written by Edgar E. Iglesias <edgar.iglesias@amd.com>.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "qapi/error.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/virtio-msg-bus.h"
+#include "hw/virtio/virtio-msg.h"
+#include "qemu/error-report.h"
+#include "qemu/log.h"
+#include "trace.h"
+
+#define VIRTIO_MSG_VENDOR_ID 0x554D4551 /* 'QEMU' */
+
+static bool virtio_msg_bad(VirtIOMSGProxy *s, VirtIOMSG *msg)
+{
+    bool drop = false;
+    size_t min_size;
+    unsigned int n;
+
+    min_size = virtio_msg_header_size();
+    switch (msg->msg_id) {
+    case VIRTIO_MSG_GET_DEVICE_STATUS:
+    case VIRTIO_MSG_DEVICE_INFO:
+        break;
+    case VIRTIO_MSG_GET_FEATURES:
+        min_size += sizeof msg->get_features;
+        break;
+    case VIRTIO_MSG_SET_FEATURES:
+        n = msg->set_features.num;
+
+        /* We expect at least one feature block.  */
+        if (n == 0 || n > VIRTIO_MSG_MAX_FEATURE_NUM) {
+            drop = true;
+            break;
+        }
+
+        min_size += sizeof msg->set_features + n * 4;
+        break;
+    case VIRTIO_MSG_GET_CONFIG:
+        min_size += sizeof msg->get_config;
+        break;
+    case VIRTIO_MSG_SET_CONFIG:
+        if (msg->set_config.size > VIRTIO_MSG_MAX_CONFIG_BYTES) {
+            drop = true;
+            break;
+        }
+
+        min_size += sizeof msg->set_config + msg->set_config.size;
+        break;
+    case VIRTIO_MSG_SET_DEVICE_STATUS:
+        min_size += sizeof msg->set_device_status;
+        break;
+    case VIRTIO_MSG_GET_VQUEUE:
+        min_size += sizeof msg->get_vqueue;
+        break;
+    case VIRTIO_MSG_SET_VQUEUE:
+        min_size += sizeof msg->set_vqueue;
+        break;
+    case VIRTIO_MSG_RESET_VQUEUE:
+        min_size += sizeof msg->reset_vqueue;
+        break;
+    case VIRTIO_MSG_EVENT_AVAIL:
+        min_size += sizeof msg->event_avail;
+        break;
+    default:
+        /* Unexpected message.  */
+        drop = true;
+        break;
+    }
+
+    /* Accept large messages allowing future backwards compatible extensions. */
+    if (drop ||
+        msg->msg_size < min_size || msg->msg_size > VIRTIO_MSG_MAX_SIZE) {
+        return true;
+    }
+
+    if (msg->dev_num >= ARRAY_SIZE(s->devs)) {
+        return true;
+    }
+
+    return false;
+}
+
+static VirtIODevice *virtio_msg_vdev(VirtIOMSGProxy *s, uint16_t dev_num)
+{
+    VirtIODevice *vdev;
+
+    vdev = virtio_bus_get_device(&s->devs[dev_num].bus);
+    return vdev;
+}
+
+static void virtio_msg_bus_get_devices(VirtIOMSGProxy *s,
+                                       VirtIOMSG *msg)
+{
+    VirtIODevice *vdev;
+    VirtIOMSG msg_resp;
+    uint8_t data[VIRTIO_MSG_MAX_DEVS / 8] = {0};
+    uint16_t num = MAX(msg->bus_get_devices.num, VIRTIO_MSG_MAX_DEVS);
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(s->devs); i++) {
+        vdev = virtio_msg_vdev(s, i);
+        if (vdev) {
+            data[i / 8] |= 1U << (i & 7);
+        }
+    }
+
+    virtio_msg_pack_bus_get_devices_resp(&msg_resp,
+                                         msg->bus_get_devices.offset, num, 0,
+                                         data);
+    virtio_msg_bus_send(&s->msg_bus, &msg_resp);
+}
+
+static void virtio_msg_device_info(VirtIOMSGProxy *s,
+                                   VirtIOMSG *msg)
+{
+    VirtIODevice *vdev = virtio_msg_vdev(s, msg->dev_num);
+    uint32_t config_len = 0;
+    uint32_t device_id = 0;
+    VirtIOMSG msg_resp;
+
+    if (vdev) {
+        device_id = vdev->device_id;
+        config_len = vdev->config_len;
+    } else {
+        error_report("%s: No virtio device on bus %s!",
+                     __func__, BUS(&s->devs[msg->dev_num].bus)->name);
+    }
+
+    virtio_msg_pack_get_device_info_resp(&msg_resp, msg->dev_num, msg->token,
+                                         device_id,
+                                         VIRTIO_MSG_VENDOR_ID,
+                                         /* Feature bits */
+                                         64,
+                                         config_len,
+                                         VIRTIO_QUEUE_MAX,
+                                         0, 0);
+    virtio_msg_bus_send(&s->msg_bus, &msg_resp);
+}
+
+static void virtio_msg_get_features(VirtIOMSGProxy *s,
+                                    VirtIOMSG *msg)
+{
+    VirtIODevice *vdev = virtio_msg_vdev(s, msg->dev_num);
+    VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
+    VirtIOMSG msg_resp;
+    uint32_t index = msg->get_features.index;
+    uint32_t f[VIRTIO_MSG_MAX_FEATURE_NUM] = { 0 };
+    uint32_t num = msg->get_features.num;
+    uint64_t features;
+
+    features = vdev->host_features & ~vdc->legacy_features;
+
+    /* We only have 64 feature bits. If driver asks for more, return zeros  */
+    if (index < 2) {
+        features >>= index * 32;
+        f[0] = features;
+        f[1] = features >> 32;
+    }
+
+    /* If index is out of bounds, we respond with num=0, f=0.  */
+    virtio_msg_pack_get_features_resp(&msg_resp, msg->dev_num, msg->token,
+                                      index, num, f);
+    virtio_msg_bus_send(&s->msg_bus, &msg_resp);
+}
+
+static void virtio_msg_set_features(VirtIOMSGProxy *s,
+                                    VirtIOMSG *msg)
+{
+    VirtIOMSG msg_resp;
+    unsigned int i;
+    uint64_t f;
+
+    f = s->devs[msg->dev_num].guest_features;
+
+    for (i = 0; i < msg->set_features.num; i++) {
+        unsigned int feature_index = i + msg->set_features.index;
+
+        /* We only support up to 64bits  */
+        if (feature_index >= 2) {
+            break;
+        }
+
+        f = deposit64(f, feature_index * 32, 32, msg->set_features.b32[i]);
+    }
+
+    s->devs[msg->dev_num].guest_features = f;
+
+    virtio_msg_pack_set_features_resp(&msg_resp, msg->dev_num, msg->token);
+    virtio_msg_bus_send(&s->msg_bus, &msg_resp);
+}
+
+static void virtio_msg_soft_reset(VirtIOMSGProxy *s, uint16_t dev_num)
+{
+    assert(dev_num < ARRAY_SIZE(s->devs));
+
+    virtio_bus_reset(&s->devs[dev_num].bus);
+    s->devs[dev_num].guest_features = 0;
+}
+
+static void virtio_msg_set_device_status(VirtIOMSGProxy *s,
+                                         VirtIOMSG *msg)
+{
+    VirtIODevice *vdev = virtio_msg_vdev(s, msg->dev_num);
+    uint32_t status = msg->set_device_status.status;
+    VirtIOMSG msg_resp;
+
+    if (!vdev) {
+        return;
+    }
+
+    if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) {
+        virtio_bus_stop_ioeventfd(&s->devs[msg->dev_num].bus);
+    }
+
+    if (status & VIRTIO_CONFIG_S_FEATURES_OK) {
+        virtio_set_features(vdev, s->devs[msg->dev_num].guest_features);
+    }
+
+    virtio_set_status(vdev, status);
+    assert(vdev->status == status);
+
+    if (status & VIRTIO_CONFIG_S_DRIVER_OK) {
+        virtio_bus_start_ioeventfd(&s->devs[msg->dev_num].bus);
+    }
+
+    if (status == 0) {
+        virtio_msg_soft_reset(s, msg->dev_num);
+    }
+
+    virtio_msg_pack_set_device_status_resp(&msg_resp, msg->dev_num, msg->token,
+                                           vdev->status);
+    virtio_msg_bus_send(&s->msg_bus, &msg_resp);
+}
+
+static void virtio_msg_get_device_status(VirtIOMSGProxy *s,
+                                         VirtIOMSG *msg)
+{
+    VirtIODevice *vdev = virtio_msg_vdev(s, msg->dev_num);
+    VirtIOMSG msg_resp;
+
+    virtio_msg_pack_get_device_status_resp(&msg_resp, msg->dev_num, msg->token,
+                                           vdev->status);
+    virtio_msg_bus_send(&s->msg_bus, &msg_resp);
+}
+
+static void virtio_msg_get_config(VirtIOMSGProxy *s,
+                                  VirtIOMSG *msg)
+{
+    VirtIODevice *vdev = virtio_msg_vdev(s, msg->dev_num);
+    uint32_t size = msg->get_config.size;
+    uint32_t offset = msg->get_config.offset;
+    uint8_t data[VIRTIO_MSG_MAX_CONFIG_BYTES];
+    VirtIOMSG msg_resp;
+    unsigned int i;
+
+    if (size > VIRTIO_MSG_MAX_CONFIG_BYTES) {
+        return;
+    }
+
+    for (i = 0; i < size; i++) {
+        data[i] = virtio_config_modern_readb(vdev, offset + i);
+    }
+
+    virtio_msg_pack_get_config_resp(&msg_resp, msg->dev_num, msg->token,
+                                    size, offset,
+                                    vdev->generation, data);
+    virtio_msg_bus_send(&s->msg_bus, &msg_resp);
+}
+
+static void virtio_msg_set_config(VirtIOMSGProxy *s,
+                                  VirtIOMSG *msg)
+{
+    VirtIODevice *vdev = virtio_msg_vdev(s, msg->dev_num);
+    uint32_t offset = msg->set_config.offset;
+    uint32_t size = msg->set_config.size;
+    uint8_t *data = msg->set_config.data;
+    VirtIOMSG msg_resp;
+    unsigned int i;
+
+    for (i = 0; i < size; i++) {
+        virtio_config_modern_writeb(vdev, offset + i, data[i]);
+    }
+
+    virtio_msg_pack_set_config_resp(&msg_resp, msg->dev_num, msg->token,
+                                    size, offset,
+                                    vdev->generation, data);
+    virtio_msg_bus_send(&s->msg_bus, &msg_resp);
+}
+
+static void virtio_msg_get_vqueue(VirtIOMSGProxy *s,
+                                  VirtIOMSG *msg)
+{
+    VirtIODevice *vdev = virtio_msg_vdev(s, msg->dev_num);
+    uint32_t max_size = VIRTQUEUE_MAX_SIZE;
+    uint32_t index = msg->get_vqueue.index;
+    hwaddr desc, avail, used;
+    VirtIOMSG msg_resp;
+    uint32_t size;
+
+    if (index < VIRTIO_QUEUE_MAX) {
+        size = virtio_queue_get_num(vdev, index);
+        if (!size) {
+            max_size = 0;
+        }
+
+        virtio_queue_get_rings(vdev, index, &desc, &avail, &used);
+        virtio_msg_pack_get_vqueue_resp(&msg_resp, msg->dev_num, msg->token,
+                                        index, max_size, size,
+                                        desc, avail, used);
+    } else {
+        /* OOB index, respond with all zeroes. */
+        virtio_msg_pack_get_vqueue_resp(&msg_resp, msg->dev_num, msg->token,
+                                        index, 0, 0, 0, 0, 0);
+    }
+
+    virtio_msg_bus_send(&s->msg_bus, &msg_resp);
+}
+
+static void virtio_msg_set_vqueue(VirtIOMSGProxy *s, VirtIOMSG *msg)
+{
+    VirtIODevice *vdev = virtio_msg_vdev(s, msg->dev_num);
+    uint32_t index = msg->set_vqueue.index;
+    VirtIOMSG msg_resp;
+
+    if (index >= VIRTIO_QUEUE_MAX) {
+        /* OOB index, ignore. */
+        return;
+    }
+
+    virtio_queue_set_num(vdev, index, msg->set_vqueue.size);
+    virtio_queue_set_rings(vdev, index,
+                           msg->set_vqueue.descriptor_addr,
+                           msg->set_vqueue.driver_addr,
+                           msg->set_vqueue.device_addr);
+    virtio_queue_enable(vdev, index);
+
+    virtio_msg_pack_set_vqueue_resp(&msg_resp, msg->dev_num, msg->token);
+    virtio_msg_bus_send(&s->msg_bus, &msg_resp);
+}
+
+static void virtio_msg_reset_vqueue(VirtIOMSGProxy *s, VirtIOMSG *msg)
+{
+    VirtIODevice *vdev = virtio_msg_vdev(s, msg->dev_num);
+    VirtIOMSG msg_resp;
+
+    virtio_queue_reset(vdev, msg->reset_vqueue.index);
+
+    virtio_msg_pack_reset_vqueue_resp(&msg_resp, msg->dev_num, msg->token);
+    virtio_msg_bus_send(&s->msg_bus, &msg_resp);
+}
+
+static void virtio_msg_event_avail(VirtIOMSGProxy *s,
+                                   VirtIOMSG *msg)
+{
+    VirtIODevice *vdev = virtio_msg_vdev(s, msg->dev_num);
+    uint16_t vq_idx = msg->event_avail.index;
+
+    if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
+        VirtIOMSG msg_ev;
+
+        virtio_error(vdev, "Notification while driver not OK?");
+        virtio_msg_pack_event_config(&msg_ev, msg->dev_num,
+                                     vdev->status, vdev->generation,
+                                     0, 0, NULL);
+        virtio_msg_bus_send(&s->msg_bus, &msg_ev);
+        return;
+    }
+
+    if (vq_idx >= VIRTIO_QUEUE_MAX) {
+        virtio_error(vdev, "Notification to bad VQ!");
+        return;
+    }
+
+    if (!virtio_queue_get_num(vdev, vq_idx)) {
+        virtio_error(vdev, "Notification to unconfigured VQ!");
+        return;
+    }
+
+    /* NOTIFICATION_DATA doesn't exist in QEMU 8.2.7. if (0) it */
+    if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFICATION_DATA) && 0) {
+        VirtQueue *vq = virtio_get_queue(vdev, vq_idx);
+        uint32_t next_offset_wrap = msg->event_avail.next_offset_wrap;
+        uint16_t qsize = virtio_queue_get_num(vdev, vq_idx);
+        uint32_t offset = next_offset_wrap & 0x7fffffff;
+        bool wrap = next_offset_wrap & 0x80000000;
+        uint16_t data;
+
+        if (offset > 0x7fff || offset >= qsize) {
+            virtio_error(vdev, "Next offset to large!");
+            /* Bail out without notification???  */
+            return;
+        }
+
+        data = wrap << 15;
+        data |= offset & 0x7fff;
+
+        virtio_queue_set_shadow_avail_idx(vq, data);
+    }
+    virtio_queue_notify(vdev, msg->event_avail.index);
+}
+
+typedef void (*VirtIOMSGHandler)(VirtIOMSGProxy *s,
+                                 VirtIOMSG *msg);
+
+static const VirtIOMSGHandler msg_handlers[] = {
+    [VIRTIO_MSG_DEVICE_INFO] = virtio_msg_device_info,
+    [VIRTIO_MSG_GET_FEATURES] = virtio_msg_get_features,
+    [VIRTIO_MSG_SET_FEATURES] = virtio_msg_set_features,
+    [VIRTIO_MSG_GET_DEVICE_STATUS] = virtio_msg_get_device_status,
+    [VIRTIO_MSG_SET_DEVICE_STATUS] = virtio_msg_set_device_status,
+    [VIRTIO_MSG_GET_CONFIG] = virtio_msg_get_config,
+    [VIRTIO_MSG_SET_CONFIG] = virtio_msg_set_config,
+    [VIRTIO_MSG_GET_VQUEUE] = virtio_msg_get_vqueue,
+    [VIRTIO_MSG_SET_VQUEUE] = virtio_msg_set_vqueue,
+    [VIRTIO_MSG_RESET_VQUEUE] = virtio_msg_reset_vqueue,
+    [VIRTIO_MSG_EVENT_AVAIL] = virtio_msg_event_avail,
+};
+
+static int virtio_msg_receive_msg(VirtIOMSGBusDevice *bd, VirtIOMSG *msg)
+{
+    VirtIOMSGProxy *s = VIRTIO_MSG(bd->opaque);
+    VirtIOMSGHandler handler;
+
+    /* virtio_msg_print(msg); */
+
+    /* We handle some generic bus messages. */
+    if (msg->type & VIRTIO_MSG_TYPE_BUS) {
+        if (msg->msg_id == VIRTIO_MSG_BUS_GET_DEVICES) {
+            virtio_msg_bus_get_devices(s, msg);
+            return VIRTIO_MSG_NO_ERROR;
+        }
+    }
+
+    if (msg->msg_id > ARRAY_SIZE(msg_handlers)) {
+        return VIRTIO_MSG_ERROR_UNSUPPORTED_MESSAGE_ID;
+    }
+
+    handler = msg_handlers[msg->msg_id];
+    assert((msg->type & VIRTIO_MSG_TYPE_RESPONSE) == 0);
+
+    if (virtio_msg_bad(s, msg)) {
+        /* Drop bad messages.  */
+        return VIRTIO_MSG_ERROR_BAD_MESSAGE;
+    }
+
+    if (handler) {
+        handler(s, msg);
+    }
+
+    return VIRTIO_MSG_NO_ERROR;
+}
+
+static const VirtIOMSGBusPort virtio_msg_port = {
+    .receive = virtio_msg_receive_msg,
+    .is_driver = false
+};
+
+static void virtio_msg_notify_queue(DeviceState *opaque, uint16_t index)
+{
+    VirtIOMSGDev *mdev = VIRTIO_MSG_DEV(opaque);
+    VirtIOMSGProxy *s = VIRTIO_MSG(mdev->proxy);
+    VirtIODevice *vdev = virtio_msg_vdev(s, mdev->dev_num);
+    VirtIOMSG msg;
+
+    if (!vdev || !virtio_msg_bus_connected(&s->msg_bus)) {
+        return;
+    }
+
+    virtio_msg_pack_event_used(&msg, mdev->dev_num, index);
+    virtio_msg_bus_send(&s->msg_bus, &msg);
+}
+
+static void virtio_msg_notify(DeviceState *opaque, uint16_t vector)
+{
+    VirtIOMSGDev *mdev = VIRTIO_MSG_DEV(opaque);
+    VirtIOMSGProxy *s = VIRTIO_MSG(mdev->proxy);
+    VirtIODevice *vdev = virtio_msg_vdev(s, mdev->dev_num);
+    VirtIOMSG msg;
+
+    if (!virtio_msg_bus_connected(&s->msg_bus)) {
+        return;
+    }
+
+    /* Check if we're notifying for VQ or CONFIG updates.  */
+    if (vdev->isr & 2) {
+        virtio_msg_pack_event_config(&msg, mdev->dev_num,
+                                     vdev->status, vdev->generation,
+                                     0, 0, NULL);
+        virtio_msg_bus_send(&s->msg_bus, &msg);
+    }
+}
+
+static const VMStateDescription vmstate_virtio_msg_state_sub = {
+    .name = "virtio_msg_device",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_virtio_msg = {
+    .name = "virtio_msg_proxy_backend",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (const VMStateField[]) {
+        VMSTATE_END_OF_LIST()
+    },
+    .subsections = (const VMStateDescription * []) {
+        &vmstate_virtio_msg_state_sub,
+        NULL
+    }
+};
+
+static void virtio_msg_save_extra_state(DeviceState *opaque, QEMUFile *f)
+{
+    VirtIOMSGProxy *s = VIRTIO_MSG(opaque);
+
+    vmstate_save_state(f, &vmstate_virtio_msg, s, NULL, &error_fatal);
+}
+
+static int virtio_msg_load_extra_state(DeviceState *opaque, QEMUFile *f)
+{
+    VirtIOMSGProxy *s = VIRTIO_MSG(opaque);
+
+    return vmstate_load_state(f, &vmstate_virtio_msg, s, 1, &error_fatal);
+}
+
+static bool virtio_msg_has_extra_state(DeviceState *opaque)
+{
+    return true;
+}
+
+static void virtio_msg_reset_hold(Object *obj, ResetType type)
+{
+    VirtIOMSGProxy *s = VIRTIO_MSG(obj);
+    VirtIODevice *vdev;
+    bool found_a_vdev;
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(s->devs); i++) {
+        virtio_msg_soft_reset(s, i);
+
+        vdev = virtio_msg_vdev(s, i);
+        if (vdev) {
+            found_a_vdev = true;
+        }
+    }
+
+    /* Only connect transports with virtio-devs.  */
+    if (found_a_vdev) {
+        bool r;
+
+        r = virtio_msg_bus_connect(&s->msg_bus, &virtio_msg_port, s);
+        if (!r) {
+            /* This is a user error, forgetting to setup a msg-bus.  */
+            error_report("%s: No bus connected!",
+                         object_get_canonical_path(obj));
+            exit(EXIT_FAILURE);
+        }
+    }
+}
+
+static void virtio_msg_pre_plugged(DeviceState *d, Error **errp)
+{
+    VirtIOMSGDev *mdev = VIRTIO_MSG_DEV(d);
+    VirtIOMSGProxy *s = VIRTIO_MSG(mdev->proxy);
+    VirtIODevice *vdev = virtio_msg_vdev(s, mdev->dev_num);
+
+    virtio_add_feature(&vdev->host_features, VIRTIO_F_VERSION_1);
+}
+
+static AddressSpace *virtio_msg_get_dma_as(DeviceState *d)
+{
+    VirtIOMSGProxy *s = VIRTIO_MSG(d);
+    AddressSpace *as;
+
+    as = virtio_msg_bus_get_remote_as(&s->msg_bus);
+    return as;
+}
+
+static void virtio_msg_realize(DeviceState *d, Error **errp)
+{
+    VirtIOMSGProxy *s = VIRTIO_MSG(d);
+    Object *o = OBJECT(d);
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(s->devs); i++) {
+        g_autofree char *outer_bus_name = g_strdup_printf("bus%d", i);
+
+        qbus_init(&s->devs_bus[i], sizeof(s->devs_bus[i]),
+                  TYPE_VIRTIO_MSG_OUTER_BUS, d, outer_bus_name);
+
+        object_initialize_child(o, "dev[*]", &s->devs[i], TYPE_VIRTIO_MSG_DEV);
+        s->devs[i].proxy = s;
+        s->devs[i].dev_num = i;
+        qdev_realize(DEVICE(&s->devs[i]), BUS(&s->devs_bus[i]), &error_fatal);
+
+        qbus_init(&s->devs[i].bus, sizeof(s->devs[i].bus),
+                  TYPE_VIRTIO_MSG_PROXY_BUS, DEVICE(&s->devs[i]), "bus");
+    }
+
+    qbus_init(&s->msg_bus, sizeof(s->msg_bus),
+              TYPE_VIRTIO_MSG_BUS, d, "msg-bus");
+}
+
+static void virtio_msg_class_init(ObjectClass *klass, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+    dc->realize = virtio_msg_realize;
+    dc->bus_type = TYPE_VIRTIO_MSG_OUTER_BUS;
+    dc->user_creatable = true;
+    rc->phases.hold = virtio_msg_reset_hold;
+}
+
+static void virtio_msg_dev_class_init(ObjectClass *klass, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->bus_type = TYPE_VIRTIO_MSG_OUTER_BUS;
+}
+
+static const TypeInfo virtio_msg_types[] = {
+    {
+        .name          = TYPE_VIRTIO_MSG,
+        .parent        = TYPE_DEVICE,
+        .instance_size = sizeof(VirtIOMSGProxy),
+        .class_init    = virtio_msg_class_init,
+    },
+    {
+        .name          = TYPE_VIRTIO_MSG_DEV,
+        .parent        = TYPE_DEVICE,
+        .instance_size = sizeof(DeviceState),
+        .class_init    = virtio_msg_dev_class_init,
+    },
+};
+DEFINE_TYPES(virtio_msg_types);
+
+static char *virtio_msg_bus_get_dev_path(DeviceState *dev)
+{
+    BusState *bus = qdev_get_parent_bus(dev);
+    return strdup(object_get_canonical_path(OBJECT(bus->parent)));
+}
+
+static void virtio_msg_bus_class_init(ObjectClass *klass, const void *data)
+{
+    BusClass *bus_class = BUS_CLASS(klass);
+    VirtioBusClass *k = VIRTIO_BUS_CLASS(klass);
+
+    k->notify_queue = virtio_msg_notify_queue;
+    k->notify = virtio_msg_notify;
+    k->save_extra_state = virtio_msg_save_extra_state;
+    k->load_extra_state = virtio_msg_load_extra_state;
+    k->has_extra_state = virtio_msg_has_extra_state;
+    k->pre_plugged = virtio_msg_pre_plugged;
+    k->has_variable_vring_alignment = true;
+    k->get_dma_as = virtio_msg_get_dma_as;
+
+    /* Needed for multiple devs of the same kind (virtio-net).  */
+    bus_class->get_dev_path = virtio_msg_bus_get_dev_path;
+}
+
+static const TypeInfo virtio_msg_bus_types[] = {
+    {
+        /* Specialized virtio-bus with our custom callbacks.  */
+        .name          = TYPE_VIRTIO_MSG_PROXY_BUS,
+        .parent        = TYPE_VIRTIO_BUS,
+        .instance_size = sizeof(VirtioBusState),
+        .class_init    = virtio_msg_bus_class_init,
+    },
+    {
+        /*
+         * Outer bus to hold virtio-msg objects making them visible to the
+         * qom-tree.
+         */
+        .name          = TYPE_VIRTIO_MSG_OUTER_BUS,
+        .parent        = TYPE_BUS,
+        .instance_size = sizeof(BusState),
+    },
+};
+
+DEFINE_TYPES(virtio_msg_bus_types);
diff --git a/include/hw/virtio/virtio-msg-bus.h b/include/hw/virtio/virtio-msg-bus.h
new file mode 100644
index 0000000000..12ef8030f4
--- /dev/null
+++ b/include/hw/virtio/virtio-msg-bus.h
@@ -0,0 +1,95 @@
+/*
+ * VirtIO MSG bus.
+ *
+ * Copyright (c) 2024 Advanced Micro Devices, Inc.
+ * Written by Edgar E. Iglesias <edgar.iglesias@amd.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef QEMU_VIRTIO_MSG_BUS_H
+#define QEMU_VIRTIO_MSG_BUS_H
+
+#include "qom/object.h"
+#include "system/dma.h"
+#include "hw/qdev-core.h"
+#include "hw/virtio/virtio-msg-prot.h"
+
+#define TYPE_VIRTIO_MSG_BUS "virtio-msg-bus"
+DECLARE_INSTANCE_CHECKER(BusState, VIRTIO_MSG_BUS,
+                         TYPE_VIRTIO_MSG_BUS)
+
+
+#define TYPE_VIRTIO_MSG_BUS_DEVICE "virtio-msg-bus-device"
+OBJECT_DECLARE_TYPE(VirtIOMSGBusDevice, VirtIOMSGBusDeviceClass,
+                    VIRTIO_MSG_BUS_DEVICE)
+
+typedef struct VirtIOMSGBusPort {
+    int (*receive)(VirtIOMSGBusDevice *bd, VirtIOMSG *msg);
+    bool is_driver;
+} VirtIOMSGBusPort;
+
+struct VirtIOMSGBusDeviceClass {
+    /*< private >*/
+    DeviceClass parent_class;
+
+    DeviceRealize parent_realize;
+    DeviceUnrealize parent_unrealize;
+
+    /*
+     * Ask the bus to receive and process all messages that
+     * are readily available. The bus will call the registered
+     * VirtIOMSGBusPort.receive() function for each message.
+     *
+     * Will return immediately if no messages are available.
+     */
+    void (*process)(VirtIOMSGBusDevice *bd);
+
+    /*
+     * Called by the transport to send a message.
+     */
+    int (*send)(VirtIOMSGBusDevice *bd, VirtIOMSG *msg_req);
+};
+
+typedef struct VirtIOMSGBusDevice {
+    DeviceState parent;
+
+    const VirtIOMSGBusPort *peer;
+    void *opaque;
+} VirtIOMSGBusDevice;
+
+static inline VirtIOMSGBusDevice *virtio_msg_bus_get_device(BusState *qbus)
+{
+    BusChild *kid = QTAILQ_FIRST(&qbus->children);
+    DeviceState *qdev = kid ? kid->child : NULL;
+
+    return (VirtIOMSGBusDevice *)qdev;
+}
+
+static inline bool virtio_msg_bus_connected(BusState *bus)
+{
+    VirtIOMSGBusDevice *bd = virtio_msg_bus_get_device(bus);
+
+    return bd && bd->peer != NULL;
+}
+
+void virtio_msg_bus_process(VirtIOMSGBusDevice *bd);
+
+bool virtio_msg_bus_connect(BusState *bus,
+                            const VirtIOMSGBusPort *port,
+                            void *opaque);
+
+static inline void
+virtio_msg_bus_receive(VirtIOMSGBusDevice *bd, VirtIOMSG *msg)
+{
+    virtio_msg_unpack(msg);
+    bd->peer->receive(bd, msg);
+}
+
+int virtio_msg_bus_send(BusState *bus, VirtIOMSG *msg_req);
+
+static inline AddressSpace *virtio_msg_bus_get_remote_as(BusState *bus)
+{
+    return &address_space_memory;
+}
+#endif
diff --git a/include/hw/virtio/virtio-msg-prot.h b/include/hw/virtio/virtio-msg-prot.h
new file mode 100644
index 0000000000..8b3828b2b6
--- /dev/null
+++ b/include/hw/virtio/virtio-msg-prot.h
@@ -0,0 +1,846 @@
+/*
+ * Virtio MSG - Message packing/unpacking functions.
+ *
+ * Copyright (c) 2024 Advanced Micro Devices, Inc.
+ * Written by Edgar E. Iglesias <edgar.iglesias@amd.com>.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef QEMU_VIRTIO_MSG_H
+#define QEMU_VIRTIO_MSG_H
+
+#include <stdint.h>
+#include "standard-headers/linux/virtio_config.h"
+
+enum {
+    VIRTIO_MSG_NO_ERROR = 0,
+    VIRTIO_MSG_ERROR_RETRY,
+    VIRTIO_MSG_ERROR_TIMEOUT,
+    VIRTIO_MSG_ERROR_UNSUPPORTED_MESSAGE_ID,
+    VIRTIO_MSG_ERROR_BAD_MESSAGE,
+    VIRTIO_MSG_ERROR_MEMORY, /* General memory error. */
+};
+
+enum {
+    VIRTIO_MSG_DEVICE_INFO       = 0x02,
+    VIRTIO_MSG_GET_FEATURES      = 0x03,
+    VIRTIO_MSG_SET_FEATURES      = 0x04,
+    VIRTIO_MSG_GET_CONFIG        = 0x05,
+    VIRTIO_MSG_SET_CONFIG        = 0x06,
+    VIRTIO_MSG_GET_DEVICE_STATUS = 0x07,
+    VIRTIO_MSG_SET_DEVICE_STATUS = 0x08,
+    VIRTIO_MSG_GET_VQUEUE        = 0x09,
+    VIRTIO_MSG_SET_VQUEUE        = 0x0a,
+    VIRTIO_MSG_RESET_VQUEUE      = 0x0b,
+    VIRTIO_MSG_GET_SHM           = 0x0c, /* Not yet supported */
+    VIRTIO_MSG_EVENT_CONFIG      = 0x40,
+    VIRTIO_MSG_EVENT_AVAIL       = 0x41,
+    VIRTIO_MSG_EVENT_USED        = 0x42,
+
+    /* Generic bus messages.  */
+    VIRTIO_MSG_BUS_GET_DEVICES   = 0x02,
+
+    VIRTIO_MSG_MAX = VIRTIO_MSG_EVENT_USED,
+};
+
+#define VIRTIO_MSG_MAX_SIZE 48
+
+#define VIRTIO_MSG_TYPE_RESPONSE  (1 << 0)
+#define VIRTIO_MSG_TYPE_BUS       (1 << 1)
+
+typedef struct VirtIOMSG {
+    uint8_t type;
+    uint8_t msg_id;
+    uint16_t dev_num;
+    uint16_t token;
+    uint16_t msg_size;
+
+    union {
+        uint8_t payload_u8[40];
+
+        struct {
+            uint32_t device_id;
+            uint32_t vendor_id;
+            uint32_t num_feature_bits;
+            uint32_t config_size;
+            uint32_t max_vqs;
+            uint16_t admin_vq_idx;
+            uint16_t admin_vq_count;
+        } QEMU_PACKED get_device_info_resp;
+        struct {
+            uint32_t index;
+            uint32_t num;
+        } QEMU_PACKED get_features;
+        struct {
+            uint32_t index;
+            uint32_t num;
+            uint32_t b32[];
+        } QEMU_PACKED get_features_resp;
+        struct {
+            uint32_t index;
+            uint32_t num;
+            uint32_t b32[];
+        } QEMU_PACKED set_features;
+        struct {
+            uint32_t offset;
+            uint32_t size;
+        } QEMU_PACKED get_config;
+        struct {
+            uint32_t generation;
+            uint32_t offset;
+            uint32_t size;
+            uint8_t data[];
+        } QEMU_PACKED get_config_resp;
+        struct {
+            uint32_t generation;
+            uint32_t offset;
+            uint32_t size;
+            uint8_t data[];
+        } QEMU_PACKED set_config;
+        struct {
+            uint32_t generation;
+            uint32_t offset;
+            uint32_t size;
+            uint8_t data[];
+        } QEMU_PACKED set_config_resp;
+        struct {
+            uint32_t status;
+        } QEMU_PACKED get_device_status_resp;
+        struct {
+            uint32_t status;
+        } QEMU_PACKED set_device_status;
+        struct {
+            uint32_t status;
+        } QEMU_PACKED set_device_status_resp;
+        struct {
+            uint32_t index;
+        } QEMU_PACKED get_vqueue;
+        struct {
+            uint32_t index;
+            uint32_t max_size;
+            uint32_t size;
+            uint32_t reserved;
+            uint64_t descriptor_addr;
+            uint64_t driver_addr;
+            uint64_t device_addr;
+        } QEMU_PACKED get_vqueue_resp;
+        struct {
+            uint32_t index;
+            uint32_t unused;
+            uint32_t size;
+            uint32_t reserved;
+            uint64_t descriptor_addr;
+            uint64_t driver_addr;
+            uint64_t device_addr;
+        } QEMU_PACKED set_vqueue;
+        struct {
+            uint32_t index;
+        } QEMU_PACKED reset_vqueue;
+        struct {
+            uint32_t status;
+            uint32_t generation;
+            uint32_t offset;
+            uint32_t size;
+            uint8_t config_value[];
+        } QEMU_PACKED event_config;
+        struct {
+            uint32_t index;
+            uint32_t next_offset_wrap;
+        } QEMU_PACKED event_avail;
+        struct {
+            uint32_t index;
+        } QEMU_PACKED event_used;
+
+        /* Generic bus messages.  */
+        struct {
+            uint16_t offset;
+            uint16_t num;
+        } QEMU_PACKED bus_get_devices;
+        struct {
+            uint16_t offset;
+            uint16_t num;
+            uint16_t next_offset;
+            uint8_t data[];
+        } QEMU_PACKED bus_get_devices_resp;
+    };
+} QEMU_PACKED VirtIOMSG;
+
+/* Maximum number of 32b feature-blocks in a single message.  */
+#define VIRTIO_MSG_MAX_FEATURE_NUM \
+    ((VIRTIO_MSG_MAX_SIZE - offsetof(VirtIOMSG, get_features_resp.b32)) / 4)
+
+/* Maximum amount of config-data in a single message, in bytes.  */
+#define VIRTIO_MSG_MAX_CONFIG_BYTES \
+    (VIRTIO_MSG_MAX_SIZE - offsetof(VirtIOMSG, set_config.data))
+
+#define LE_TO_CPU(v)                                        \
+{                                                           \
+    if (sizeof(v) == 2) {                                   \
+        v = le16_to_cpu(v);                                 \
+    } else if (sizeof(v) == 4) {                            \
+        v = le32_to_cpu(v);                                 \
+    } else if (sizeof(v) == 8) {                            \
+        v = le64_to_cpu(v);                                 \
+    } else {                                                \
+        g_assert_not_reached();                             \
+    }                                                       \
+}
+
+static inline void virtio_msg_unpack_bus(VirtIOMSG *msg)
+{
+    switch (msg->msg_id) {
+    case VIRTIO_MSG_BUS_GET_DEVICES:
+        LE_TO_CPU(msg->bus_get_devices.offset);
+        LE_TO_CPU(msg->bus_get_devices.num);
+        break;
+    default:
+        break;
+    }
+}
+
+/**
+ * virtio_msg_unpack_resp: Unpacks a wire virtio message responses into
+ *                         a host version
+ * @msg: the virtio message to unpack
+ *
+ * See virtio_msg_unpack().
+ */
+static inline void virtio_msg_unpack_resp(VirtIOMSG *msg)
+{
+    int i;
+
+    switch (msg->msg_id) {
+    case VIRTIO_MSG_DEVICE_INFO:
+        LE_TO_CPU(msg->get_device_info_resp.device_id);
+        LE_TO_CPU(msg->get_device_info_resp.vendor_id);
+        LE_TO_CPU(msg->get_device_info_resp.num_feature_bits);
+        LE_TO_CPU(msg->get_device_info_resp.config_size);
+        LE_TO_CPU(msg->get_device_info_resp.max_vqs);
+        LE_TO_CPU(msg->get_device_info_resp.admin_vq_idx);
+        LE_TO_CPU(msg->get_device_info_resp.admin_vq_count);
+        break;
+    case VIRTIO_MSG_GET_FEATURES:
+        LE_TO_CPU(msg->get_features_resp.index);
+        LE_TO_CPU(msg->get_features_resp.num);
+        for (i = 0; i < VIRTIO_MSG_MAX_FEATURE_NUM &&
+                    i < msg->get_features_resp.num; i++) {
+            LE_TO_CPU(msg->get_features_resp.b32[i]);
+        }
+        break;
+    case VIRTIO_MSG_GET_DEVICE_STATUS:
+        LE_TO_CPU(msg->get_device_status_resp.status);
+        break;
+    case VIRTIO_MSG_GET_CONFIG:
+        LE_TO_CPU(msg->get_config_resp.generation);
+        LE_TO_CPU(msg->get_config_resp.offset);
+        LE_TO_CPU(msg->get_config_resp.size);
+        break;
+    case VIRTIO_MSG_SET_CONFIG:
+        LE_TO_CPU(msg->set_config_resp.generation);
+        LE_TO_CPU(msg->set_config_resp.offset);
+        LE_TO_CPU(msg->set_config_resp.size);
+        break;
+    case VIRTIO_MSG_GET_VQUEUE:
+        LE_TO_CPU(msg->get_vqueue_resp.index);
+        LE_TO_CPU(msg->get_vqueue_resp.max_size);
+        LE_TO_CPU(msg->get_vqueue_resp.size);
+        LE_TO_CPU(msg->get_vqueue_resp.descriptor_addr);
+        LE_TO_CPU(msg->get_vqueue_resp.driver_addr);
+        LE_TO_CPU(msg->get_vqueue_resp.device_addr);
+        break;
+    default:
+        break;
+    }
+}
+
+/**
+ * virtio_msg_unpack: Unpacks a wire virtio message into a host version
+ * @msg: the virtio message to unpack
+ *
+ * Virtio messages arriving on the virtio message bus have a specific
+ * format (little-endian, packet encoding, etc). To simplify for the
+ * the rest of the implementation, we have packers and unpackers that
+ * convert the wire messages into host versions. This includes endianess
+ * conversion and potentially future decoding and expansion.
+ *
+ * At the moment, we only do endian conversion, virtio_msg_unpack() should
+ * get completely eliminated by the compiler when targetting little-endian
+ * hosts.
+ */
+static inline void virtio_msg_unpack(VirtIOMSG *msg)
+{
+    int i;
+
+    LE_TO_CPU(msg->dev_num);
+    LE_TO_CPU(msg->token);
+    LE_TO_CPU(msg->msg_size);
+
+    if (msg->type & VIRTIO_MSG_TYPE_BUS) {
+        virtio_msg_unpack_bus(msg);
+        return;
+    }
+
+    if (msg->type & VIRTIO_MSG_TYPE_RESPONSE) {
+        virtio_msg_unpack_resp(msg);
+        return;
+    }
+
+    switch (msg->msg_id) {
+    case VIRTIO_MSG_GET_FEATURES:
+        LE_TO_CPU(msg->get_features.index);
+        LE_TO_CPU(msg->get_features.num);
+        break;
+    case VIRTIO_MSG_SET_FEATURES:
+        LE_TO_CPU(msg->set_features.index);
+        LE_TO_CPU(msg->set_features.num);
+        for (i = 0; i < VIRTIO_MSG_MAX_FEATURE_NUM &&
+                    i < msg->set_features.num; i++) {
+            LE_TO_CPU(msg->set_features.b32[i]);
+        }
+        break;
+    case VIRTIO_MSG_SET_DEVICE_STATUS:
+        LE_TO_CPU(msg->set_device_status.status);
+        break;
+    case VIRTIO_MSG_GET_CONFIG:
+        LE_TO_CPU(msg->get_config.offset);
+        LE_TO_CPU(msg->get_config.size);
+        break;
+    case VIRTIO_MSG_SET_CONFIG:
+        LE_TO_CPU(msg->set_config.generation);
+        LE_TO_CPU(msg->set_config.offset);
+        LE_TO_CPU(msg->set_config.size);
+        break;
+    case VIRTIO_MSG_GET_VQUEUE:
+        LE_TO_CPU(msg->get_vqueue.index);
+        break;
+    case VIRTIO_MSG_SET_VQUEUE:
+        LE_TO_CPU(msg->set_vqueue.index);
+        LE_TO_CPU(msg->set_vqueue.size);
+        LE_TO_CPU(msg->set_vqueue.descriptor_addr);
+        LE_TO_CPU(msg->set_vqueue.driver_addr);
+        LE_TO_CPU(msg->set_vqueue.device_addr);
+        break;
+    case VIRTIO_MSG_RESET_VQUEUE:
+        LE_TO_CPU(msg->reset_vqueue.index);
+        break;
+    case VIRTIO_MSG_EVENT_CONFIG:
+        LE_TO_CPU(msg->event_config.status);
+        LE_TO_CPU(msg->event_config.generation);
+        LE_TO_CPU(msg->event_config.offset);
+        LE_TO_CPU(msg->event_config.size);
+        break;
+    case VIRTIO_MSG_EVENT_AVAIL:
+        LE_TO_CPU(msg->event_avail.index);
+        LE_TO_CPU(msg->event_avail.next_offset_wrap);
+        break;
+    case VIRTIO_MSG_EVENT_USED:
+        LE_TO_CPU(msg->event_used.index);
+        break;
+    default:
+        break;
+    }
+}
+
+static inline size_t virtio_msg_header_size(void)
+{
+    return offsetof(VirtIOMSG, payload_u8);
+}
+
+static inline void virtio_msg_pack_header(VirtIOMSG *msg,
+                                          uint8_t msg_id,
+                                          uint8_t type,
+                                          uint16_t dev_num,
+                                          uint16_t token,
+                                          uint16_t payload_size)
+{
+    uint16_t msg_size = virtio_msg_header_size() + payload_size;
+
+    msg->type = type;
+    msg->msg_id = msg_id;
+    msg->dev_num = cpu_to_le16(dev_num);
+    msg->token = cpu_to_le16(token);
+    msg->msg_size = cpu_to_le16(msg_size);
+
+    /* Keep things predictable.  */
+    memset(msg->payload_u8, 0, sizeof msg->payload_u8);
+}
+
+static inline void virtio_msg_pack_get_device_info(VirtIOMSG *msg,
+                                                   uint16_t dev_num,
+                                                   uint16_t token)
+{
+    virtio_msg_pack_header(msg, VIRTIO_MSG_DEVICE_INFO, 0, dev_num, token, 0);
+}
+
+static inline void virtio_msg_pack_get_device_info_resp(VirtIOMSG *msg,
+                                                   uint16_t dev_num,
+                                                   uint16_t token,
+                                                   uint32_t device_id,
+                                                   uint32_t vendor_id,
+                                                   uint32_t num_feature_bits,
+                                                   uint32_t config_size,
+                                                   uint32_t max_vqs,
+                                                   uint16_t admin_vq_idx,
+                                                   uint16_t admin_vq_count)
+{
+    virtio_msg_pack_header(msg, VIRTIO_MSG_DEVICE_INFO,
+                           VIRTIO_MSG_TYPE_RESPONSE, dev_num, token,
+                           sizeof msg->get_device_info_resp);
+
+    msg->get_device_info_resp.device_id = cpu_to_le32(device_id);
+    msg->get_device_info_resp.vendor_id = cpu_to_le32(vendor_id);
+    msg->get_device_info_resp.num_feature_bits = cpu_to_le32(num_feature_bits);
+    msg->get_device_info_resp.config_size = cpu_to_le32(config_size);
+    msg->get_device_info_resp.max_vqs = cpu_to_le32(max_vqs);
+    msg->get_device_info_resp.admin_vq_idx = cpu_to_le16(admin_vq_idx);
+    msg->get_device_info_resp.admin_vq_count = cpu_to_le16(admin_vq_count);
+}
+
+static inline void virtio_msg_pack_get_features(VirtIOMSG *msg,
+                                                uint16_t dev_num,
+                                                uint16_t token,
+                                                uint32_t index,
+                                                uint32_t num)
+{
+    virtio_msg_pack_header(msg, VIRTIO_MSG_GET_FEATURES, 0, dev_num, token,
+                           sizeof msg->get_features);
+
+    msg->get_features.index = cpu_to_le32(index);
+    msg->get_features.num = cpu_to_le32(num);
+}
+
+static inline void virtio_msg_pack_get_features_resp(VirtIOMSG *msg,
+                                                     uint16_t dev_num,
+                                                     uint16_t token,
+                                                     uint32_t index,
+                                                     uint32_t num,
+                                                     uint32_t *f)
+{
+    unsigned int i;
+
+    virtio_msg_pack_header(msg, VIRTIO_MSG_GET_FEATURES,
+                           VIRTIO_MSG_TYPE_RESPONSE, dev_num, token,
+                           sizeof msg->get_features_resp + num * sizeof(*f));
+
+    msg->get_features_resp.index = cpu_to_le32(index);
+    msg->get_features_resp.num = cpu_to_le32(num);
+
+    assert(num <= VIRTIO_MSG_MAX_FEATURE_NUM);
+
+    for (i = 0; i < num && i < VIRTIO_MSG_MAX_FEATURE_NUM; i++) {
+        msg->get_features_resp.b32[i] = cpu_to_le32(f[i]);
+    }
+}
+
+static inline void virtio_msg_pack_set_features(VirtIOMSG *msg,
+                                                uint16_t dev_num,
+                                                uint16_t token,
+                                                uint32_t index,
+                                                uint32_t num,
+                                                uint32_t *f)
+{
+    unsigned int i;
+
+    virtio_msg_pack_header(msg, VIRTIO_MSG_SET_FEATURES, 0, dev_num, token,
+                           sizeof msg->set_features + num * sizeof(*f));
+
+    msg->set_features.index = cpu_to_le32(index);
+    msg->set_features.num = cpu_to_le32(num);
+
+    assert(num <= VIRTIO_MSG_MAX_FEATURE_NUM);
+
+    for (i = 0; i < num && i < VIRTIO_MSG_MAX_FEATURE_NUM; i++) {
+        msg->set_features.b32[i] = cpu_to_le32(f[i]);
+    }
+}
+
+static inline void virtio_msg_pack_set_features_resp(VirtIOMSG *msg,
+                                                     uint16_t dev_num,
+                                                     uint16_t token)
+{
+    virtio_msg_pack_header(msg, VIRTIO_MSG_SET_FEATURES,
+                           VIRTIO_MSG_TYPE_RESPONSE, dev_num, token, 0);
+}
+
+static inline void virtio_msg_pack_set_device_status(VirtIOMSG *msg,
+                                                     uint16_t dev_num,
+                                                     uint16_t token,
+                                                     uint32_t status)
+{
+    virtio_msg_pack_header(msg, VIRTIO_MSG_SET_DEVICE_STATUS, 0, dev_num,
+                           token, sizeof msg->set_device_status);
+
+    msg->set_device_status.status = cpu_to_le32(status);
+}
+
+static inline void virtio_msg_pack_set_device_status_resp(VirtIOMSG *msg,
+                                                          uint16_t dev_num,
+                                                          uint16_t token,
+                                                          uint32_t status)
+{
+    virtio_msg_pack_header(msg, VIRTIO_MSG_SET_DEVICE_STATUS,
+                           VIRTIO_MSG_TYPE_RESPONSE, dev_num, token,
+                           sizeof msg->set_device_status_resp);
+
+    msg->set_device_status_resp.status = cpu_to_le32(status);
+}
+
+static inline void virtio_msg_pack_get_device_status(VirtIOMSG *msg,
+                                                     uint16_t dev_num,
+                                                     uint16_t token)
+{
+    virtio_msg_pack_header(msg, VIRTIO_MSG_GET_DEVICE_STATUS, 0,
+                           dev_num, token, 0);
+}
+
+static inline void virtio_msg_pack_get_device_status_resp(VirtIOMSG *msg,
+                                                          uint16_t dev_num,
+                                                          uint16_t token,
+                                                          uint32_t status)
+{
+    virtio_msg_pack_header(msg, VIRTIO_MSG_GET_DEVICE_STATUS,
+                           VIRTIO_MSG_TYPE_RESPONSE, dev_num, token,
+                           sizeof msg->get_device_status_resp);
+
+    msg->get_device_status_resp.status = cpu_to_le32(status);
+}
+
+static inline void virtio_msg_pack_get_config(VirtIOMSG *msg,
+                                              uint16_t dev_num,
+                                              uint16_t token,
+                                              uint32_t size,
+                                              uint32_t offset)
+{
+    virtio_msg_pack_header(msg, VIRTIO_MSG_GET_CONFIG, 0, dev_num, token,
+                           sizeof msg->get_config);
+
+    msg->get_config.offset = cpu_to_le32(offset);
+    msg->get_config.size = cpu_to_le32(size);
+}
+
+static inline void virtio_msg_pack_get_config_resp(VirtIOMSG *msg,
+                                                   uint16_t dev_num,
+                                                   uint16_t token,
+                                                   uint32_t size,
+                                                   uint32_t offset,
+                                                   uint32_t generation,
+                                                   uint8_t data[])
+{
+    assert(size <= VIRTIO_MSG_MAX_CONFIG_BYTES);
+
+    virtio_msg_pack_header(msg, VIRTIO_MSG_GET_CONFIG,
+                           VIRTIO_MSG_TYPE_RESPONSE, dev_num, token,
+                           sizeof msg->get_config_resp + size);
+
+    msg->get_config_resp.generation = cpu_to_le32(generation);
+    msg->get_config_resp.offset = cpu_to_le32(offset);
+    msg->get_config_resp.size = cpu_to_le32(size);
+
+    memcpy(&msg->get_config_resp.data, data, size);
+}
+
+static inline void virtio_msg_pack_set_config(VirtIOMSG *msg,
+                                              uint16_t dev_num,
+                                              uint16_t token,
+                                              uint32_t size,
+                                              uint32_t offset,
+                                              uint32_t generation,
+                                              uint8_t data[])
+{
+    assert(size <= VIRTIO_MSG_MAX_CONFIG_BYTES);
+
+    virtio_msg_pack_header(msg, VIRTIO_MSG_SET_CONFIG, 0, dev_num, token,
+                           sizeof msg->set_config + size);
+
+    msg->set_config.offset = cpu_to_le32(offset);
+    msg->set_config.size = cpu_to_le32(size);
+    msg->set_config.generation = cpu_to_le32(generation);
+
+    memcpy(&msg->set_config.data, data, size);
+}
+
+static inline void virtio_msg_pack_set_config_resp(VirtIOMSG *msg,
+                                                   uint16_t dev_num,
+                                                   uint16_t token,
+                                                   uint32_t size,
+                                                   uint32_t offset,
+                                                   uint32_t generation,
+                                                   uint8_t data[])
+{
+    assert(size <= VIRTIO_MSG_MAX_CONFIG_BYTES);
+
+    virtio_msg_pack_header(msg, VIRTIO_MSG_SET_CONFIG,
+                           VIRTIO_MSG_TYPE_RESPONSE, dev_num, token,
+                           sizeof msg->set_config_resp + size);
+
+    msg->set_config_resp.offset = cpu_to_le32(offset);
+    msg->set_config_resp.size = cpu_to_le32(size);
+    msg->set_config_resp.generation = cpu_to_le32(generation);
+
+    memcpy(&msg->set_config_resp.data, data, size);
+}
+
+static inline void virtio_msg_pack_get_vqueue(VirtIOMSG *msg,
+                                              uint16_t dev_num,
+                                              uint16_t token,
+                                              uint32_t index)
+{
+    virtio_msg_pack_header(msg, VIRTIO_MSG_GET_VQUEUE, 0, dev_num, token,
+                           sizeof msg->get_vqueue);
+
+    msg->get_vqueue.index = cpu_to_le32(index);
+}
+
+static inline void virtio_msg_pack_get_vqueue_resp(VirtIOMSG *msg,
+                                                   uint16_t dev_num,
+                                                   uint16_t token,
+                                                   uint32_t index,
+                                                   uint32_t max_size,
+                                                   uint32_t size,
+                                                   uint64_t descriptor_addr,
+                                                   uint64_t driver_addr,
+                                                   uint64_t device_addr)
+{
+    virtio_msg_pack_header(msg, VIRTIO_MSG_GET_VQUEUE,
+                           VIRTIO_MSG_TYPE_RESPONSE, dev_num, token,
+                           sizeof msg->get_vqueue_resp);
+
+    msg->get_vqueue_resp.index = cpu_to_le32(index);
+    msg->get_vqueue_resp.max_size = cpu_to_le32(max_size);
+    msg->get_vqueue_resp.size = cpu_to_le32(size);
+    msg->get_vqueue_resp.descriptor_addr = cpu_to_le64(descriptor_addr);
+    msg->get_vqueue_resp.driver_addr = cpu_to_le64(driver_addr);
+    msg->get_vqueue_resp.device_addr = cpu_to_le64(device_addr);
+}
+
+static inline void virtio_msg_pack_reset_vqueue(VirtIOMSG *msg,
+                                                uint16_t dev_num,
+                                                uint16_t token,
+                                                uint32_t index)
+{
+    virtio_msg_pack_header(msg, VIRTIO_MSG_RESET_VQUEUE, 0, dev_num, token,
+                           sizeof msg->reset_vqueue);
+
+    msg->reset_vqueue.index = cpu_to_le32(index);
+}
+
+static inline void virtio_msg_pack_reset_vqueue_resp(VirtIOMSG *msg,
+                                                     uint16_t dev_num,
+                                                     uint16_t token)
+{
+    virtio_msg_pack_header(msg, VIRTIO_MSG_RESET_VQUEUE,
+                           VIRTIO_MSG_TYPE_RESPONSE, dev_num, token, 0);
+}
+
+static inline void virtio_msg_pack_set_vqueue(VirtIOMSG *msg,
+                                              uint16_t dev_num,
+                                              uint16_t token,
+                                              uint32_t index,
+                                              uint32_t size,
+                                              uint64_t descriptor_addr,
+                                              uint64_t driver_addr,
+                                              uint64_t device_addr)
+{
+    virtio_msg_pack_header(msg, VIRTIO_MSG_SET_VQUEUE, 0, dev_num, token,
+                           sizeof msg->set_vqueue);
+
+    msg->set_vqueue.index = cpu_to_le32(index);
+    msg->set_vqueue.unused = 0;
+    msg->set_vqueue.size = cpu_to_le32(size);
+    msg->set_vqueue.descriptor_addr = cpu_to_le64(descriptor_addr);
+    msg->set_vqueue.driver_addr = cpu_to_le64(driver_addr);
+    msg->set_vqueue.device_addr = cpu_to_le64(device_addr);
+}
+
+static inline void virtio_msg_pack_set_vqueue_resp(VirtIOMSG *msg,
+                                                   uint16_t dev_num,
+                                                   uint16_t token)
+{
+    virtio_msg_pack_header(msg, VIRTIO_MSG_SET_VQUEUE,
+                           VIRTIO_MSG_TYPE_RESPONSE, dev_num, token, 0);
+}
+
+static inline void virtio_msg_pack_event_avail(VirtIOMSG *msg,
+                                               uint16_t dev_num,
+                                               uint32_t index,
+                                               uint32_t next_offset,
+                                               bool next_wrap)
+{
+    uint32_t next_ow;
+
+    virtio_msg_pack_header(msg, VIRTIO_MSG_EVENT_AVAIL, 0, dev_num, 0,
+                           sizeof msg->event_avail);
+
+    /* next_offset is 31b wide.  */
+    assert((next_offset & 0x80000000U) == 0);
+
+    /* Pack the next_offset_wrap field. */
+    next_ow = next_wrap << 31;
+    next_ow |= next_offset;
+
+    msg->event_avail.index = cpu_to_le32(index);
+    msg->event_avail.next_offset_wrap = cpu_to_le32(next_ow);
+}
+
+static inline void virtio_msg_pack_event_used(VirtIOMSG *msg,
+                                              uint16_t dev_num,
+                                              uint32_t index)
+{
+    virtio_msg_pack_header(msg, VIRTIO_MSG_EVENT_USED, 0, dev_num, 0,
+                           sizeof msg->event_used);
+
+    msg->event_used.index = cpu_to_le32(index);
+}
+
+static inline void virtio_msg_pack_event_config(VirtIOMSG *msg,
+                                                uint16_t dev_num,
+                                                uint32_t status,
+                                                uint32_t generation,
+                                                uint32_t offset,
+                                                uint32_t size,
+                                                uint8_t *value)
+{
+    unsigned int max_size;
+
+    virtio_msg_pack_header(msg, VIRTIO_MSG_EVENT_CONFIG, 0, dev_num, 0,
+                           sizeof msg->event_config);
+
+    msg->event_config.status = cpu_to_le32(status);
+    msg->event_config.generation = cpu_to_le32(generation);
+    msg->event_config.offset = cpu_to_le32(offset);
+    msg->event_config.size = cpu_to_le32(size);
+
+    max_size = VIRTIO_MSG_MAX_SIZE;
+    max_size -= offsetof(VirtIOMSG, event_config.config_value);
+    assert(size <= max_size);
+
+    if (size > 0 && size <= max_size) {
+        memcpy(&msg->event_config.config_value[0], value, size);
+    }
+}
+
+static inline void virtio_msg_pack_bus_get_devices(VirtIOMSG *msg,
+                                                   uint16_t offset,
+                                                   uint16_t num)
+{
+    virtio_msg_pack_header(msg,
+                           VIRTIO_MSG_BUS_GET_DEVICES, VIRTIO_MSG_TYPE_BUS,
+                           0, 0, sizeof msg->bus_get_devices);
+
+    msg->bus_get_devices.offset = cpu_to_le16(offset);
+    msg->bus_get_devices.num = cpu_to_le16(num);
+}
+
+static inline void virtio_msg_pack_bus_get_devices_resp(VirtIOMSG *msg,
+                                                        uint16_t offset,
+                                                        uint16_t num,
+                                                        uint16_t next_offset,
+                                                        uint8_t *data)
+{
+    virtio_msg_pack_header(msg,
+                           VIRTIO_MSG_BUS_GET_DEVICES,
+                           VIRTIO_MSG_TYPE_BUS | VIRTIO_MSG_TYPE_RESPONSE,
+                           0, 0, sizeof msg->bus_get_devices_resp + num);
+
+    msg->bus_get_devices_resp.offset = cpu_to_le16(offset);
+    msg->bus_get_devices_resp.num = cpu_to_le16(num);
+    msg->bus_get_devices_resp.next_offset = cpu_to_le16(next_offset);
+
+    memcpy(msg->bus_get_devices_resp.data, data, num / 8);
+}
+
+static inline const char *virtio_msg_id_to_str(unsigned int type)
+{
+#define VIRTIO_MSG_TYPE2STR(x) [VIRTIO_MSG_ ## x] = stringify(x)
+    static const char *type2str[VIRTIO_MSG_MAX + 1] = {
+        VIRTIO_MSG_TYPE2STR(DEVICE_INFO),
+        VIRTIO_MSG_TYPE2STR(GET_FEATURES),
+        VIRTIO_MSG_TYPE2STR(SET_FEATURES),
+        VIRTIO_MSG_TYPE2STR(GET_CONFIG),
+        VIRTIO_MSG_TYPE2STR(SET_CONFIG),
+        VIRTIO_MSG_TYPE2STR(GET_DEVICE_STATUS),
+        VIRTIO_MSG_TYPE2STR(SET_DEVICE_STATUS),
+        VIRTIO_MSG_TYPE2STR(GET_VQUEUE),
+        VIRTIO_MSG_TYPE2STR(SET_VQUEUE),
+        VIRTIO_MSG_TYPE2STR(RESET_VQUEUE),
+        VIRTIO_MSG_TYPE2STR(EVENT_CONFIG),
+        VIRTIO_MSG_TYPE2STR(EVENT_AVAIL),
+        VIRTIO_MSG_TYPE2STR(EVENT_USED),
+    };
+
+    return type2str[type];
+}
+
+static inline void virtio_msg_print_status(uint32_t status)
+{
+    printf("status %x", status);
+
+    if (status & VIRTIO_CONFIG_S_ACKNOWLEDGE) {
+        printf(" ACKNOWLEDGE");
+    }
+    if (status & VIRTIO_CONFIG_S_DRIVER) {
+        printf(" DRIVER");
+    }
+    if (status & VIRTIO_CONFIG_S_DRIVER_OK) {
+        printf(" DRIVER_OK");
+    }
+    if (status & VIRTIO_CONFIG_S_FEATURES_OK) {
+        printf(" FEATURES_OK");
+    }
+    if (status & VIRTIO_CONFIG_S_NEEDS_RESET) {
+        printf(" NEEDS_RESET");
+    }
+    if (status & VIRTIO_CONFIG_S_FAILED) {
+        printf(" FAILED");
+    }
+
+    printf("\n");
+}
+
+static inline void virtio_msg_print(VirtIOMSG *msg)
+{
+    bool resp = msg->type & VIRTIO_MSG_TYPE_RESPONSE;
+    size_t payload_size;
+    int i;
+
+    assert(msg);
+    printf("virtio-msg: id %s 0x%x type 0x%x dev_num 0x%x msg_size 0x%x\n",
+           virtio_msg_id_to_str(msg->msg_id), msg->msg_id,
+           msg->type, msg->dev_num, msg->msg_size);
+
+    payload_size = msg->msg_size - offsetof(VirtIOMSG, payload_u8);
+    if (payload_size > ARRAY_SIZE(msg->payload_u8)) {
+        printf("Size overflow! %zu > %zu\n",
+                payload_size, ARRAY_SIZE(msg->payload_u8));
+        payload_size = ARRAY_SIZE(msg->payload_u8);
+    }
+
+    for (i = 0; i < payload_size; i++) {
+        printf("%2.2x ", msg->payload_u8[i]);
+        if (((i + 1) %  16) == 0) {
+            printf("\n");
+        }
+    }
+
+    switch (msg->msg_id) {
+    case VIRTIO_MSG_GET_DEVICE_STATUS:
+        if (resp) {
+            virtio_msg_print_status(msg->get_device_status_resp.status);
+        }
+        break;
+    case VIRTIO_MSG_SET_DEVICE_STATUS:
+        virtio_msg_print_status(msg->set_device_status.status);
+        break;
+    case VIRTIO_MSG_SET_VQUEUE:
+        printf("set-vqueue: index=%d size=%d desc-addr=%lx "
+               "driver-addr=%lx device-addr=%lx\n",
+               msg->set_vqueue.index, msg->set_vqueue.size,
+               msg->set_vqueue.descriptor_addr,
+               msg->set_vqueue.driver_addr,
+               msg->set_vqueue.device_addr);
+        break;
+    }
+    printf("\n");
+}
+#endif
diff --git a/include/hw/virtio/virtio-msg.h b/include/hw/virtio/virtio-msg.h
new file mode 100644
index 0000000000..e92bad84d2
--- /dev/null
+++ b/include/hw/virtio/virtio-msg.h
@@ -0,0 +1,56 @@
+/*
+ * Virtio MSG bindings
+ *
+ * Copyright (c) 2024 Advanced Micro Devices, Inc.
+ * Written by Edgar E. Iglesias <edgar.iglesias@amd.com>.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_VIRTIO_MSG_H
+#define HW_VIRTIO_MSG_H
+
+#include "hw/sysbus.h"
+#include "hw/virtio/virtio-bus.h"
+
+#define TYPE_VIRTIO_MSG_PROXY_BUS "virtio-msg-proxy-bus"
+/* This is reusing the VirtioBusState typedef from TYPE_VIRTIO_BUS */
+DECLARE_OBJ_CHECKERS(VirtioBusState, VirtioBusClass,
+                     VIRTIO_MSG_PROXY_BUS, TYPE_VIRTIO_MSG_PROXY_BUS)
+
+#define TYPE_VIRTIO_MSG_OUTER_BUS "virtio-msg-outer-bus"
+OBJECT_DECLARE_SIMPLE_TYPE(BusState, VIRTIO_MSG_OUTER_BUS)
+
+#define TYPE_VIRTIO_MSG_DEV "virtio-msg-dev"
+OBJECT_DECLARE_SIMPLE_TYPE(VirtIOMSGDev, VIRTIO_MSG_DEV)
+
+#define TYPE_VIRTIO_MSG "virtio-msg"
+OBJECT_DECLARE_SIMPLE_TYPE(VirtIOMSGProxy, VIRTIO_MSG)
+
+struct VirtIOMSGDev {
+    DeviceState parent_obj;
+
+    /* virtio-bus */
+    VirtioBusState bus;
+
+    VirtIOMSGProxy *proxy;
+    uint16_t dev_num;
+    uint64_t guest_features;
+};
+
+#define VIRTIO_MSG_MAX_DEVS 32
+struct VirtIOMSGProxy {
+    DeviceState parent_obj;
+
+    AddressSpace dma_as;
+    AddressSpace *bus_as;
+    IOMMUMemoryRegion mr_iommu;
+    MemoryRegion *mr_bus;
+
+    BusState devs_bus[VIRTIO_MSG_MAX_DEVS];
+    VirtIOMSGDev devs[VIRTIO_MSG_MAX_DEVS];
+
+    /* virtio-msg-bus.  */
+    BusState msg_bus;
+};
+#endif
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v1 4/5] hw/misc: Add generic virtio-msg AMP PCI device
  2025-10-28 15:23 [PATCH v1 0/5] virtio: Add the virtio-msg transport Edgar E. Iglesias
                   ` (2 preceding siblings ...)
  2025-10-28 15:23 ` [PATCH v1 3/5] virtio: Add the virtio-msg transport Edgar E. Iglesias
@ 2025-10-28 15:23 ` Edgar E. Iglesias
  2025-10-28 15:23 ` [PATCH v1 5/5] docs: Describe virtio-msg-amp-pci Edgar E. Iglesias
  2025-10-28 15:30 ` [PATCH v1 0/5] virtio: Add the virtio-msg transport Edgar E. Iglesias
  5 siblings, 0 replies; 10+ messages in thread
From: Edgar E. Iglesias @ 2025-10-28 15:23 UTC (permalink / raw)
  To: qemu-devel, Paolo Bonzini, Michael S. Tsirkin
  Cc: alex.bennee, bill.mills, edgar.iglesias

From: "Edgar E. Iglesias" <edgar.iglesias@amd.com>

Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com>
---
 hw/misc/Kconfig                |   7 +
 hw/misc/meson.build            |   1 +
 hw/misc/virtio-msg-amp-pci.c   | 328 +++++++++++++++++++++++++++++++++
 include/hw/virtio/spsc_queue.h | 213 +++++++++++++++++++++
 4 files changed, 549 insertions(+)
 create mode 100644 hw/misc/virtio-msg-amp-pci.c
 create mode 100644 include/hw/virtio/spsc_queue.h

diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index fccd735c24..cdc318bec2 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -25,6 +25,13 @@ config PCI_TESTDEV
     default y if TEST_DEVICES
     depends on PCI
 
+config VIRTIO_MSG_AMP_PCI
+    bool
+    default y if PCI_DEVICES
+    depends on PCI
+    select VIRTIO
+    select VIRTIO_MSG
+
 config EDU
     bool
     default y if TEST_DEVICES
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index b1d8d8e5d2..80d4886808 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -8,6 +8,7 @@ system_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c'))
 system_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c'))
 system_ss.add(when: 'CONFIG_LED', if_true: files('led.c'))
 system_ss.add(when: 'CONFIG_PVPANIC_COMMON', if_true: files('pvpanic.c'))
+system_ss.add(when: 'CONFIG_VIRTIO_MSG_AMP_PCI', if_true: files('virtio-msg-amp-pci.c'))
 
 # ARM devices
 system_ss.add(when: 'CONFIG_PL310', if_true: files('arm_l2x0.c'))
diff --git a/hw/misc/virtio-msg-amp-pci.c b/hw/misc/virtio-msg-amp-pci.c
new file mode 100644
index 0000000000..29fa00d931
--- /dev/null
+++ b/hw/misc/virtio-msg-amp-pci.c
@@ -0,0 +1,328 @@
+/*
+ * Model of a virtio-msg AMP capable PCI device.
+ *
+ * Copyright (C) 2025 Advanced Micro Devices, Inc.
+ * Written by Edgar E. Iglesias <edgar.iglesias@amd.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "qemu/log.h"
+
+#include "migration/vmstate.h"
+#include "hw/qdev-properties.h"
+#include "hw/pci/pci_device.h"
+#include "hw/pci/msix.h"
+#include "hw/sysbus.h"
+#include "hw/register.h"
+
+#include "hw/virtio/virtio-msg.h"
+#include "hw/virtio/virtio-msg-bus.h"
+#include "hw/virtio/spsc_queue.h"
+
+#define TYPE_VMSG_AMP_PCI "virtio-msg-amp-pci"
+OBJECT_DECLARE_SIMPLE_TYPE(VmsgAmpPciState, VMSG_AMP_PCI)
+
+#define TYPE_VMSG_BUS_AMP_PCI "virtio-msg-bus-amp-pci"
+OBJECT_DECLARE_SIMPLE_TYPE(VmsgBusAmpPciState, VMSG_BUS_AMP_PCI)
+#define VMSG_BUS_AMP_PCI_GET_PARENT_CLASS(obj) \
+        OBJECT_GET_PARENT_CLASS(obj, TYPE_VMSG_BUS_AMP_PCI)
+
+REG32(VERSION,  0x00)
+REG32(FEATURES, 0x04)
+REG32(NOTIFY,   0x20)
+
+#define MAX_FIFOS 8
+
+typedef struct VmsgBusAmpPciState {
+    VirtIOMSGBusDevice parent;
+    PCIDevice *pcidev;
+    unsigned int queue_index;
+
+    struct {
+        void *va;
+        spsc_queue driver;
+        spsc_queue device;
+        unsigned int mapcount;
+    } shm;
+} VmsgBusAmpPciState;
+
+typedef struct VmsgAmpPciState {
+    PCIDevice dev;
+    MemoryRegion mr_mmio;
+    MemoryRegion mr_ram;
+
+    struct fifo_bus {
+        VmsgBusAmpPciState dev;
+        VirtIOMSGProxy proxy;
+        BusState bus;
+    } fifo[MAX_FIFOS];
+
+    struct {
+        uint32_t num_fifos;
+    } cfg;
+} VmsgAmpPciState;
+
+static void vmsg_bus_amp_pci_process(VirtIOMSGBusDevice *bd);
+
+static uint64_t vmsg_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    uint64_t r = 0;
+
+    assert(size == 4);
+
+    switch (addr) {
+    case A_VERSION:
+        /* v0.1 */
+        r = 0x0001;
+        break;
+    case A_FEATURES:
+        /* No features bit yet.  */
+        break;
+    default:
+        break;
+    }
+
+    return r;
+}
+
+static void vmsg_write(void *opaque, hwaddr addr, uint64_t val,
+                       unsigned int size)
+{
+    VmsgAmpPciState *s = VMSG_AMP_PCI(opaque);
+    unsigned int q;
+
+    assert(size == 4);
+
+    if (addr >= A_NOTIFY) {
+        q = (addr - A_NOTIFY) / 4;
+        if (q >= s->cfg.num_fifos) {
+            /* Fifo doesn't exist.  */
+            return;
+        }
+
+        vmsg_bus_amp_pci_process(VIRTIO_MSG_BUS_DEVICE(&s->fifo[q].dev));
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: write to read-only reg 0x%" HWADDR_PRIx "\n",
+                      __func__, addr);
+    }
+}
+
+static const MemoryRegionOps vmsg_pci_ops = {
+    .read = vmsg_read,
+    .write = vmsg_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static void vmsg_create_bus(VmsgAmpPciState *s, unsigned int i)
+{
+    DeviceState *dev = DEVICE(s);
+    Object *o = OBJECT(s);
+    struct fifo_bus *fifo = &s->fifo[i];
+    g_autofree char *fifo_name = g_strdup_printf("fifo%d", i);
+
+    qbus_init(&fifo->bus, sizeof(fifo->bus), TYPE_VIRTIO_MSG_OUTER_BUS,
+              dev, fifo_name);
+
+    /* Create the proxy.  */
+    object_initialize_child(o, "proxy[*]", &fifo->proxy, TYPE_VIRTIO_MSG);
+    qdev_realize(DEVICE(&fifo->proxy), BUS(&fifo->bus), &error_fatal);
+
+    object_initialize_child(o, "vmsg[*]", &fifo->dev,
+                            TYPE_VMSG_BUS_AMP_PCI);
+    qdev_realize(DEVICE(&fifo->dev), &fifo->proxy.msg_bus, &error_fatal);
+
+    msix_vector_use(PCI_DEVICE(s), i);
+
+    /* Caches for quick lookup. */
+    fifo->dev.queue_index = i;
+    fifo->dev.pcidev = PCI_DEVICE(s);
+}
+
+static void vmsg_amp_pci_realizefn(PCIDevice *dev, Error **errp)
+{
+    VmsgAmpPciState *s = VMSG_AMP_PCI(dev);
+    int i;
+
+    if (!s->cfg.num_fifos || s->cfg.num_fifos > MAX_FIFOS) {
+        error_setg(errp, "Unsupported number of FIFOs (%u)", s->cfg.num_fifos);
+        return;
+    }
+
+    memory_region_init_io(&s->mr_mmio, OBJECT(s), &vmsg_pci_ops, s,
+                          TYPE_VMSG_AMP_PCI, 16 * KiB);
+
+    /* 16KB per FIFO.  */
+    memory_region_init_ram(&s->mr_ram, OBJECT(s), "ram",
+                           s->cfg.num_fifos * 16 * KiB, &error_fatal);
+
+    pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mr_mmio);
+    pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY |
+                             PCI_BASE_ADDRESS_MEM_PREFETCH,
+                             &s->mr_ram);
+
+    msix_init_exclusive_bar(PCI_DEVICE(s), s->cfg.num_fifos, 2, &error_fatal);
+    for (i = 0; i < s->cfg.num_fifos; i++) {
+        vmsg_create_bus(s, i);
+    }
+}
+
+static const Property vmsg_properties[] = {
+    DEFINE_PROP_UINT32("num-fifos", VmsgAmpPciState, cfg.num_fifos, 1),
+};
+
+static const VMStateDescription vmstate_vmsg_pci = {
+    .name = TYPE_VMSG_AMP_PCI,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (const VMStateField[]) {
+        VMSTATE_PCI_DEVICE(dev, VmsgAmpPciState),
+        /* TODO: Add all the sub-devs.  */
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void vmsg_amp_pci_class_init(ObjectClass *klass, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass);
+
+    device_class_set_props(dc, vmsg_properties);
+
+    pc->realize = vmsg_amp_pci_realizefn;
+    pc->vendor_id = PCI_VENDOR_ID_XILINX;
+    pc->device_id = 0x9039;
+    pc->revision = 1;
+    pc->class_id = PCI_CLASS_SYSTEM_OTHER;
+    dc->vmsd = &vmstate_vmsg_pci;
+
+    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+}
+
+static bool vmsg_bus_amp_pci_map_fifo(VmsgBusAmpPciState *s)
+{
+    VmsgAmpPciState *pci_s = VMSG_AMP_PCI(s->pcidev);
+    void *va;
+
+    if (s->shm.mapcount) {
+        s->shm.mapcount++;
+        return true;
+    }
+
+    va = memory_region_get_ram_ptr(&pci_s->mr_ram);
+    if (!va) {
+        return false;
+    }
+
+    if (!s->shm.driver.shm) {
+        int capacity = spsc_capacity(4 * KiB);
+
+        /*
+         * Layout:
+         * 0     - 4KB    Reserved
+         * 4KB   - 8KB    Driver queue
+         * 8KB   - 12KB   Device queue
+         */
+        spsc_init(&s->shm.driver, "driver", capacity, va + 4 * KiB);
+        spsc_init(&s->shm.device, "device", capacity, va + 8 * KiB);
+    }
+
+    /* Map queues.  */
+    s->shm.va = va;
+    s->shm.mapcount++;
+    return true;
+}
+
+static void vmsg_bus_amp_pci_unmap_fifo(VmsgBusAmpPciState *s)
+{
+    assert(s->shm.mapcount);
+    if (--s->shm.mapcount) {
+        return;
+    }
+
+    /* TODO: Actually unmap. */
+}
+
+static void vmsg_bus_amp_pci_process(VirtIOMSGBusDevice *bd)
+{
+    VmsgBusAmpPciState *s = VMSG_BUS_AMP_PCI(bd);
+    spsc_queue *q;
+    VirtIOMSG msg;
+    bool r;
+
+    if (!vmsg_bus_amp_pci_map_fifo(s)) {
+        return;
+    }
+
+    /*
+     * We process the opposite queue, i.e, a driver will want to receive
+     * messages on the backend queue (and send messages on the driver queue).
+     */
+    q = bd->peer->is_driver ? &s->shm.device : &s->shm.driver;
+    do {
+        r = spsc_recv(q, &msg, sizeof msg);
+        if (r) {
+            virtio_msg_bus_receive(bd, &msg);
+        }
+    } while (r);
+    vmsg_bus_amp_pci_unmap_fifo(s);
+}
+
+static int vmsg_bus_amp_pci_send(VirtIOMSGBusDevice *bd, VirtIOMSG *msg_req)
+{
+    VmsgAmpPciState *pci_s = VMSG_AMP_PCI(OBJECT(bd)->parent);
+    VmsgBusAmpPciState *s = VMSG_BUS_AMP_PCI(bd);
+
+    if (!vmsg_bus_amp_pci_map_fifo(s)) {
+        return VIRTIO_MSG_ERROR_MEMORY;
+    }
+
+    spsc_send(&s->shm.device, msg_req, sizeof *msg_req);
+
+    /* Notify.  */
+    msix_notify(PCI_DEVICE(pci_s), s->queue_index);
+
+    vmsg_bus_amp_pci_unmap_fifo(s);
+    return VIRTIO_MSG_NO_ERROR;
+}
+
+static void vmsg_bus_amp_pci_class_init(ObjectClass *klass,
+                                              const void *data)
+{
+    VirtIOMSGBusDeviceClass *bdc = VIRTIO_MSG_BUS_DEVICE_CLASS(klass);
+
+    bdc->process = vmsg_bus_amp_pci_process;
+    bdc->send = vmsg_bus_amp_pci_send;
+}
+
+static const TypeInfo vmsg_pci_info[] = {
+    {
+        .name = TYPE_VMSG_AMP_PCI,
+        .parent = TYPE_PCI_DEVICE,
+        .instance_size = sizeof(VmsgAmpPciState),
+        .class_init = vmsg_amp_pci_class_init,
+        .interfaces = (const InterfaceInfo[]) {
+            { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+            { }
+        },
+    }, {
+        .name = TYPE_VMSG_BUS_AMP_PCI,
+        .parent = TYPE_VIRTIO_MSG_BUS_DEVICE,
+        .instance_size = sizeof(VmsgBusAmpPciState),
+        .class_init = vmsg_bus_amp_pci_class_init,
+    },
+};
+
+static void vmsg_pci_register_types(void)
+{
+    type_register_static_array(vmsg_pci_info, ARRAY_SIZE(vmsg_pci_info));
+}
+
+type_init(vmsg_pci_register_types);
diff --git a/include/hw/virtio/spsc_queue.h b/include/hw/virtio/spsc_queue.h
new file mode 100644
index 0000000000..3d88baab55
--- /dev/null
+++ b/include/hw/virtio/spsc_queue.h
@@ -0,0 +1,213 @@
+/*
+ * Hardened and lockless Single Producer Single Consumer Queue implemented
+ * over shared-memory.
+ *
+ * The queue implementation does not look at packet contents, it's up to upper
+ * layers to make sure data is produced and parsed safely. All data is copied
+ * in/out from/to local private buffers so the peer cannot mess with them while
+ * upper layers parse.
+ *
+ * The queue is split into a private and a shared part.
+ * The private part contains cached and sanitized versions of the indexes that
+ * indicate our position in the ring-buffer. Peers can corrupt the shared area
+ * but have no access to the private area. So whenever we copy from the shared
+ * area into the private one, we need to sanitize indexes and make sure they
+ * are within bounds.
+ *
+ * A malicious peer can send corrupt data, it can stop receiving or flood the
+ * queue causing a sort of denial of service but it can NOT cause our side
+ * to copy data in or out of buffers outside of the shared memory area.
+ *
+ * This implementation expects the SHM area to be cache-coherent or uncached.
+ * The shared area can be mapped in different ways and our peer may be anything
+ * from another thread on our same OS to an FPGA implementation on a PCI card.
+ * So local CPU cache-lines sizes, or spin-locks and things that work on a
+ * single CPU cluster are not used. Instead the implementation sticks to atomic
+ * load/stores of 32b values and to using memory-barriers to guarantee ordering.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef SPSC_QUEUE_H__
+#define SPSC_QUEUE_H__
+
+#include <assert.h>
+#include "qemu/atomic.h"
+
+#define BUG_ON(x) assert(!(x))
+
+#define SPSC_QUEUE_MAX_PACKET_SIZE 64
+/*
+ * This cache-line size is used to align fields in the hope of
+ * avoiding cache-line ping-pong:ing. Since the queue layout is
+ * used across heterogenous CPU clusters and across FPGA/HW implementations,
+ * a fixed size must be used, i.e not the local CPU's cache-line size.
+ */
+#define SPSC_QUEUE_CACHE_LINE_SIZE 64
+
+typedef struct spsc_queue_shared {
+    uint32_t head __attribute__((__aligned__(SPSC_QUEUE_CACHE_LINE_SIZE)));
+    uint32_t tail __attribute__((__aligned__(SPSC_QUEUE_CACHE_LINE_SIZE)));
+    uint32_t packets[][SPSC_QUEUE_MAX_PACKET_SIZE / 4]
+        __attribute__((__aligned__(SPSC_QUEUE_CACHE_LINE_SIZE)));
+} spsc_queue_shared;
+
+typedef struct spsc_queue {
+    uint32_t cached_tail;
+    uint32_t cached_head;
+    spsc_queue_shared *shm;
+    const char *name;
+    unsigned int capacity;
+} spsc_queue;
+
+/* Atomically load and sanitize an index from the SHM area.  */
+static inline uint32_t spsc_atomic_load(spsc_queue *q, uint32_t *ptr)
+{
+    uint32_t val;
+
+    val = qatomic_read(ptr);
+    /* Make sure packet reads are done after reading the index.  */
+    smp_mb_acquire();
+
+    /* Bounds check that index is within queue size.  */
+    if (val >= q->capacity) {
+        val = val % q->capacity;
+    }
+
+    return val;
+}
+
+static inline void spsc_atomic_store(spsc_queue *q, uint32_t *ptr, uint32_t v)
+{
+    /* Make sure packet-data gets written before updating the index.  */
+    smp_mb_release();
+    qatomic_set(ptr, v);
+}
+
+/* Returns the capacity of a queue given a specific mapsize. */
+static inline unsigned int spsc_capacity(size_t mapsize)
+{
+    unsigned int capacity;
+    spsc_queue *q = NULL;
+
+    if (mapsize < sizeof(*q->shm)) {
+        return 0;
+    }
+
+    /* Start with the size of the shared area. */
+    mapsize -= sizeof(*q->shm);
+    capacity = mapsize / sizeof(q->shm->packets[0]);
+
+    if (capacity < 2) {
+        /* Capacities of less than 2 are invalid. */
+        return 0;
+    }
+
+    return capacity;
+}
+
+static inline size_t spsc_mapsize(unsigned int capacity)
+{
+    spsc_queue *q = NULL;
+    size_t mapsize;
+
+    BUG_ON(capacity < 2);
+
+    mapsize = sizeof(*q->shm);
+    mapsize += sizeof(q->shm->packets[0]) * capacity;
+
+    return mapsize;
+}
+
+static inline void spsc_init(spsc_queue *q, const char *name, size_t capacity,
+                             void *mem)
+{
+    BUG_ON(!mem);
+
+    /* Initialize private queue area to all zeroes */
+    memset(q, 0, sizeof *q);
+
+    q->shm = (spsc_queue_shared *) mem;
+    q->name = name;
+    q->capacity = capacity;
+
+    /* In case we're opening a pre-existing queue, pick up where we left off. */
+    q->cached_tail = spsc_atomic_load(q, &q->shm->tail);
+    q->cached_head = spsc_atomic_load(q, &q->shm->head);
+}
+
+static inline bool spsc_queue_is_full(spsc_queue *q)
+{
+    uint32_t next_head;
+    uint32_t head;
+
+    head = spsc_atomic_load(q, &q->shm->head);
+
+    next_head = head + 1;
+    if (next_head >= q->capacity) {
+        next_head = 0;
+    }
+
+    if (next_head == q->cached_tail) {
+        q->cached_tail = spsc_atomic_load(q, &q->shm->tail);
+        if (next_head == q->cached_tail) {
+            return true;
+        }
+    }
+    return false;
+}
+
+static inline bool spsc_send(spsc_queue *q, void *buf, size_t size)
+{
+    uint32_t next_head;
+    uint32_t head;
+
+    BUG_ON(size > sizeof q->shm->packets[0]);
+    BUG_ON(size == 0);
+
+    /* Is the queue full?  */
+    if (spsc_queue_is_full(q)) {
+        return false;
+    }
+
+    head = spsc_atomic_load(q, &q->shm->head);
+    next_head = head + 1;
+    if (next_head >= q->capacity) {
+        next_head = 0;
+    }
+
+    memcpy(q->shm->packets[head], buf, size);
+
+    spsc_atomic_store(q, &q->shm->head, next_head);
+    return true;
+}
+
+static inline bool spsc_recv(spsc_queue *q, void *buf, size_t size)
+{
+    uint32_t tail;
+
+    BUG_ON(size > sizeof q->shm->packets[0]);
+    BUG_ON(size == 0);
+
+    tail = spsc_atomic_load(q, &q->shm->tail);
+
+    /* Is the queue empty?  */
+    if (tail == q->cached_head) {
+        q->cached_head = spsc_atomic_load(q, &q->shm->head);
+        if (tail == q->cached_head) {
+            return false;
+        }
+    }
+
+    memcpy(buf, q->shm->packets[tail], size);
+
+    /* Update the read pointer.  */
+    tail++;
+    if (tail >= q->capacity) {
+        tail = 0;
+    }
+
+    spsc_atomic_store(q, &q->shm->tail, tail);
+    return true;
+}
+#endif /* SPSC_QUEUE_H__ */
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v1 5/5] docs: Describe virtio-msg-amp-pci
  2025-10-28 15:23 [PATCH v1 0/5] virtio: Add the virtio-msg transport Edgar E. Iglesias
                   ` (3 preceding siblings ...)
  2025-10-28 15:23 ` [PATCH v1 4/5] hw/misc: Add generic virtio-msg AMP PCI device Edgar E. Iglesias
@ 2025-10-28 15:23 ` Edgar E. Iglesias
  2025-11-05 19:52   ` Alex Bennée
  2025-10-28 15:30 ` [PATCH v1 0/5] virtio: Add the virtio-msg transport Edgar E. Iglesias
  5 siblings, 1 reply; 10+ messages in thread
From: Edgar E. Iglesias @ 2025-10-28 15:23 UTC (permalink / raw)
  To: qemu-devel; +Cc: mst, alex.bennee, bill.mills, edgar.iglesias

From: "Edgar E. Iglesias" <edgar.iglesias@amd.com>

Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com>
---
 .../devices/virtio/virtio-msg-amp-pci.rst     | 70 +++++++++++++++++++
 1 file changed, 70 insertions(+)
 create mode 100644 docs/system/devices/virtio/virtio-msg-amp-pci.rst

diff --git a/docs/system/devices/virtio/virtio-msg-amp-pci.rst b/docs/system/devices/virtio/virtio-msg-amp-pci.rst
new file mode 100644
index 0000000000..c73f7a878b
--- /dev/null
+++ b/docs/system/devices/virtio/virtio-msg-amp-pci.rst
@@ -0,0 +1,70 @@
+Virtio-msg AMP PCI
+==================
+
+This document explains the setup and usage of the virtio-msg-amp-pci device..
+The virtio-msg-amp-pci, is an emulated PCI device that provides a small
+set of features to enable virtio-msg over shared-memory queue's.
+
+Usecase
+-------
+
+Virtio-msg is a virtio transport where driver and device communicate over
+messages rather than using memory accesses that get trapped and emulated.
+Virtio-msg depends on a lower lever virtio-msg-bus responsible for delivering
+these messages. In this case, we're using the Virtio-msg AMP bus which moves
+messages back and forth using a FIFO on top of shared-memory and interrupts.
+
+The virtio-msg-amp-pci device exposes a BAR with RAM and doorbell registers
+so guests can implement the shared-memory FIFO protocol and QEMU implements
+the backend side of it.
+
+Virtio pmem allows to bypass the guest page cache and directly use
+host page cache. This reduces guest memory footprint as the host can
+make efficient memory reclaim decisions under memory pressure.
+
+Virtio-msg-amp-pci PCI device
+-----------------------------
+
+The virtio-msg-amp-pci device has the following layout:
+
+- BAR 0: Registers (Version, features and notification/doorbell regs)
+- BAR 1: RAM for FIFOs
+
+Each FIFO gets an MSI-X interrupt reserved for it and a dedicated doorbell
+register::
+
+        REG32(VERSION,  0x00)
+        REG32(FEATURES, 0x04)
+        REG32(NOTIFY0,  0x20)
+        REG32(NOTIFY1,  0x24)
+        REG32(NOTIFY2,  0x28)
+        And so on.
+
+How does virtio-msg-amp-pci compare to virtio-pci emulation?
+------------------------------------------------------------
+
+Both virtio-msg-amp-pci and virtio-pci emulate PCI devices and allow users
+to plug virtio devices behind them. The main difference is in how the
+guest uses virtio-msg vs virtio-pci to discover and configure the virtio dev.
+
+virtio pmem usage
+-----------------
+
+A virtio-msg-amp-pci can be greated by adding the following to the QEMU
+command-line::
+
+    -device virtio-msg-amp-pci
+
+Virtio devices can then be attached to the virtio-msg bus with for example
+the following::
+
+    -device virtio-rng-device,bus=/gpex-pcihost/pcie.0/virtio-msg-amp-pci/fifo0/virtio-msg/bus0/virtio-msg-dev
+
+Multiple virtio devices can be connected by using bus1, bus2 and so on.
+
+Device properties
+-----------------
+
+The virtio-msg-amp-pci  device can be configured with the following properties:
+
+ * ``num-fifos`` number of fifos (default 2).
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v1 0/5] virtio: Add the virtio-msg transport
  2025-10-28 15:23 [PATCH v1 0/5] virtio: Add the virtio-msg transport Edgar E. Iglesias
                   ` (4 preceding siblings ...)
  2025-10-28 15:23 ` [PATCH v1 5/5] docs: Describe virtio-msg-amp-pci Edgar E. Iglesias
@ 2025-10-28 15:30 ` Edgar E. Iglesias
  5 siblings, 0 replies; 10+ messages in thread
From: Edgar E. Iglesias @ 2025-10-28 15:30 UTC (permalink / raw)
  To: Edgar E. Iglesias; +Cc: qemu-devel, mst, alex.bennee, bill.mills

On Tue, Oct 28, 2025 at 04:23:45PM +0100, Edgar E. Iglesias wrote:
> From: "Edgar E. Iglesias" <edgar.iglesias@amd.com>
>
> This adds virtio-msg, a new virtio transport. Virtio-msg works by


This series was meant to be an RFC but I managed to send it out without the
RFC subject tags, sorry about that!

Cheers,
Edgar


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v1 1/5] virtio: Introduce notify_queue
  2025-10-28 15:23 ` [PATCH v1 1/5] virtio: Introduce notify_queue Edgar E. Iglesias
@ 2025-11-05 12:38   ` Alex Bennée
  0 siblings, 0 replies; 10+ messages in thread
From: Alex Bennée @ 2025-11-05 12:38 UTC (permalink / raw)
  To: Edgar E. Iglesias
  Cc: qemu-devel, Michael S. Tsirkin, bill.mills, edgar.iglesias

"Edgar E. Iglesias" <edgar.iglesias@gmail.com> writes:

> From: "Edgar E. Iglesias" <edgar.iglesias@amd.com>
>
> Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com>
> ---
>  hw/virtio/virtio.c             | 7 +++++++
>  include/hw/virtio/virtio-bus.h | 1 +
>  2 files changed, 8 insertions(+)
>
> diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
> index 153ee0a0cf..8a53fb5f93 100644
> --- a/hw/virtio/virtio.c
> +++ b/hw/virtio/virtio.c
> @@ -2700,12 +2700,19 @@ static void virtio_irq(VirtQueue *vq)
>  
>  void virtio_notify(VirtIODevice *vdev, VirtQueue *vq)
>  {
> +    BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
> +    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
> +
>      WITH_RCU_READ_LOCK_GUARD() {
>          if (!virtio_should_notify(vdev, vq)) {
>              return;
>          }
>      }
>  
> +    if (k->notify_queue) {
> +        k->notify_queue(qbus->parent, virtio_get_queue_index(vq));
> +    }
> +
>      trace_virtio_notify(vdev, vq);
>      virtio_irq(vq);
>  }
> diff --git a/include/hw/virtio/virtio-bus.h b/include/hw/virtio/virtio-bus.h
> index 7ab8c9dab0..043dbeb4cf 100644
> --- a/include/hw/virtio/virtio-bus.h
> +++ b/include/hw/virtio/virtio-bus.h
> @@ -39,6 +39,7 @@ DECLARE_OBJ_CHECKERS(VirtioBusState, VirtioBusClass,
>  struct VirtioBusClass {
>      /* This is what a VirtioBus must implement */
>      BusClass parent;
> +    void (*notify_queue)(DeviceState *d, uint16_t index);
>      void (*notify)(DeviceState *d, uint16_t vector);
>      void (*save_config)(DeviceState *d, QEMUFile *f);
>      void (*save_queue)(DeviceState *d, int n, QEMUFile *f);

The code looks fine but we could do with a little outline of why we need
this is the commit messages. Why do we have notify and notify_queue? Are
they mutually exclusive? 

Not specific to this patch but we should strive to document the
individual methods in each class to give a clearer idea of what they do.

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v1 2/5] virtio: Add virtio_queue_get_rings
  2025-10-28 15:23 ` [PATCH v1 2/5] virtio: Add virtio_queue_get_rings Edgar E. Iglesias
@ 2025-11-05 12:39   ` Alex Bennée
  0 siblings, 0 replies; 10+ messages in thread
From: Alex Bennée @ 2025-11-05 12:39 UTC (permalink / raw)
  To: Edgar E. Iglesias
  Cc: qemu-devel, Michael S. Tsirkin, bill.mills, edgar.iglesias

"Edgar E. Iglesias" <edgar.iglesias@gmail.com> writes:

> From: "Edgar E. Iglesias" <edgar.iglesias@amd.com>
>
> Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com>
> ---
>  hw/virtio/virtio.c         | 16 ++++++++++++++++
>  include/hw/virtio/virtio.h |  2 ++
>  2 files changed, 18 insertions(+)
>
> diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
> index 8a53fb5f93..fe7c635390 100644
> --- a/hw/virtio/virtio.c
> +++ b/hw/virtio/virtio.c
> @@ -2379,6 +2379,22 @@ void virtio_queue_set_rings(VirtIODevice *vdev, int n, hwaddr desc,
>      virtio_init_region_cache(vdev, n);
>  }
>  
> +void virtio_queue_get_rings(VirtIODevice *vdev, int n, hwaddr *desc,
> +                            hwaddr *avail, hwaddr *used)
> +{
> +    assert(vdev->vq[n].vring.num);
> +
> +    if (desc) {
> +        *desc = vdev->vq[n].vring.desc;
> +    }
> +    if (avail) {
> +        *avail = vdev->vq[n].vring.avail;
> +    }
> +    if (used) {
> +        *used = vdev->vq[n].vring.used;
> +    }
> +}
> +
>  void virtio_queue_set_num(VirtIODevice *vdev, int n, int num)
>  {
>      /* Don't allow guest to flip queue between existent and
> diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
> index d97529c3f1..8bceb115a3 100644
> --- a/include/hw/virtio/virtio.h
> +++ b/include/hw/virtio/virtio.h
> @@ -361,6 +361,8 @@ int virtio_queue_get_max_num(VirtIODevice *vdev, int n);
>  int virtio_get_num_queues(VirtIODevice *vdev);
>  void virtio_queue_set_rings(VirtIODevice *vdev, int n, hwaddr desc,
>                              hwaddr avail, hwaddr used);
> +void virtio_queue_get_rings(VirtIODevice *vdev, int n, hwaddr *desc,
> +                            hwaddr *avail, hwaddr *used);

Same as last comment, lets start documenting the API.

>  void virtio_queue_update_rings(VirtIODevice *vdev, int n);
>  void virtio_init_region_cache(VirtIODevice *vdev, int n);
>  void virtio_queue_set_align(VirtIODevice *vdev, int n, int align);

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v1 5/5] docs: Describe virtio-msg-amp-pci
  2025-10-28 15:23 ` [PATCH v1 5/5] docs: Describe virtio-msg-amp-pci Edgar E. Iglesias
@ 2025-11-05 19:52   ` Alex Bennée
  0 siblings, 0 replies; 10+ messages in thread
From: Alex Bennée @ 2025-11-05 19:52 UTC (permalink / raw)
  To: Edgar E. Iglesias; +Cc: qemu-devel, mst, bill.mills, edgar.iglesias

"Edgar E. Iglesias" <edgar.iglesias@gmail.com> writes:

> From: "Edgar E. Iglesias" <edgar.iglesias@amd.com>
>
> Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com>
> ---
>  .../devices/virtio/virtio-msg-amp-pci.rst     | 70
> +++++++++++++++++++

You also need to add this to index.rst otherwise the docs build will
fail.


>  1 file changed, 70 insertions(+)
>  create mode 100644 docs/system/devices/virtio/virtio-msg-amp-pci.rst
>
> diff --git a/docs/system/devices/virtio/virtio-msg-amp-pci.rst b/docs/system/devices/virtio/virtio-msg-amp-pci.rst
> new file mode 100644
> index 0000000000..c73f7a878b
> --- /dev/null
> +++ b/docs/system/devices/virtio/virtio-msg-amp-pci.rst
> @@ -0,0 +1,70 @@
> +Virtio-msg AMP PCI
> +==================
> +
> +This document explains the setup and usage of the virtio-msg-amp-pci device..
> +The virtio-msg-amp-pci, is an emulated PCI device that provides a small
> +set of features to enable virtio-msg over shared-memory queue's.
> +
> +Usecase
> +-------
> +
> +Virtio-msg is a virtio transport where driver and device communicate over
> +messages rather than using memory accesses that get trapped and emulated.
> +Virtio-msg depends on a lower lever virtio-msg-bus responsible for delivering
> +these messages. In this case, we're using the Virtio-msg AMP bus which moves
> +messages back and forth using a FIFO on top of shared-memory and interrupts.
> +
> +The virtio-msg-amp-pci device exposes a BAR with RAM and doorbell registers
> +so guests can implement the shared-memory FIFO protocol and QEMU implements
> +the backend side of it.
> +
> +Virtio pmem allows to bypass the guest page cache and directly use
> +host page cache. This reduces guest memory footprint as the host can
> +make efficient memory reclaim decisions under memory pressure.
> +
> +Virtio-msg-amp-pci PCI device
> +-----------------------------
> +
> +The virtio-msg-amp-pci device has the following layout:
> +
> +- BAR 0: Registers (Version, features and notification/doorbell regs)
> +- BAR 1: RAM for FIFOs
> +
> +Each FIFO gets an MSI-X interrupt reserved for it and a dedicated doorbell
> +register::
> +
> +        REG32(VERSION,  0x00)
> +        REG32(FEATURES, 0x04)
> +        REG32(NOTIFY0,  0x20)
> +        REG32(NOTIFY1,  0x24)
> +        REG32(NOTIFY2,  0x28)
> +        And so on.
> +
> +How does virtio-msg-amp-pci compare to virtio-pci emulation?
> +------------------------------------------------------------
> +
> +Both virtio-msg-amp-pci and virtio-pci emulate PCI devices and allow users
> +to plug virtio devices behind them. The main difference is in how the
> +guest uses virtio-msg vs virtio-pci to discover and configure the virtio dev.
> +
> +virtio pmem usage
> +-----------------
> +
> +A virtio-msg-amp-pci can be greated by adding the following to the QEMU
> +command-line::
> +
> +    -device virtio-msg-amp-pci
> +
> +Virtio devices can then be attached to the virtio-msg bus with for example
> +the following::
> +
> +    -device virtio-rng-device,bus=/gpex-pcihost/pcie.0/virtio-msg-amp-pci/fifo0/virtio-msg/bus0/virtio-msg-dev
> +
> +Multiple virtio devices can be connected by using bus1, bus2 and so on.
> +
> +Device properties
> +-----------------
> +
> +The virtio-msg-amp-pci  device can be configured with the following properties:
> +
> + * ``num-fifos`` number of fifos (default 2).

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2025-11-05 19:53 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-28 15:23 [PATCH v1 0/5] virtio: Add the virtio-msg transport Edgar E. Iglesias
2025-10-28 15:23 ` [PATCH v1 1/5] virtio: Introduce notify_queue Edgar E. Iglesias
2025-11-05 12:38   ` Alex Bennée
2025-10-28 15:23 ` [PATCH v1 2/5] virtio: Add virtio_queue_get_rings Edgar E. Iglesias
2025-11-05 12:39   ` Alex Bennée
2025-10-28 15:23 ` [PATCH v1 3/5] virtio: Add the virtio-msg transport Edgar E. Iglesias
2025-10-28 15:23 ` [PATCH v1 4/5] hw/misc: Add generic virtio-msg AMP PCI device Edgar E. Iglesias
2025-10-28 15:23 ` [PATCH v1 5/5] docs: Describe virtio-msg-amp-pci Edgar E. Iglesias
2025-11-05 19:52   ` Alex Bennée
2025-10-28 15:30 ` [PATCH v1 0/5] virtio: Add the virtio-msg transport Edgar E. Iglesias

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).