[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <c3b5352befaac3bdeb0c169bdb76b7ae2453361e.1753865268.git.viresh.kumar@linaro.org>
Date: Wed, 30 Jul 2025 14:59:34 +0530
From: Viresh Kumar <viresh.kumar@...aro.org>
To: linux-kernel@...r.kernel.org,
"Michael S. Tsirkin" <mst@...hat.com>,
Jason Wang <jasowang@...hat.com>,
Xuan Zhuo <xuanzhuo@...ux.alibaba.com>,
Eugenio Pérez <eperezma@...hat.com>,
Viresh Kumar <viresh.kumar@...aro.org>
Cc: Arnd Bergmann <arnd@...nel.org>,
Vincent Guittot <vincent.guittot@...aro.org>,
Alex Bennée <alex.bennee@...aro.org>,
Bill Mills <bill.mills@...aro.org>,
Rob Herring <robh@...nel.org>,
Saravana Kannan <saravanak@...gle.com>,
devicetree@...r.kernel.org,
virtualization@...ts.linux.dev,
Sudeep Holla <sudeep.holla@....com>,
Bertrand Marquis <bertrand.marquis@....com>,
"Edgar E . Iglesias" <edgar.iglesias@....com>,
Arnaud Pouliquen <arnaud.pouliquen@...s.st.com>,
Krzysztof Kozlowski <krzk+dt@...nel.org>,
Conor Dooley <conor+dt@...nel.org>
Subject: [RFC PATCH 5/6] virtio-msg: Add support for FF-A (Firmware Framework for Arm) bus
Introduce a virtio-msg bus implementation based on the Arm FF-A
(Firmware Framework for Arm) communication interface.
This bus enables virtio-msg transport over secure channels typically
used between the normal world OS and a secure OS or hypervisor. It
leverages the standardized FF-A interface to exchange messages with a
remote backend service.
The implementation integrates with the core virtio-msg transport and
uses FF-A service calls to transmit and receive messages.
Optionally, this bus supports attaching a reserved-memory region to
constrain DMA-coherent and streaming DMA allocations to a well-defined
contiguous area. This memory can be pre-mapped on the remote side,
reducing runtime overhead and preventing accidental sharing of unrelated
pages due to page-granularity mapping.
To enable reserved memory, the following device tree node should be
defined (the node must be named "vmsgffa"):
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
ranges;
vmsgffa@...000000 {
compatible = "restricted-dma-pool";
reg = <0x00000001 0x00000000 0x0 0x00400000>; /* 4 MiB */
};
};
Signed-off-by: Viresh Kumar <viresh.kumar@...aro.org>
---
drivers/virtio/Kconfig | 12 +-
drivers/virtio/Makefile | 1 +
drivers/virtio/virtio_msg_ffa.c | 505 ++++++++++++++++++++++++++++
include/uapi/linux/virtio_msg_ffa.h | 94 ++++++
4 files changed, 611 insertions(+), 1 deletion(-)
create mode 100644 drivers/virtio/virtio_msg_ffa.c
create mode 100644 include/uapi/linux/virtio_msg_ffa.h
diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
index a86025c9e008..683152477e3f 100644
--- a/drivers/virtio/Kconfig
+++ b/drivers/virtio/Kconfig
@@ -176,7 +176,8 @@ config VIRTIO_MSG
select VIRTIO
help
This enables support for Virtio message transport. This option is
- selected by any driver which implements the virtio message bus.
+ selected by any driver which implements the virtio message bus, such
+ as VIRTIO_MSG_FFA.
config VIRTIO_MSG_USER
tristate "Userspace interface for virtio message transport"
@@ -186,6 +187,15 @@ config VIRTIO_MSG_USER
can be used to read / write messages over virtio-msg transport from
userspace.
+config VIRTIO_MSG_FFA
+ tristate "FF-A bus driver for virtio message transport"
+ depends on ARM_FFA_TRANSPORT
+ select VIRTIO_MSG
+ help
+ This implements a Virtio message bus based on ARM FF-A protocol.
+
+ If unsure, say N.
+
config VIRTIO_DMA_SHARED_BUFFER
tristate
depends on DMA_SHARED_BUFFER
diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
index 5b664c5f5f25..96ec0a9c4a7a 100644
--- a/drivers/virtio/Makefile
+++ b/drivers/virtio/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o
virtio_msg_transport-y := virtio_msg.o
virtio_msg_transport-$(CONFIG_VIRTIO_MSG_USER) += virtio_msg_user.o
obj-$(CONFIG_VIRTIO_MSG) += virtio_msg_transport.o
+obj-$(CONFIG_VIRTIO_MSG_FFA) += virtio_msg_ffa.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_ffa.c b/drivers/virtio/virtio_msg_ffa.c
new file mode 100644
index 000000000000..e7f2cbcd238b
--- /dev/null
+++ b/drivers/virtio/virtio_msg_ffa.c
@@ -0,0 +1,505 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * FF-A bus implementation for Virtio message transport.
+ *
+ * Copyright (C) 2025 Google LLC and Linaro.
+ * Viresh Kumar <viresh.kumar@...aro.org>
+ *
+ * This implements the FF-A (Arm Firmware Framework) bus for Virtio msg
+ * transport.
+ */
+
+#define pr_fmt(fmt) "virtio-msg-ffa: " fmt
+
+#include <linux/arm_ffa.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/virtio.h>
+#include <uapi/linux/virtio_msg_ffa.h>
+
+#include "virtio_msg_internal.h"
+
+struct virtio_msg_indirect_data {
+ struct completion completion;
+ struct virtio_msg *response;
+};
+
+struct virtio_msg_device_data {
+ struct virtio_msg_device vmdev;
+ struct virtio_msg_indirect_data idata;
+};
+
+/* Represents FF-A corresponding to a partition */
+struct virtio_msg_ffa_device {
+ struct ffa_device *ffa_dev;
+ struct reserved_mem *rmem;
+ struct virtio_msg_indirect_data idata;
+ struct virtio_msg_device_data *vmdevs;
+ int (*send)(struct virtio_msg_ffa_device *vmfdev,
+ struct virtio_msg *request,
+ struct virtio_msg *response,
+ struct virtio_msg_indirect_data *idata);
+ int vmdev_count;
+ u16 msg_size;
+};
+
+#define to_vmdevdata(_vmdev) \
+ container_of(_vmdev, struct virtio_msg_device_data, vmdev)
+#define to_vmfdev(_vmdev) ((struct virtio_msg_ffa_device *)(_vmdev)->bus_data)
+
+static int vmsg_ffa_send_direct(struct virtio_msg_ffa_device *vmfdev,
+ struct virtio_msg *request,
+ struct virtio_msg *response,
+ struct virtio_msg_indirect_data *idata_unused)
+{
+ struct ffa_device *ffa_dev = vmfdev->ffa_dev;
+ struct ffa_send_direct_data2 ffa_data;
+ int ret;
+
+ memcpy(&ffa_data, request, request->msg_size);
+
+ ret = ffa_dev->ops->msg_ops->sync_send_receive2(ffa_dev, &ffa_data);
+ if (ret) {
+ dev_dbg(&ffa_dev->dev,
+ "Unable to send direct FF-A message: %d\n", ret);
+ return ret;
+ }
+
+ if (response)
+ memcpy(response, &ffa_data, vmfdev->msg_size);
+
+ return 0;
+}
+
+static int vmsg_ffa_send_indirect(struct virtio_msg_ffa_device *vmfdev,
+ struct virtio_msg *request,
+ struct virtio_msg *response,
+ struct virtio_msg_indirect_data *idata)
+{
+ struct ffa_device *ffa_dev = vmfdev->ffa_dev;
+ struct device *dev = &ffa_dev->dev;
+ int ret, count = 10;
+
+ /*
+ * Store the response pointer in idata structure. This will be updated
+ * by vmsg_ffa_notifier_cb() later.
+ */
+ idata->response = response;
+
+try_again:
+ ret = ffa_dev->ops->msg_ops->indirect_send(ffa_dev, request,
+ request->msg_size);
+ if (ret == -EBUSY && --count) {
+ cpu_relax();
+ goto try_again;
+ }
+
+ if (ret) {
+ dev_err(dev, "Failed sending indirect FF-A message: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Always wait for the operation to finish, otherwise we may start
+ * another operation while the previous one is still ongoing.
+ */
+ ret = wait_for_completion_interruptible_timeout(&idata->completion, 1000);
+ if (ret < 0) {
+ dev_err(dev, "Interrupted - waiting for a response: %d\n", ret);
+ } else if (!ret) {
+ dev_err(dev, "Timed out waiting for a response\n");
+ ret = -ETIMEDOUT;
+ } else {
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static struct virtio_msg_device *
+find_vmdev(struct virtio_msg_ffa_device *vmfdev, u16 dev_id)
+{
+ int i;
+
+ /* Find the device corresponding to a dev_id */
+ for (i = 0; i < vmfdev->vmdev_count; i++) {
+ if (vmfdev->vmdevs[i].vmdev.dev_id == dev_id)
+ return &vmfdev->vmdevs[i].vmdev;
+ }
+
+ dev_err(&vmfdev->ffa_dev->dev, "Couldn't find matching vmdev: %d\n",
+ dev_id);
+ return NULL;
+}
+
+static void vmsg_ffa_notifier_cb(int notify_id, void *cb_data, void *buf)
+{
+ struct virtio_msg_ffa_device *vmfdev = cb_data;
+ struct ffa_device *ffa_dev = vmfdev->ffa_dev;
+ struct virtio_msg_indirect_data *idata;
+ struct virtio_msg_device *vmdev;
+ struct virtio_msg *vmsg = buf;
+
+ /*
+ * We can either receive a response message (to a previously sent
+ * request), or an EVENT_USED request message.
+ */
+ if (vmsg->type & VIRTIO_MSG_TYPE_RESPONSE) {
+ if (vmsg->type & VIRTIO_MSG_TYPE_BUS) {
+ idata = &vmfdev->idata;
+ } else {
+ vmdev = find_vmdev(vmfdev, le16_to_cpu(vmsg->dev_id));
+ if (!vmdev)
+ return;
+
+ idata = &to_vmdevdata(vmdev)->idata;
+ }
+
+ if (idata->response)
+ memcpy(idata->response, vmsg, vmsg->msg_size);
+
+ complete(&idata->completion);
+
+ return;
+ }
+
+ /* Only support EVENT_USED virtio request messages */
+ if (vmsg->type & VIRTIO_MSG_TYPE_BUS ||
+ vmsg->msg_id != VIRTIO_MSG_EVENT_USED) {
+ dev_err(&ffa_dev->dev, "Unsupported message received\n");
+ return;
+ }
+
+ vmdev = find_vmdev(vmfdev, le16_to_cpu(vmsg->dev_id));
+ if (!vmdev)
+ return;
+
+ virtio_msg_event(vmdev, vmsg);
+}
+
+static int vmsg_ffa_notify_setup(struct virtio_msg_ffa_device *vmfdev)
+{
+ struct ffa_device *ffa_dev = vmfdev->ffa_dev;
+ int ret;
+
+ ret = ffa_dev->ops->notifier_ops->fwk_notify_request(ffa_dev,
+ &vmsg_ffa_notifier_cb, vmfdev, 0);
+ if (ret)
+ dev_err(&ffa_dev->dev, "Unable to request notifier: %d\n", ret);
+
+ return ret;
+}
+
+static void vmsg_ffa_notify_cleanup(struct virtio_msg_ffa_device *vmfdev)
+{
+ struct ffa_device *ffa_dev = vmfdev->ffa_dev;
+ int ret;
+
+ ret = ffa_dev->ops->notifier_ops->fwk_notify_relinquish(ffa_dev, 0);
+ if (ret)
+ dev_err(&ffa_dev->dev, "Unable to relinquish notifier: %d\n", ret);
+}
+
+static int vmsg_ffa_bus_get_devices(struct virtio_msg_ffa_device *vmfdev,
+ u16 *map, u16 *count)
+{
+ u8 req_buf[VIRTIO_MSG_FFA_BUS_MSG_SIZE];
+ u8 res_buf[VIRTIO_MSG_FFA_BUS_MSG_SIZE];
+ struct virtio_msg *request = (struct virtio_msg *)&req_buf;
+ struct virtio_msg *response = (struct virtio_msg *)&res_buf;
+ struct bus_get_devices *req_payload = virtio_msg_payload(request);
+ struct bus_get_devices_resp *res_payload = virtio_msg_payload(response);
+ int ret;
+
+ static_assert(sizeof(*request) + sizeof(*req_payload) <
+ VIRTIO_MSG_FFA_BUS_MSG_SIZE);
+ static_assert(sizeof(*response) + sizeof(*res_payload) <
+ VIRTIO_MSG_FFA_BUS_MSG_SIZE);
+
+ virtio_msg_prepare(request, VIRTIO_MSG_BUS_GET_DEVICES,
+ sizeof(*req_payload));
+ req_payload->offset = 0;
+ req_payload->num = cpu_to_le16(0xFF);
+
+ ret = vmfdev->send(vmfdev, request, response, &vmfdev->idata);
+ if (ret < 0)
+ return ret;
+
+ *count = le16_to_cpu(res_payload->num);
+ if (!*count)
+ return -ENODEV;
+
+ if (res_payload->offset != req_payload->offset)
+ return -EINVAL;
+
+ /* Support up to 16 devices for now */
+ if (res_payload->next_offset)
+ return -EINVAL;
+
+ map[0] = res_payload->devices[0];
+ map[1] = res_payload->devices[1];
+
+ return 0;
+}
+
+static int vmsg_ffa_bus_version(struct virtio_msg_ffa_device *vmfdev)
+{
+ u8 req_buf[VIRTIO_MSG_FFA_BUS_MSG_SIZE];
+ u8 res_buf[VIRTIO_MSG_FFA_BUS_MSG_SIZE];
+ struct virtio_msg *request = (struct virtio_msg *)&req_buf;
+ struct virtio_msg *response = (struct virtio_msg *)&res_buf;
+ struct bus_ffa_version *req_payload = virtio_msg_payload(request);
+ struct bus_ffa_version_resp *res_payload = virtio_msg_payload(response);
+ u32 features;
+ int ret;
+
+ static_assert(sizeof(*request) + sizeof(*req_payload) <
+ VIRTIO_MSG_FFA_BUS_MSG_SIZE);
+ static_assert(sizeof(*response) + sizeof(*res_payload) <
+ VIRTIO_MSG_FFA_BUS_MSG_SIZE);
+
+ virtio_msg_prepare(request, VIRTIO_MSG_FFA_BUS_VERSION,
+ sizeof(*req_payload));
+ req_payload->driver_version = cpu_to_le32(VIRTIO_MSG_FFA_BUS_VERSION_1_0);
+ req_payload->vmsg_revision = cpu_to_le32(VIRTIO_MSG_REVISION_1);
+ req_payload->vmsg_features = cpu_to_le32(VIRTIO_MSG_FEATURES);
+ req_payload->features = cpu_to_le32(VIRTIO_MSG_FFA_FEATURE_BOTH_SUPP);
+ req_payload->area_num = cpu_to_le16(VIRTIO_MSG_FFA_AREA_ID_MAX);
+
+ ret = vmfdev->send(vmfdev, request, response, &vmfdev->idata);
+ if (ret < 0)
+ return ret;
+
+ if (le32_to_cpu(res_payload->device_version) != VIRTIO_MSG_FFA_BUS_VERSION_1_0)
+ return -EINVAL;
+
+ if (le32_to_cpu(res_payload->vmsg_revision) != VIRTIO_MSG_REVISION_1)
+ return -EINVAL;
+
+ if (le32_to_cpu(res_payload->vmsg_features) != VIRTIO_MSG_FEATURES)
+ return -EINVAL;
+
+ features = le32_to_cpu(res_payload->features);
+
+ /*
+ * - Direct message must be supported if it already worked.
+ * - Indirect message must be supported if it already worked
+ * - And direct message must not be supported since it didn't work.
+ */
+ if ((ffa_partition_supports_direct_recv(vmfdev->ffa_dev) &&
+ !(features & VIRTIO_MSG_FFA_FEATURE_DIRECT_MSG_RX_SUPP)) ||
+ (ffa_partition_supports_indirect_msg(vmfdev->ffa_dev) &&
+ !(features & VIRTIO_MSG_FFA_FEATURE_INDIRECT_MSG_SUPP))) {
+ dev_err(&vmfdev->ffa_dev->dev, "Invalid features\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int virtio_msg_ffa_transfer(struct virtio_msg_device *vmdev,
+ struct virtio_msg *request,
+ struct virtio_msg *response)
+{
+ struct virtio_msg_indirect_data *idata = &to_vmdevdata(vmdev)->idata;
+ struct virtio_msg_ffa_device *vmfdev = to_vmfdev(vmdev);
+
+ return vmfdev->send(vmfdev, request, response, idata);
+}
+
+static const char *virtio_msg_ffa_bus_info(struct virtio_msg_device *vmdev,
+ u16 *msg_size, u32 *rev)
+{
+ struct virtio_msg_ffa_device *vmfdev = to_vmfdev(vmdev);
+
+ *msg_size = vmfdev->msg_size;
+ *rev = VIRTIO_MSG_REVISION_1;
+
+ return dev_name(&vmfdev->ffa_dev->dev);
+}
+
+static struct virtio_msg_ops vmf_ops = {
+ .transfer = virtio_msg_ffa_transfer,
+ .bus_info = virtio_msg_ffa_bus_info,
+};
+
+static void remove_vmdevs(struct virtio_msg_ffa_device *vmfdev, int count)
+{
+ while (count--)
+ virtio_msg_unregister(&vmfdev->vmdevs[count].vmdev);
+}
+
+static int virtio_msg_ffa_probe(struct ffa_device *ffa_dev)
+{
+ struct virtio_msg_ffa_device *vmfdev;
+ struct device *dev = &ffa_dev->dev;
+ struct virtio_msg_device *vmdev;
+ unsigned long devices = 0;
+ int ret, i = 0, bit;
+ u16 count;
+
+ vmfdev = devm_kzalloc(dev, sizeof(*vmfdev), GFP_KERNEL);
+ if (!vmfdev)
+ return -ENOMEM;
+
+ if (ffa_partition_supports_direct_recv(ffa_dev)) {
+ vmfdev->send = vmsg_ffa_send_direct;
+ } else if (ffa_partition_supports_indirect_msg(ffa_dev)) {
+ vmfdev->send = vmsg_ffa_send_indirect;
+ } else {
+ dev_err(dev, "Direct or Indirect messages not supported\n");
+ return -EINVAL;
+ }
+
+ vmfdev->ffa_dev = ffa_dev;
+ vmfdev->msg_size = VIRTIO_MSG_FFA_BUS_MSG_SIZE;
+ ffa_dev_set_drvdata(ffa_dev, vmfdev);
+ init_completion(&vmfdev->idata.completion);
+
+ ret = vmsg_ffa_notify_setup(vmfdev);
+ if (ret)
+ return ret;
+
+ ret = vmsg_ffa_bus_version(vmfdev);
+ if (ret)
+ goto notify_cleanup;
+
+ ret = vmsg_ffa_bus_get_devices(vmfdev, (u16 *)&devices, &count);
+ if (ret)
+ goto notify_cleanup;
+
+ ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(64));
+ if (ret)
+ dev_warn(dev, "Failed to enable 64-bit or 32-bit DMA\n");
+
+ vmfdev->rmem = of_reserved_mem_lookup_by_name("vmsgffa");
+ if (!IS_ERR(vmfdev->rmem)) {
+ ret = reserved_mem_device_init(dev, vmfdev->rmem);
+ if (ret)
+ goto rmem_free;
+ } else {
+ dev_info(dev, "Continuing without reserved-memory block\n");
+ }
+
+ vmfdev->vmdevs = devm_kcalloc(dev, count, sizeof(*vmfdev->vmdevs),
+ GFP_KERNEL);
+ if (!vmfdev->vmdevs) {
+ ret = -ENOMEM;
+ goto rmem_free;
+ }
+ vmfdev->vmdev_count = count;
+
+ for_each_set_bit(bit, &devices, sizeof(devices)) {
+ init_completion(&vmfdev->vmdevs[i].idata.completion);
+ vmdev = &vmfdev->vmdevs[i].vmdev;
+ vmdev->dev_id = bit;
+ vmdev->ops = &vmf_ops;
+ vmdev->vdev.dev.parent = dev;
+ vmdev->bus_data = vmfdev;
+
+ ret = virtio_msg_register(vmdev);
+ if (ret) {
+ dev_err(dev, "Failed to register virtio-msg device (%d)\n", ret);
+ goto unregister;
+ }
+
+ i++;
+ }
+
+ return 0;
+
+unregister:
+ remove_vmdevs(vmfdev, i);
+rmem_free:
+ if (!IS_ERR(vmfdev->rmem))
+ of_reserved_mem_device_release(dev);
+notify_cleanup:
+ vmsg_ffa_notify_cleanup(vmfdev);
+ return ret;
+}
+
+static void virtio_msg_ffa_remove(struct ffa_device *ffa_dev)
+{
+ struct virtio_msg_ffa_device *vmfdev = ffa_dev->dev.driver_data;
+
+ remove_vmdevs(vmfdev, vmfdev->vmdev_count);
+
+ if (!IS_ERR(vmfdev->rmem))
+ of_reserved_mem_device_release(&ffa_dev->dev);
+
+ vmsg_ffa_notify_cleanup(vmfdev);
+}
+
+static const struct ffa_device_id virtio_msg_ffa_device_ids[] = {
+ /* c66028b5-2498-4aa1-9de7-77da6122abf0 */
+ { UUID_INIT(0xc66028b5, 0x2498, 0x4aa1,
+ 0x9d, 0xe7, 0x77, 0xda, 0x61, 0x22, 0xab, 0xf0) },
+ {}
+};
+
+static int __maybe_unused virtio_msg_ffa_suspend(struct device *dev)
+{
+ struct virtio_msg_ffa_device *vmfdev = dev_get_drvdata(dev);
+ int ret, i, index;
+
+ for (i = 0; i < vmfdev->vmdev_count; i++) {
+ index = vmfdev->vmdev_count - i - 1;
+ ret = virtio_device_freeze(&vmfdev->vmdevs[index].vmdev.vdev);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused virtio_msg_ffa_resume(struct device *dev)
+{
+ struct virtio_msg_ffa_device *vmfdev = dev_get_drvdata(dev);
+ int ret, i;
+
+ for (i = 0; i < vmfdev->vmdev_count; i++) {
+ ret = virtio_device_restore(&vmfdev->vmdevs[i].vmdev.vdev);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops virtio_msg_ffa_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(virtio_msg_ffa_suspend, virtio_msg_ffa_resume)
+};
+
+static struct ffa_driver virtio_msg_ffa_driver = {
+ .name = "virtio-msg-ffa",
+ .probe = virtio_msg_ffa_probe,
+ .remove = virtio_msg_ffa_remove,
+ .id_table = virtio_msg_ffa_device_ids,
+ .driver = {
+ .pm = &virtio_msg_ffa_pm_ops,
+ },
+};
+
+static int virtio_msg_ffa_init(void)
+{
+ if (IS_REACHABLE(CONFIG_ARM_FFA_TRANSPORT))
+ return ffa_register(&virtio_msg_ffa_driver);
+ else
+ return -EOPNOTSUPP;
+}
+module_init(virtio_msg_ffa_init);
+
+static void virtio_msg_ffa_exit(void)
+{
+ if (IS_REACHABLE(CONFIG_ARM_FFA_TRANSPORT))
+ ffa_unregister(&virtio_msg_ffa_driver);
+}
+module_exit(virtio_msg_ffa_exit);
+
+MODULE_AUTHOR("Viresh Kumar <viresh.kumar@...aro.org>");
+MODULE_DESCRIPTION("Virtio message FF-A bus driver");
+MODULE_LICENSE("GPL");
diff --git a/include/uapi/linux/virtio_msg_ffa.h b/include/uapi/linux/virtio_msg_ffa.h
new file mode 100644
index 000000000000..adcc081b483a
--- /dev/null
+++ b/include/uapi/linux/virtio_msg_ffa.h
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/*
+ * Virtio message FF-A (Arm Firmware Framework) bus header.
+ *
+ * Copyright (C) 2025 Google LLC and Linaro.
+ * Viresh Kumar <viresh.kumar@...aro.org>
+ */
+
+#ifndef _LINUX_VIRTIO_MSG_FFA_H
+#define _LINUX_VIRTIO_MSG_FFA_H
+
+#include <linux/types.h>
+
+/* Message types */
+#define VIRTIO_MSG_FFA_BUS_VERSION 0x80
+#define VIRTIO_MSG_FFA_BUS_AREA_SHARE 0x81
+#define VIRTIO_MSG_FFA_BUS_AREA_UNSHARE 0x82
+#define VIRTIO_MSG_FFA_BUS_RESET 0x83
+#define VIRTIO_MSG_FFA_BUS_EVENT_POLL 0x84
+#define VIRTIO_MSG_FFA_BUS_AREA_RELEASE 0xC0
+
+#define VIRTIO_MSG_FEATURES 0
+#define VIRTIO_MSG_FFA_BUS_VERSION_1_0 0x1
+#define VIRTIO_MSG_FFA_BUS_MSG_SIZE VIRTIO_MSG_MIN_SIZE
+
+#define VIRTIO_MSG_FFA_FEATURE_DIRECT_MSG_RX_SUPP (1 << 0)
+#define VIRTIO_MSG_FFA_FEATURE_DIRECT_MSG_TX_SUPP (1 << 1)
+#define VIRTIO_MSG_FFA_FEATURE_DIRECT_MSG_SUPP \
+ (VIRTIO_MSG_FFA_FEATURE_DIRECT_MSG_RX_SUPP | \
+ VIRTIO_MSG_FFA_FEATURE_DIRECT_MSG_TX_SUPP)
+
+#define VIRTIO_MSG_FFA_FEATURE_INDIRECT_MSG_RX_SUPP (1 << 2)
+#define VIRTIO_MSG_FFA_FEATURE_INDIRECT_MSG_TX_SUPP (1 << 3)
+#define VIRTIO_MSG_FFA_FEATURE_INDIRECT_MSG_SUPP \
+ (VIRTIO_MSG_FFA_FEATURE_INDIRECT_MSG_RX_SUPP | \
+ VIRTIO_MSG_FFA_FEATURE_INDIRECT_MSG_TX_SUPP)
+
+#define VIRTIO_MSG_FFA_FEATURE_BOTH_SUPP \
+ (VIRTIO_MSG_FFA_FEATURE_DIRECT_MSG_SUPP | \
+ VIRTIO_MSG_FFA_FEATURE_INDIRECT_MSG_SUPP)
+
+#define VIRTIO_MSG_FFA_AREA_ID_MAX 0xFF
+#define VIRTIO_MSG_FFA_AREA_ID_OFFSET 56
+#define VIRTIO_MSG_FFA_OFFSET_MASK \
+ ((ULL(1) << VIRTIO_MSG_FFA_AREA_ID_OFFSET) - 1)
+
+#define VIRTIO_MSG_FFA_RESULT_ERROR (1 << 0)
+#define VIRTIO_MSG_FFA_RESULT_BUSY (1 << 1)
+
+/* Message payload format */
+
+struct bus_ffa_version {
+ __le32 driver_version;
+ __le32 vmsg_revision;
+ __le32 vmsg_features;
+ __le32 features;
+ __le16 area_num;
+} __attribute__((packed));
+
+struct bus_ffa_version_resp {
+ __le32 device_version;
+ __le32 vmsg_revision;
+ __le32 vmsg_features;
+ __le32 features;
+ __le16 area_num;
+} __attribute__((packed));
+
+struct bus_area_share {
+ __le16 area_id;
+ __le64 mem_handle;
+ __le64 tag;
+ __le32 count;
+ __le32 attr;
+} __attribute__((packed));
+
+struct bus_area_share_resp {
+ __le16 area_id;
+ __le16 result;
+} __attribute__((packed));
+
+struct bus_area_unshare {
+ __le16 area_id;
+} __attribute__((packed));
+
+struct bus_area_unshare_resp {
+ __le16 area_id;
+ __le16 result;
+} __attribute__((packed));
+
+struct bus_area_release {
+ __le16 area_id;
+} __attribute__((packed));
+
+#endif /* _LINUX_VIRTIO_MSG_FFA_H */
--
2.31.1.272.g89b43f80a514
Powered by blists - more mailing lists