On Fri, Jul 25, 2025 at 03:02:14PM +0530, Viresh Kumar wrote:
This introduces support for a new Virtio transport type: "virtio-msg". Unlike existing transport types like virtio-mmio or virtio-pci which rely on memory-mapped registers, virtio-msg implements transport operations via structured message exchanges using standard virtqueues.
It separates bus-level functionality (e.g., device enumeration, hotplug events) from device-specific operations (e.g., feature negotiation, virtqueue setup), ensuring that a single, generic transport layer can be reused across multiple bus implementations (like ARM Firmware Framework (FF-A), IPC, etc.).
Signed-off-by: Viresh Kumar viresh.kumar@linaro.org
Hi Viresh,
Found an issue today when testing with our most recent virtio-msg QEMU backend. Looks like the driver is setting the wrong length for set_features, see inline.
I'm using something based on your virtio/msg-v2-xen + our AMP/PCIe bus drivers.
MAINTAINERS | 7 + drivers/virtio/Kconfig | 7 + drivers/virtio/Makefile | 1 + drivers/virtio/virtio_msg.c | 611 ++++++++++++++++++++++++++++++++ drivers/virtio/virtio_msg.h | 56 +++ include/uapi/linux/virtio_msg.h | 221 ++++++++++++ 6 files changed, 903 insertions(+) create mode 100644 drivers/virtio/virtio_msg.c create mode 100644 drivers/virtio/virtio_msg.h create mode 100644 include/uapi/linux/virtio_msg.h
diff --git a/MAINTAINERS b/MAINTAINERS index 60bba48f5479..6fc644e405e6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -26355,6 +26355,13 @@ W: https://virtio-mem.gitlab.io/ F: drivers/virtio/virtio_mem.c F: include/uapi/linux/virtio_mem.h +VIRTIO MSG TRANSPORT +M: Viresh Kumar viresh.kumar@linaro.org +L: virtualization@lists.linux.dev +S: Maintained +F: drivers/virtio/virtio_msg* +F: include/uapi/linux/virtio_msg*
VIRTIO PMEM DRIVER M: Pankaj Gupta pankaj.gupta.linux@gmail.com L: virtualization@lists.linux.dev diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index 6db5235a7693..690ac98850b6 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -171,6 +171,13 @@ config VIRTIO_MMIO_CMDLINE_DEVICES If unsure, say 'N'. +config VIRTIO_MSG
- tristate
- select VIRTIO
- help
This enables support for Virtio message transport. This option is
selected by any driver which implements the virtio message bus.
config VIRTIO_DMA_SHARED_BUFFER tristate depends on DMA_SHARED_BUFFER diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile index eefcfe90d6b8..3eff8ca72446 100644 --- a/drivers/virtio/Makefile +++ b/drivers/virtio/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_VIRTIO_ANCHOR) += virtio_anchor.o obj-$(CONFIG_VIRTIO_PCI_LIB) += virtio_pci_modern_dev.o obj-$(CONFIG_VIRTIO_PCI_LIB_LEGACY) += virtio_pci_legacy_dev.o obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o +obj-$(CONFIG_VIRTIO_MSG) += virtio_msg.o obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o virtio_pci-y := virtio_pci_modern.o virtio_pci_common.o virtio_pci-$(CONFIG_VIRTIO_PCI_LEGACY) += virtio_pci_legacy.o diff --git a/drivers/virtio/virtio_msg.c b/drivers/virtio/virtio_msg.c new file mode 100644 index 000000000000..2de1f1ff33b8 --- /dev/null +++ b/drivers/virtio/virtio_msg.c @@ -0,0 +1,611 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Virtio message transport.
- Copyright (C) 2025 Google LLC and Linaro.
- Viresh Kumar viresh.kumar@linaro.org
- The virtio-msg transport encapsulates virtio operations as discrete message
- exchanges rather than relying on PCI or memory-mapped I/O regions. It
- separates bus-level functionality (e.g., device enumeration, hotplug events)
- from device-specific operations (e.g., feature negotiation, virtqueue setup),
- ensuring that a single, generic transport layer can be reused across multiple
- bus implementations (like ARM Firmware Framework (FF-A), IPC, etc.).
- This file implements the generic Virtio message transport layer.
- */
+#define pr_fmt(fmt) "virtio-msg: " fmt
+#include <linux/err.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/virtio.h> +#include <linux/virtio_config.h> +#include <linux/virtio_ring.h> +#include <uapi/linux/virtio_msg.h>
+#include "virtio_msg.h"
+#define to_virtio_msg_device(_dev) \
- container_of(_dev, struct virtio_msg_device, vdev)
+static void msg_prepare(struct virtio_msg *vmsg, bool bus, u8 msg_id,
u16 dev_id, u16 payload_size)
+{
- u16 size = sizeof(*vmsg) + payload_size;
- memset(vmsg, 0, size);
- if (bus) {
vmsg->type = VIRTIO_MSG_TYPE_BUS;
- } else {
vmsg->type = VIRTIO_MSG_TYPE_TRANSPORT;
vmsg->dev_id = cpu_to_le16(dev_id);
- }
- vmsg->msg_id = msg_id;
- vmsg->msg_size = cpu_to_le16(size);
+}
+static void transport_msg_prepare(struct virtio_msg_device *vmdev, u8 msg_id,
u16 payload_size)
+{
- msg_prepare(vmdev->request, false, msg_id, vmdev->dev_id, payload_size);
+}
+void virtio_msg_prepare(struct virtio_msg *vmsg, u8 msg_id, u16 payload_size) +{
- msg_prepare(vmsg, true, msg_id, 0, payload_size);
+} +EXPORT_SYMBOL_GPL(virtio_msg_prepare);
+static int virtio_msg_xfer(struct virtio_msg_device *vmdev) +{
- int ret;
- memset(vmdev->response, 0, vmdev->msg_size);
- ret = vmdev->ops->transfer(vmdev, vmdev->request, vmdev->response);
- if (ret)
dev_err(&vmdev->vdev.dev, "Transfer request failed (%d)\n", ret);
- return ret;
+}
+static inline int virtio_msg_send(struct virtio_msg_device *vmdev) +{
- int ret;
- ret = vmdev->ops->transfer(vmdev, vmdev->request, NULL);
- if (ret)
dev_err(&vmdev->vdev.dev, "Send request failed (%d)\n", ret);
- return ret;
+}
+static int virtio_msg_get_device_info(struct virtio_msg_device *vmdev) +{
- struct get_device_info_resp *payload = virtio_msg_payload(vmdev->response);
- struct virtio_device *vdev = &vmdev->vdev;
- u32 num_feature_bits;
- int ret;
- static_assert(sizeof(*vmdev->response) + sizeof(*payload) <
VIRTIO_MSG_MIN_SIZE);
- transport_msg_prepare(vmdev, VIRTIO_MSG_DEVICE_INFO, 0);
- ret = virtio_msg_xfer(vmdev);
- if (ret)
return ret;
- vdev->id.device = le32_to_cpu(payload->device_id);
- if (vdev->id.device == 0) {
/*
* virtio device with an ID 0 is a (dummy) placeholder with no
* function.
*/
return -ENODEV;
- }
- vdev->id.vendor = le32_to_cpu(payload->vendor_id);
- vmdev->config_size = le32_to_cpu(payload->config_size);
- num_feature_bits = le32_to_cpu(payload->num_feature_bits);
- /* Linux supports 64 feature bits */
- if (num_feature_bits != 64) {
dev_err(&vdev->dev, "Incompatible num_feature_bits (%u)\n",
num_feature_bits);
return -EINVAL;
- }
- return 0;
+}
+static u64 virtio_msg_get_features(struct virtio_device *vdev) +{
- struct virtio_msg_device *vmdev = to_virtio_msg_device(vdev);
- struct get_features *req_payload = virtio_msg_payload(vmdev->request);
- struct get_features_resp *res_payload = virtio_msg_payload(vmdev->response);
- __le32 *features;
- int ret;
- static_assert(sizeof(*vmdev->request) + sizeof(*req_payload) <
VIRTIO_MSG_MIN_SIZE);
- static_assert(sizeof(*vmdev->response) + sizeof(*res_payload) <
VIRTIO_MSG_MIN_SIZE);
- transport_msg_prepare(vmdev, VIRTIO_MSG_GET_DEV_FEATURES,
sizeof(*req_payload));
- /* Linux supports 64 feature bits */
- req_payload->num = cpu_to_le32(2);
- req_payload->index = 0;
- ret = virtio_msg_xfer(vmdev);
- if (ret)
return 0;
- features = (__le32 *)res_payload->features;
- return ((u64)(le32_to_cpu(features[1])) << 32) | le32_to_cpu(features[0]);
+}
+static int virtio_msg_finalize_features(struct virtio_device *vdev) +{
- struct virtio_msg_device *vmdev = to_virtio_msg_device(vdev);
- struct set_features *payload = virtio_msg_payload(vmdev->request);
- __le32 *features = (__le32 *)payload->features;
- static_assert(sizeof(*vmdev->request) + sizeof(*payload) <
VIRTIO_MSG_MIN_SIZE);
- /* Give virtio_ring a chance to accept features */
- vring_transport_features(vdev);
- transport_msg_prepare(vmdev, VIRTIO_MSG_SET_DRV_FEATURES, sizeof(*payload));
I think the length here should be sizeof(*payload) + 2 * 4, shouldn't it?
We may have the same issue with set_config...