lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<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

Powered by Openwall GNU/*/Linux Powered by OpenVZ