]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
firmware: arm_ffa: Add support for {un,}registration of framework notifications
authorSudeep Holla <sudeep.holla@arm.com>
Mon, 17 Feb 2025 15:38:57 +0000 (15:38 +0000)
committerSudeep Holla <sudeep.holla@arm.com>
Mon, 17 Feb 2025 15:42:04 +0000 (15:42 +0000)
Framework notifications are doorbells that are rung by the partition
managers to signal common events to an endpoint. These doorbells cannot
be rung by an endpoint directly. A partition manager can signal a
Framework notification in response to an FF-A ABI invocation by an
endpoint.

Two additional notify_ops interface is being added for any FF-A device/
driver to register and unregister for such a framework notifications.

Tested-by: Viresh Kumar <viresh.kumar@linaro.org>
Message-Id: <20250217-ffa_updates-v3-16-bd1d9de615e7@arm.com>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
drivers/firmware/arm_ffa/driver.c
include/linux/arm_ffa.h

index 5863272f45d9c075d8e86d45a90ac6b1fb743c98..a889ad6d94ac499cff869a138970c9dc7ae0a1f9 100644 (file)
@@ -1117,6 +1117,7 @@ static int ffa_memory_lend(struct ffa_mem_ops_args *args)
 struct notifier_cb_info {
        struct hlist_node hnode;
        struct ffa_device *dev;
+       ffa_fwk_notifier_cb fwk_cb;
        ffa_notifier_cb cb;
        void *cb_data;
 };
@@ -1180,28 +1181,61 @@ static enum notify_type ffa_notify_type_get(u16 vm_id)
                return NON_SECURE_VM;
 }
 
-/* Should be called while the notify_lock is taken */
+/* notifier_hnode_get* should be called with notify_lock held */
 static struct notifier_cb_info *
-notifier_hash_node_get(u16 notify_id, enum notify_type type)
+notifier_hnode_get_by_vmid(u16 notify_id, int vmid)
 {
        struct notifier_cb_info *node;
 
        hash_for_each_possible(drv_info->notifier_hash, node, hnode, notify_id)
-               if (type == ffa_notify_type_get(node->dev->vm_id))
+               if (node->fwk_cb && vmid == node->dev->vm_id)
+                       return node;
+
+       return NULL;
+}
+
+static struct notifier_cb_info *
+notifier_hnode_get_by_vmid_uuid(u16 notify_id, int vmid, const uuid_t *uuid)
+{
+       struct notifier_cb_info *node;
+
+       if (uuid_is_null(uuid))
+               return notifier_hnode_get_by_vmid(notify_id, vmid);
+
+       hash_for_each_possible(drv_info->notifier_hash, node, hnode, notify_id)
+               if (node->fwk_cb && vmid == node->dev->vm_id &&
+                   uuid_equal(&node->dev->uuid, uuid))
+                       return node;
+
+       return NULL;
+}
+
+static struct notifier_cb_info *
+notifier_hnode_get_by_type(u16 notify_id, enum notify_type type)
+{
+       struct notifier_cb_info *node;
+
+       hash_for_each_possible(drv_info->notifier_hash, node, hnode, notify_id)
+               if (node->cb && type == ffa_notify_type_get(node->dev->vm_id))
                        return node;
 
        return NULL;
 }
 
 static int
