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 --- MAINTAINERS | 7 + drivers/virtio/Kconfig | 7 + drivers/virtio/Makefile | 1 + drivers/virtio/virtio_msg.c | 546 ++++++++++++++++++++++++++++++++ drivers/virtio/virtio_msg.h | 56 ++++ include/uapi/linux/virtio_msg.h | 221 +++++++++++++ 6 files changed, 838 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..207fa1f18bf9 --- /dev/null +++ b/drivers/virtio/virtio_msg.c @@ -0,0 +1,546 @@ +// 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; + + 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; + + 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; + + /* Give virtio_ring a chance to accept features */ + vring_transport_features(vdev); + + transport_msg_prepare(vmdev, VIRTIO_MSG_SET_DRV_FEATURES, sizeof(*payload)); + + /* Linux supports 64 feature bits */ + payload->num = cpu_to_le32(2); + payload->index = 0; + + features[0] = cpu_to_le32((u32)vmdev->vdev.features); + features[1] = cpu_to_le32(vmdev->vdev.features >> 32); + + return virtio_msg_xfer(vmdev); +} + +static void virtio_msg_get(struct virtio_device *vdev, unsigned int offset, + void *buf, unsigned int len) +{ + struct virtio_msg_device *vmdev = to_virtio_msg_device(vdev); + struct get_config *req_payload = virtio_msg_payload(vmdev->request); + struct get_config_resp *res_payload = virtio_msg_payload(vmdev->response); + + BUG_ON(len > 8); + + if (offset + len > vmdev->config_size) { + dev_err(&vmdev->vdev.dev, + "Invalid config read operation: %u: %u: %u\n", offset, + len, vmdev->config_size); + return; + } + + transport_msg_prepare(vmdev, VIRTIO_MSG_GET_CONFIG, sizeof(*req_payload)); + req_payload->offset = cpu_to_le32(offset); + req_payload->size = cpu_to_le32(len); + + if (virtio_msg_xfer(vmdev)) + return; + + /* Buffer holds the data in little endian */ + if (buf) + memcpy(buf, res_payload->config, len); + vmdev->generation_count = le32_to_cpu(res_payload->generation); +} + +static void virtio_msg_set(struct virtio_device *vdev, unsigned int offset, + const void *buf, unsigned int len) +{ + struct virtio_msg_device *vmdev = to_virtio_msg_device(vdev); + struct set_config *payload = virtio_msg_payload(vmdev->request); + + BUG_ON(len > 8); + + if (offset + len > vmdev->config_size) { + dev_err(&vmdev->vdev.dev, + "Invalid config write operation: %u: %u: %u\n", offset, + len, vmdev->config_size); + return; + } + + transport_msg_prepare(vmdev, VIRTIO_MSG_SET_CONFIG, sizeof(*payload)); + payload->offset = cpu_to_le32(offset); + payload->size = cpu_to_le32(len); + payload->generation = cpu_to_le32(vmdev->generation_count); + + /* Buffer holds the data in little endian */ + memcpy(payload->config, buf, len); + + virtio_msg_xfer(vmdev); +} + +static u32 virtio_msg_generation(struct virtio_device *vdev) +{ + struct virtio_msg_device *vmdev = to_virtio_msg_device(vdev); + + virtio_msg_get(vdev, 0, NULL, 0); + return vmdev->generation_count; +} + +static u8 virtio_msg_get_status(struct virtio_device *vdev) +{ + struct virtio_msg_device *vmdev = to_virtio_msg_device(vdev); + struct get_device_status_resp *payload = virtio_msg_payload(vmdev->response); + + transport_msg_prepare(vmdev, VIRTIO_MSG_GET_DEVICE_STATUS, 0); + + if (virtio_msg_xfer(vmdev)) + return 0; + + return (u8)le32_to_cpu(payload->status); +} + +static void virtio_msg_set_status(struct virtio_device *vdev, u8 status) +{ + struct virtio_msg_device *vmdev = to_virtio_msg_device(vdev); + struct set_device_status *payload = virtio_msg_payload(vmdev->request); + + transport_msg_prepare(vmdev, VIRTIO_MSG_SET_DEVICE_STATUS, sizeof(*payload)); + payload->status = cpu_to_le32(status); + + virtio_msg_xfer(vmdev); +} + +static void virtio_msg_vq_reset(struct virtqueue *vq) +{ + struct virtio_msg_device *vmdev = to_virtio_msg_device(vq->vdev); + struct reset_vqueue *payload = virtio_msg_payload(vmdev->request); + + transport_msg_prepare(vmdev, VIRTIO_MSG_RESET_VQUEUE, sizeof(*payload)); + payload->index = cpu_to_le32(vq->index); + + virtio_msg_xfer(vmdev); +} + +static void virtio_msg_reset(struct virtio_device *vdev) +{ + /* Status value `0` means a reset */ + virtio_msg_set_status(vdev, 0); +} + +static bool _vmsg_notify(struct virtqueue *vq, u32 index, u32 offset, u32 wrap) +{ + struct virtio_msg_device *vmdev = to_virtio_msg_device(vq->vdev); + struct event_avail *payload = virtio_msg_payload(vmdev->request); + u32 val; + + transport_msg_prepare(vmdev, VIRTIO_MSG_EVENT_AVAIL, sizeof(*payload)); + payload->index = cpu_to_le32(index); + + val = offset & ((1U << VIRTIO_MSG_EVENT_AVAIL_WRAP_SHIFT) - 1); + val |= wrap & (1U << VIRTIO_MSG_EVENT_AVAIL_WRAP_SHIFT); + payload->next_offset_wrap = cpu_to_le32(val); + + return !virtio_msg_send(vmdev); +} + +static bool virtio_msg_notify(struct virtqueue *vq) +{ + return _vmsg_notify(vq, vq->index, 0, 0); +} + +static bool virtio_msg_notify_with_data(struct virtqueue *vq) +{ + u32 index, offset, wrap, data = vring_notification_data(vq); + + index = data | 0xFFFF; + data >>= 16; + offset = data | 0x7FFF; + wrap = data >> 15; + + return _vmsg_notify(vq, index, offset, wrap); +} + +int virtio_msg_event(struct virtio_msg_device *vmdev, struct virtio_msg *vmsg) +{ + struct event_used *payload = virtio_msg_payload(vmsg); + struct device *dev = &vmdev->vdev.dev; + struct virtqueue *vq; + unsigned int index; + + if (vmsg->msg_id == VIRTIO_MSG_EVENT_CONFIG) { + virtio_config_changed(&vmdev->vdev); + return 0; + } + + if (vmsg->msg_id == VIRTIO_MSG_EVENT_USED) { + index = le32_to_cpu(payload->index); + + virtio_device_for_each_vq(&vmdev->vdev, vq) { + if (index == vq->index) { + if (vring_interrupt(0, vq) != IRQ_HANDLED) + return -EIO; + + return 0; + } + } + + dev_err(dev, "Failed to find virtqueue (%u)", index); + } else { + dev_err(dev, "Unexpected message id: (%u)\n", vmsg->msg_id); + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(virtio_msg_event); + +static void virtio_msg_del_vqs(struct virtio_device *vdev) +{ + struct virtqueue *vq, *n; + + list_for_each_entry_safe(vq, n, &vdev->vqs, list) { + virtio_msg_vq_reset(vq); + vring_del_virtqueue(vq); + } +} + +static int virtio_msg_vq_get(struct virtio_msg_device *vmdev, unsigned int *num, + unsigned int index) +{ + struct get_vqueue *req_payload = virtio_msg_payload(vmdev->request); + struct get_vqueue_resp *res_payload = virtio_msg_payload(vmdev->response); + int ret; + + transport_msg_prepare(vmdev, VIRTIO_MSG_GET_VQUEUE, sizeof(*req_payload)); + req_payload->index = cpu_to_le32(index); + + ret = virtio_msg_xfer(vmdev); + if (ret) + return ret; + + *num = le32_to_cpu(res_payload->max_size); + if (!*num) + return -ENOENT; + + return 0; +} + +static int virtio_msg_vq_set(struct virtio_msg_device *vmdev, + struct virtqueue *vq, unsigned int index) +{ + struct set_vqueue *payload = virtio_msg_payload(vmdev->request); + + transport_msg_prepare(vmdev, VIRTIO_MSG_SET_VQUEUE, sizeof(*payload)); + payload->index = cpu_to_le32(index); + payload->size = cpu_to_le32(virtqueue_get_vring_size(vq)); + payload->descriptor_addr = cpu_to_le64(virtqueue_get_desc_addr(vq)); + payload->driver_addr = cpu_to_le64(virtqueue_get_avail_addr(vq)); + payload->device_addr = cpu_to_le64(virtqueue_get_used_addr(vq)); + + return virtio_msg_xfer(vmdev); +} + +static struct virtqueue * +virtio_msg_setup_vq(struct virtio_msg_device *vmdev, unsigned int index, + void (*callback)(struct virtqueue *vq), const char *name, + bool ctx) +{ + bool (*notify)(struct virtqueue *vq); + struct virtqueue *vq; + unsigned int num; + int ret; + + if (__virtio_test_bit(&vmdev->vdev, VIRTIO_F_NOTIFICATION_DATA)) + notify = virtio_msg_notify_with_data; + else + notify = virtio_msg_notify; + + ret = virtio_msg_vq_get(vmdev, &num, index); + if (ret) + return ERR_PTR(ret); + + vq = vring_create_virtqueue(index, num, PAGE_SIZE, &vmdev->vdev, true, + true, ctx, notify, callback, name); + if (!vq) + return ERR_PTR(-ENOMEM); + + vq->num_max = num; + + ret = virtio_msg_vq_set(vmdev, vq, index); + if (ret) { + vring_del_virtqueue(vq); + return ERR_PTR(ret); + } + + return vq; +} + +static int virtio_msg_find_vqs(struct virtio_device *vdev, unsigned int nvqs, + struct virtqueue *vqs[], + struct virtqueue_info vqs_info[], + struct irq_affinity *desc) +{ + struct virtio_msg_device *vmdev = to_virtio_msg_device(vdev); + int i, queue_idx = 0; + + for (i = 0; i < nvqs; ++i) { + struct virtqueue_info *vqi = &vqs_info[i]; + + if (!vqi->name) { + vqs[i] = NULL; + continue; + } + + vqs[i] = virtio_msg_setup_vq(vmdev, queue_idx++, vqi->callback, + vqi->name, vqi->ctx); + if (IS_ERR(vqs[i])) { + virtio_msg_del_vqs(vdev); + return PTR_ERR(vqs[i]); + } + } + + return 0; +} + +static const char *virtio_msg_bus_name(struct virtio_device *vdev) +{ + struct virtio_msg_device *vmdev = to_virtio_msg_device(vdev); + + return vmdev->bus_name; +} + +static void virtio_msg_synchronize_cbs(struct virtio_device *vdev) +{ + struct virtio_msg_device *vmdev = to_virtio_msg_device(vdev); + + vmdev->ops->synchronize_cbs(vmdev); +} + +static void virtio_msg_release_dev(struct device *_d) +{ + struct virtio_device *vdev = + container_of(_d, struct virtio_device, dev); + struct virtio_msg_device *vmdev = to_virtio_msg_device(vdev); + + if (vmdev->ops->release) + vmdev->ops->release(vmdev); +} + +static struct virtio_config_ops virtio_msg_config_ops = { + .get = virtio_msg_get, + .set = virtio_msg_set, + .generation = virtio_msg_generation, + .get_status = virtio_msg_get_status, + .set_status = virtio_msg_set_status, + .reset = virtio_msg_reset, + .find_vqs = virtio_msg_find_vqs, + .del_vqs = virtio_msg_del_vqs, + .get_features = virtio_msg_get_features, + .finalize_features = virtio_msg_finalize_features, + .bus_name = virtio_msg_bus_name, +}; + +int virtio_msg_register(struct virtio_msg_device *vmdev) +{ + u32 version; + int ret; + + if (!vmdev || !vmdev->ops || !vmdev->ops->bus_info || + !vmdev->ops->transfer) + return -EINVAL; + + vmdev->bus_name = vmdev->ops->bus_info(vmdev, &vmdev->msg_size, + &version); + if (version != VIRTIO_MSG_REVISION_1 || + vmdev->msg_size < VIRTIO_MSG_MIN_SIZE) + return -EINVAL; + + /* + * Allocate request/response buffers of `msg_size`. + * + * The requests are sent sequentially for each device and hence a + * per-device copy of request/response buffers is sufficient. + */ + vmdev->request = kzalloc(2 * vmdev->msg_size, GFP_KERNEL); + if (!vmdev->request) + return -ENOMEM; + + vmdev->response = (void *)vmdev->request + vmdev->msg_size; + + vmdev->vdev.config = &virtio_msg_config_ops; + vmdev->vdev.dev.release = virtio_msg_release_dev; + + if (vmdev->ops->synchronize_cbs) + virtio_msg_config_ops.synchronize_cbs = virtio_msg_synchronize_cbs; + + ret = virtio_msg_get_device_info(vmdev); + if (ret) { + if (vmdev->ops->release) + vmdev->ops->release(vmdev); + goto free; + } + + ret = register_virtio_device(&vmdev->vdev); + if (ret) { + put_device(&vmdev->vdev.dev); + goto free; + } + + return 0; + +free: + kfree(vmdev->request); + return ret; +} +EXPORT_SYMBOL_GPL(virtio_msg_register); + +void virtio_msg_unregister(struct virtio_msg_device *vmdev) +{ + unregister_virtio_device(&vmdev->vdev); + kfree(vmdev->request); +} +EXPORT_SYMBOL_GPL(virtio_msg_unregister); + +MODULE_AUTHOR("Viresh Kumar viresh.kumar@linaro.org"); +MODULE_DESCRIPTION("Virtio message transport"); +MODULE_LICENSE("GPL"); diff --git a/drivers/virtio/virtio_msg.h b/drivers/virtio/virtio_msg.h new file mode 100644 index 000000000000..099bb2f0f679 --- /dev/null +++ b/drivers/virtio/virtio_msg.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ +/* + * Virtio message transport header. + * + * Copyright (C) 2025 Google LLC and Linaro. + * Viresh Kumar viresh.kumar@linaro.org + */ + +#ifndef _DRIVERS_VIRTIO_VIRTIO_MSG_H +#define _DRIVERS_VIRTIO_VIRTIO_MSG_H + +#include <linux/virtio.h> +#include <uapi/linux/virtio_msg.h> + +struct virtio_msg_device; + +/* + * struct virtio_msg_ops - Virtio message bus operations. + * @bus_info: Return bus information. + * @transfer: Transfer a message. + * @synchronize_cbs: Synchronize with the virtqueue callbacks (optional). + * @release: Release the resources corresponding to the device (optional). + */ +struct virtio_msg_ops { + const char *(*bus_info)(struct virtio_msg_device *vmdev, u16 *msg_size, u32 *rev); + int (*transfer)(struct virtio_msg_device *vmdev, struct virtio_msg *request, + struct virtio_msg *response); + void (*synchronize_cbs)(struct virtio_msg_device *vmdev); + void (*release)(struct virtio_msg_device *vmdev); +}; + +/* + * Representation of a device using virtio message + * transport. + */ +struct virtio_msg_device { + struct virtio_device vdev; + struct virtio_msg_ops *ops; + const char *bus_name; + void *bus_data; + u32 generation_count; + u32 config_size; + u16 msg_size; + u16 dev_id; + + struct virtio_msg *request; + struct virtio_msg *response; +}; + +int virtio_msg_register(struct virtio_msg_device *vmdev); +void virtio_msg_unregister(struct virtio_msg_device *vmdev); + +void virtio_msg_prepare(struct virtio_msg *vmsg, u8 msg_id, u16 payload_size); +int virtio_msg_event(struct virtio_msg_device *vmdev, struct virtio_msg *vmsg); + +#endif /* _DRIVERS_VIRTIO_VIRTIO_MSG_H */ diff --git a/include/uapi/linux/virtio_msg.h b/include/uapi/linux/virtio_msg.h new file mode 100644 index 000000000000..823bfbd6ecf1 --- /dev/null +++ b/include/uapi/linux/virtio_msg.h @@ -0,0 +1,221 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ +/* + * Virtio message transport header. + * + * Copyright (c) 2025 Advanced Micro Devices, Inc. + * Written by Edgar E. Iglesias edgar.iglesias@amd.com + * + * Copyright (C) 2025 Google LLC and Linaro. + * Viresh Kumar viresh.kumar@linaro.org + */ + +#ifndef _LINUX_VIRTIO_MSG_H +#define _LINUX_VIRTIO_MSG_H + +#include <linux/types.h> + +/* Virtio message transport definitions */ + +/* Message types */ +#define VIRTIO_MSG_DEVICE_INFO 0x02 +#define VIRTIO_MSG_GET_DEV_FEATURES 0x03 +#define VIRTIO_MSG_SET_DRV_FEATURES 0x04 +#define VIRTIO_MSG_GET_CONFIG 0x05 +#define VIRTIO_MSG_SET_CONFIG 0x06 +#define VIRTIO_MSG_GET_DEVICE_STATUS 0x07 +#define VIRTIO_MSG_SET_DEVICE_STATUS 0x08 +#define VIRTIO_MSG_GET_VQUEUE 0x09 +#define VIRTIO_MSG_SET_VQUEUE 0x0a +#define VIRTIO_MSG_RESET_VQUEUE 0x0b +#define VIRTIO_MSG_GET_SHM 0x0c +#define VIRTIO_MSG_EVENT_CONFIG 0x40 +#define VIRTIO_MSG_EVENT_AVAIL 0x41 +#define VIRTIO_MSG_EVENT_USED 0x42 +#define VIRTIO_MSG_MAX VIRTIO_MSG_EVENT_USED + +#define VIRTIO_MSG_MIN_SIZE 44 +#define VIRTIO_MSG_MAX_SIZE 65536 +#define VIRTIO_MSG_REVISION_1 0x1 + +/* Message payload format */ + +struct get_device_info_resp { + __le32 device_id; + __le32 vendor_id; + __le32 num_feature_bits; + __le32 config_size; + __le32 max_vq_count; + __le16 admin_vq_start_idx; + __le16 admin_vq_count; +} __attribute__((packed)); + +struct get_features { + __le32 index; + __le32 num; +} __attribute__((packed)); + +struct get_features_resp { + __le32 index; + __le32 num; + __u8 features[]; +} __attribute__((packed)); + +struct set_features { + __le32 index; + __le32 num; + __u8 features[]; +} __attribute__((packed)); + +struct get_config { + __le32 offset; + __le32 size; +} __attribute__((packed)); + +struct get_config_resp { + __le32 generation; + __le32 offset; + __le32 size; + __u8 config[]; +} __attribute__((packed)); + +struct set_config { + __le32 generation; + __le32 offset; + __le32 size; + __u8 config[]; +} __attribute__((packed)); + +struct set_config_resp { + __le32 generation; + __le32 offset; + __le32 size; + __u8 config[]; +} __attribute__((packed)); + +struct get_device_status_resp { + __le32 status; +} __attribute__((packed)); + +struct set_device_status { + __le32 status; +} __attribute__((packed)); + +struct set_device_status_resp { + __le32 status; +} __attribute__((packed)); + +struct get_vqueue { + __le32 index; +} __attribute__((packed)); + +struct get_vqueue_resp { + __le32 index; + __le32 max_size; + __le32 size; + __le64 descriptor_addr; + __le64 driver_addr; + __le64 device_addr; +} __attribute__((packed)); + +struct set_vqueue { + __le32 index; + __le32 unused; + __le32 size; + __le64 descriptor_addr; + __le64 driver_addr; + __le64 device_addr; +} __attribute__((packed)); + +struct reset_vqueue { + __le32 index; +} __attribute__((packed)); + +struct get_shm { + __le32 index; +} __attribute__((packed)); + +struct get_shm_resp { + __le32 index; + __le32 count; + __le32 addr; +} __attribute__((packed)); + +struct event_config { + __le32 status; + __le32 generation; + __le32 offset; + __le32 size; + __u8 config[]; +} __attribute__((packed)); + +struct event_avail { + __le32 index; + #define VIRTIO_MSG_EVENT_AVAIL_WRAP_SHIFT 31 + __le32 next_offset_wrap; +} __attribute__((packed)); + +struct event_used { + __le32 index; +} __attribute__((packed)); + +struct virtio_msg { + #define VIRTIO_MSG_TYPE_REQUEST (0 << 0) + #define VIRTIO_MSG_TYPE_RESPONSE (1 << 0) + #define VIRTIO_MSG_TYPE_TRANSPORT (0 << 1) + #define VIRTIO_MSG_TYPE_BUS (1 << 1) + __u8 type; + + __u8 msg_id; + __le16 dev_id; + __le16 msg_size; + __u8 payload[]; +} __attribute__((packed)); + +static inline void *virtio_msg_payload(struct virtio_msg *vmsg) +{ + return &vmsg->payload; +} + +/* Virtio message bus definitions */ + +/* Message types */ +#define VIRTIO_MSG_BUS_GET_DEVICES 0x02 +#define VIRTIO_MSG_BUS_PING 0x03 +#define VIRTIO_MSG_BUS_EVENT_DEVICE 0x40 + +struct bus_get_devices { + __le16 offset; + __le16 num; +} __attribute__((packed)); + +struct bus_get_devices_resp { + __le16 offset; + __le16 num; + __le16 next_offset; + __u8 devices[]; +} __attribute__((packed)); + +struct bus_event_device { + __le16 dev_num; + #define VIRTIO_MSG_BUS_EVENT_DEV_STATE_READY 0x1 + #define VIRTIO_MSG_BUS_EVENT_DEV_STATE_REMOVED 0x2 + __le16 dev_state; +} __attribute__((packed)); + +struct bus_ping { + __le32 data; +} __attribute__((packed)); + +struct bus_ping_resp { + __le32 data; +} __attribute__((packed)); + +struct bus_status { + #define VIRTIO_BUS_STATE_RESET 0x0 + #define VIRTIO_BUS_STATE_SHUTDOWN 0x1 + #define VIRTIO_BUS_STATE_SUSPEND 0x2 + #define VIRTIO_BUS_STATE_RESUME 0x4 + __u8 state; +} __attribute__((packed)); + +#endif /* _LINUX_VIRTIO_MSG_H */