@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
ffa-bus-y = bus.o
-ffa-driver-y = driver.o
+ffa-driver-y = driver.o hyp_partitions.o
ffa-transport-$(CONFIG_ARM_FFA_SMCCC) += smccc.o
ffa-module-objs := $(ffa-bus-y) $(ffa-driver-y) $(ffa-transport-y)
obj-$(CONFIG_ARM_FFA_TRANSPORT) = ffa-module.o
@@ -8,6 +8,7 @@
#include <linux/arm-smccc.h>
#include <linux/err.h>
+#include <linux/fs.h>
typedef struct arm_smccc_v1_2_res ffa_res_t;
@@ -15,8 +16,12 @@ typedef ffa_res_t
(ffa_fn)(unsigned long, unsigned long, unsigned long, unsigned long,
unsigned long, unsigned long, unsigned long, unsigned long);
+int __init ffa_hyp_partitions_init(void);
int __init arm_ffa_bus_init(void);
void __exit arm_ffa_bus_exit(void);
+struct ffa_device *ffa_device_get_from_minor(int minor);
+int ffa_setup_partitions(const char *compatible,
+ const struct file_operations *fops);
#ifdef CONFIG_ARM_FFA_SMCCC
int __init ffa_transport_init(ffa_fn **invoke_ffa_fn);
@@ -241,6 +241,31 @@ static int ffa_msg_send_direct_req(u16 src_id, u16 dst_id,
return 0;
}
+struct ffa_device *ffa_device_get_from_minor(int minor)
+{
+ struct list_head *p;
+ struct ffa_device *ffa_dev = NULL;
+
+ mutex_lock(&ffa_devs_list_mutex);
+
+ list_for_each(p, &ffa_devs_list) {
+ struct device *dev;
+ struct ffa_device *tmp_dev;
+
+ tmp_dev = list_to_ffa_dev(p);
+ dev = &tmp_dev->dev;
+
+ if (minor == MINOR(dev->devt)) {
+ ffa_dev = tmp_dev;
+ break;
+ }
+ }
+
+ mutex_unlock(&ffa_devs_list_mutex);
+
+ return ffa_dev;
+}
+
static void ffa_device_get(struct ffa_device *ffa_dev)
{
mutex_lock(&ffa_dev->mutex);
@@ -466,6 +491,8 @@ static int __init ffa_init(void)
/* Set up all the partitions which have in-kernel drivers */
ffa_setup_partitions("arm,ffa-1.0", NULL);
+ ffa_hyp_partitions_init();
+
return 0;
free_pages:
if (drv_info->tx_buffer)
new file mode 100644
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Arm Firmware Framework for ARMv8-A(FFA) userspace interface
+ *
+ * Copyright (C) 2020 Arm Ltd.
+ */
+
+#define pr_fmt(fmt) "ARM FF-A USER : " fmt
+
+#include <linux/arm_ffa.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/uuid.h>
+#include <uapi/linux/arm_ffa.h>
+
+#include "common.h"
+
+struct ffa_dev_data {
+ struct ffa_device *dev;
+ const struct ffa_dev_ops *ops;
+};
+
+static int ffa_open(struct inode *inode, struct file *filp)
+{
+ struct ffa_dev_data *ffa_data;
+
+ ffa_data = kzalloc(sizeof(*ffa_data), GFP_KERNEL);
+ if (!ffa_data)
+ return -ENOMEM;
+
+ ffa_data->dev = ffa_device_get_from_minor(iminor(inode));
+ if (!ffa_data->dev) {
+ kfree(ffa_data);
+ return -EINVAL;
+ }
+
+ ffa_data->ops = ffa_dev_ops_get(ffa_data->dev);
+ if (!ffa_data->ops) {
+ kfree(ffa_data);
+ return -EINVAL;
+ }
+
+ ffa_data->ops->open(ffa_data->dev);
+
+ filp->private_data = ffa_data;
+
+ return 0;
+}
+
+static int ffa_release(struct inode *inode, struct file *filp)
+{
+ struct ffa_dev_data *ffa_data = filp->private_data;
+
+ ffa_data->ops->close(ffa_data->dev);
+
+ filp->private_data = NULL;
+ kfree(ffa_data);
+
+ return 0;
+}
+
+static long ffa_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
+{
+ long r = -EINVAL;
+ void __user *argp = (void __user *)arg;
+ struct ffa_dev_data *ffa_data = filp->private_data;
+ struct ffa_device *ffa_dev = ffa_data->dev;
+
+ switch (ioctl) {
+ case FFA_GET_API_VERSION:
+ case FFA_GET_PARTITION_ID:
+ if (arg)
+ return r;
+ return ffa_data->ops->ioctl(ffa_dev, ioctl, NULL);
+ case FFA_GET_PARTITION_INFO: {
+ struct ffa_part_info __user *pinfo = argp;
+ struct ffa_part_info info;
+ unsigned int count;
+
+ r = -EFAULT;
+ if (copy_from_user(&info, pinfo, sizeof(info)))
+ break;
+ count = ffa_data->ops->ioctl(ffa_dev, ioctl, &info);
+ if (count > 1) {
+ r = -E2BIG;
+ break;
+ }
+ r = -EFAULT;
+ if (copy_to_user(pinfo, &info, sizeof(info)))
+ break;
+ r = 0;
+ break;
+ }
+ case FFA_SEND_RECEIVE_SYNC: {
+ struct ffa_send_recv_sync __user *udata = argp;
+ struct ffa_send_recv_sync kdata;
+
+ r = -EFAULT;
+ if (copy_from_user(&kdata, udata, sizeof(kdata)))
+ break;
+ r = ffa_data->ops->ioctl(ffa_dev, ioctl, &kdata);
+ if (r)
+ break;
+ if (copy_to_user(udata, &kdata, sizeof(kdata)))
+ break;
+ break;
+ }
+ default:
+ r = -EINVAL;
+ }
+ return r;
+}
+
+static const struct file_operations ffa_fops = {
+ .owner = THIS_MODULE,
+ .open = ffa_open,
+ .release = ffa_release,
+ .unlocked_ioctl = ffa_ioctl,
+ .llseek = noop_llseek,
+};
+
+int __init ffa_hyp_partitions_init(void)
+{
+ /* Set up all the partitions that KVM hypervisor maintains */
+ ffa_setup_partitions("arm,ffa-1.0-hypervisor", &ffa_fops);
+
+ return 0;
+}
Parse the FFA nodes from the device-tree and register all the partitions managed by the KVM hypervisor. All the partitions including the host(self) are registered as the character device with a set of file operations. Most of the interface will concentrated in the ioctl. For now, we have a tiny set of initial ioctls implemented. Signed-off-by: Sudeep Holla <sudeep.holla@arm.com> --- drivers/firmware/arm_ffa/Makefile | 2 +- drivers/firmware/arm_ffa/common.h | 5 + drivers/firmware/arm_ffa/driver.c | 27 +++++ drivers/firmware/arm_ffa/hyp_partitions.c | 132 ++++++++++++++++++++++ 4 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 drivers/firmware/arm_ffa/hyp_partitions.c -- 2.25.1