-update_notifier_cb(struct ffa_device *dev, int notify_id, ffa_notifier_cb cb,
-                  void *cb_data, bool is_registration)
+update_notifier_cb(struct ffa_device *dev, int notify_id, void *cb,
+                  void *cb_data, bool is_registration, bool is_framework)
 {
        struct notifier_cb_info *cb_info = NULL;
        enum notify_type type = ffa_notify_type_get(dev->vm_id);
        bool cb_found;
 
-       cb_info = notifier_hash_node_get(notify_id, type);
+       if (is_framework)
+               cb_info = notifier_hnode_get_by_vmid_uuid(notify_id, dev->vm_id,
+                                                         &dev->uuid);
+       else
+               cb_info = notifier_hnode_get_by_type(notify_id, type);
+
        cb_found = !!cb_info;
 
        if (!(is_registration ^ cb_found))
@@ -1213,8 +1247,11 @@ update_notifier_cb(struct ffa_device *dev, int notify_id, ffa_notifier_cb cb,
                        return -ENOMEM;
 
                cb_info->dev = dev;
-               cb_info->cb = cb;
                cb_info->cb_data = cb_data;
+               if (is_framework)
+                       cb_info->fwk_cb = cb;
+               else
+                       cb_info->cb = cb;
 
                hash_add(drv_info->notifier_hash, &cb_info->hnode, notify_id);
        } else {
@@ -1224,7 +1261,8 @@ update_notifier_cb(struct ffa_device *dev, int notify_id, ffa_notifier_cb cb,
        return 0;
 }
 
-static int ffa_notify_relinquish(struct ffa_device *dev, int notify_id)
+static int __ffa_notify_relinquish(struct ffa_device *dev, int notify_id,
+                                  bool is_framework)
 {
        int rc;
 
@@ -1236,22 +1274,35 @@ static int ffa_notify_relinquish(struct ffa_device *dev, int notify_id)
 
        mutex_lock(&drv_info->notify_lock);
 
-       rc = update_notifier_cb(dev, notify_id, NULL, NULL, false);
+       rc = update_notifier_cb(dev, notify_id, NULL, NULL, false,
+                               is_framework);
        if (rc) {
                pr_err("Could not unregister notification callback\n");
                mutex_unlock(&drv_info->notify_lock);
                return rc;
        }
 
-       rc = ffa_notification_unbind(dev->vm_id, BIT(notify_id));
+       if (!is_framework)
+               rc = ffa_notification_unbind(dev->vm_id, BIT(notify_id));
 
        mutex_unlock(&drv_info->notify_lock);
 
        return rc;
 }
 
-static int ffa_notify_request(struct ffa_device *dev, bool is_per_vcpu,
-                             ffa_notifier_cb cb, void *cb_data, int notify_id)
+static int ffa_notify_relinquish(struct ffa_device *dev, int notify_id)
+{
+       return __ffa_notify_relinquish(dev, notify_id, false);
+}
+
+static int ffa_fwk_notify_relinquish(struct ffa_device *dev, int notify_id)
+{
+       return __ffa_notify_relinquish(dev, notify_id, true);
+}
+
+static int __ffa_notify_request(struct ffa_device *dev, bool is_per_vcpu,
+                               void *cb, void *cb_data,
+                               int notify_id, bool is_framework)
 {
        int rc;
        u32 flags = 0;
@@ -1264,26 +1315,44 @@ static int ffa_notify_request(struct ffa_device *dev, bool is_per_vcpu,
 
        mutex_lock(&drv_info->notify_lock);
 
-       if (is_per_vcpu)
-               flags = PER_VCPU_NOTIFICATION_FLAG;
+       if (!is_framework) {
+               if (is_per_vcpu)
+                       flags = PER_VCPU_NOTIFICATION_FLAG;
 
-       rc = ffa_notification_bind(dev->vm_id, BIT(notify_id), flags);
-       if (rc) {
-               mutex_unlock(&drv_info->notify_lock);
-               return rc;
+               rc = ffa_notification_bind(dev->vm_id, BIT(notify_id), flags);
+               if (rc) {
+                       mutex_unlock(&drv_info->notify_lock);
+                       return rc;
+               }
        }
 
-       rc = update_notifier_cb(dev, notify_id, cb, cb_data, true);
+       rc = update_notifier_cb(dev, notify_id, cb, cb_data, true,
+                               is_framework);
        if (rc) {
                pr_err("Failed to register callback for %d - %d\n",
                       notify_id, rc);
-               ffa_notification_unbind(dev->vm_id, BIT(notify_id));
+               if (!is_framework)
+                       ffa_notification_unbind(dev->vm_id, BIT(notify_id));
        }
        mutex_unlock(&drv_info->notify_lock);
 
        return rc;
 }
 
+static int ffa_notify_request(struct ffa_device *dev, bool is_per_vcpu,
+                             ffa_notifier_cb cb, void *cb_data, int notify_id)
+{
+       return __ffa_notify_request(dev, is_per_vcpu, cb, cb_data, notify_id,
+                                   false);
+}
+
+static int
+ffa_fwk_notify_request(struct ffa_device *dev, ffa_fwk_notifier_cb cb,
+                      void *cb_data, int notify_id)
+{
+       return __ffa_notify_request(dev, false, cb, cb_data, notify_id, true);
+}
+
 static int ffa_notify_send(struct ffa_device *dev, int notify_id,
                           bool is_per_vcpu, u16 vcpu)
 {
@@ -1313,7 +1382,7 @@ static void handle_notif_callbacks(u64 bitmap, enum notify_type type)
                        continue;
 
                mutex_lock(&drv_info->notify_lock);
-               cb_info = notifier_hash_node_get(notify_id, type);
+               cb_info = notifier_hnode_get_by_type(notify_id, type);
                mutex_unlock(&drv_info->notify_lock);
 
                if (cb_info && cb_info->cb)
@@ -1386,6 +1455,8 @@ static const struct ffa_notifier_ops ffa_drv_notifier_ops = {
        .sched_recv_cb_unregister = ffa_sched_recv_cb_unregister,
        .notify_request = ffa_notify_request,
        .notify_relinquish = ffa_notify_relinquish,
+       .fwk_notify_request = ffa_fwk_notify_request,
+       .fwk_notify_relinquish = ffa_fwk_notify_relinquish,
        .notify_send = ffa_notify_send,
 };
 
index 4fcbdc70cbc985eabb90733329c974384d229053..5bded24dc24fea8cdcbe42bf79c7c025c3fa5f4b 100644 (file)
@@ -468,6 +468,7 @@ struct ffa_cpu_ops {
 
 typedef void (*ffa_sched_recv_cb)(u16 vcpu, bool is_per_vcpu, void *cb_data);
 typedef void (*ffa_notifier_cb)(int notify_id, void *cb_data);
+typedef void (*ffa_fwk_notifier_cb)(int notify_id, void *cb_data, void *buf);
 
 struct ffa_notifier_ops {
        int (*sched_recv_cb_register)(struct ffa_device *dev,
@@ -476,6 +477,10 @@ struct ffa_notifier_ops {
        int (*notify_request)(struct ffa_device *dev, bool per_vcpu,
                              ffa_notifier_cb cb, void *cb_data, int notify_id);
        int (*notify_relinquish)(struct ffa_device *dev, int notify_id);
+       int (*fwk_notify_request)(struct ffa_device *dev,
+                                 ffa_fwk_notifier_cb cb, void *cb_data,
+                                 int notify_id);
+       int (*fwk_notify_relinquish)(struct ffa_device *dev, int notify_id);
        int (*notify_send)(struct ffa_device *dev, int notify_id, bool per_vcpu,
                           u16 vcpu);
 };