summaryrefslogtreecommitdiff
path: root/drivers/firmware/arm_ffa
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-06-01 17:54:55 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-06-01 17:54:55 +0200
commitd0acb5202d0e33d23cbe6994424587fbb05a5360 (patch)
tree4b7fd07149896994fb739e1cbb5d65f2e47cd6d7 /drivers/firmware/arm_ffa
parent55f3722fc694d6478eca3020b12a4d6a79b06f27 (diff)
parentbb532bfaf7919c7c98caab81864e9ce2646e11e3 (diff)
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/firmware/arm_ffa')
-rw-r--r--drivers/firmware/arm_ffa/bus.c4
-rw-r--r--drivers/firmware/arm_ffa/driver.c131
2 files changed, 96 insertions, 39 deletions
diff --git a/drivers/firmware/arm_ffa/bus.c b/drivers/firmware/arm_ffa/bus.c
index 9576862d89c4..601c3418e0d9 100644
--- a/drivers/firmware/arm_ffa/bus.c
+++ b/drivers/firmware/arm_ffa/bus.c
@@ -26,6 +26,8 @@ static int ffa_device_match(struct device *dev, const struct device_driver *drv)
id_table = to_ffa_driver(drv)->id_table;
ffa_dev = to_ffa_dev(dev);
+ if (!id_table)
+ return 0;
while (!uuid_is_null(&id_table->uuid)) {
/*
@@ -123,7 +125,7 @@ int ffa_driver_register(struct ffa_driver *driver, struct module *owner,
{
int ret;
- if (!driver->probe)
+ if (!driver->probe || !driver->id_table)
return -EINVAL;
driver->driver.bus = &ffa_bus_type;
diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c
index eb2782848283..e0263c3fad70 100644
--- a/drivers/firmware/arm_ffa/driver.c
+++ b/drivers/firmware/arm_ffa/driver.c
@@ -100,6 +100,7 @@ struct ffa_drv_info {
bool mem_ops_native;
bool msg_direct_req2_supp;
bool bitmap_created;
+ bool bus_notifier_registered;
bool notif_enabled;
unsigned int sched_recv_irq;
unsigned int notif_pend_irq;
@@ -322,6 +323,12 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
#define PART_INFO_ID_MASK GENMASK(15, 0)
#define PART_INFO_EXEC_CXT_MASK GENMASK(31, 16)
#define PART_INFO_PROPS_MASK GENMASK(63, 32)
+#define FFA_PART_INFO_GET_REGS_FIRST_REG 3
+#define FFA_PART_INFO_GET_REGS_REGS_PER_DESC 3
+#define FFA_PART_INFO_GET_REGS_MAX_DESC \
+ (((sizeof(ffa_value_t) / sizeof_field(ffa_value_t, a0)) - \
+ FFA_PART_INFO_GET_REGS_FIRST_REG) / \
+ FFA_PART_INFO_GET_REGS_REGS_PER_DESC)
#define PART_INFO_ID(x) ((u16)(FIELD_GET(PART_INFO_ID_MASK, (x))))
#define PART_INFO_EXEC_CXT(x) ((u16)(FIELD_GET(PART_INFO_EXEC_CXT_MASK, (x))))
#define PART_INFO_PROPERTIES(x) ((u32)(FIELD_GET(PART_INFO_PROPS_MASK, (x))))
@@ -329,15 +336,13 @@ static int
__ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
struct ffa_partition_info *buffer, int num_parts)
{
- u16 buf_sz, start_idx, cur_idx, count = 0, prev_idx = 0, tag = 0;
+ u16 buf_sz, start_idx = 0, cur_idx, count = 0, tag = 0;
struct ffa_partition_info *buf = buffer;
ffa_value_t partition_info;
do {
__le64 *regs;
- int idx;
-
- start_idx = prev_idx ? prev_idx + 1 : 0;
+ int idx, nr_desc, buf_idx;
invoke_ffa_fn((ffa_value_t){
.a0 = FFA_PARTITION_INFO_GET_REGS,
@@ -353,15 +358,28 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
count = PARTITION_COUNT(partition_info.a2);
if (!buffer || !num_parts) /* count only */
return count;
+ if (count > num_parts)
+ return -EINVAL;
cur_idx = CURRENT_INDEX(partition_info.a2);
+ if (cur_idx < start_idx || cur_idx >= count)
+ return -EINVAL;
+
+ nr_desc = cur_idx - start_idx + 1;
+ if (nr_desc > FFA_PART_INFO_GET_REGS_MAX_DESC)
+ return -EINVAL;
+
+ buf_idx = buf - buffer;
+ if (buf_idx + nr_desc > num_parts)
+ return -EINVAL;
+
tag = UUID_INFO_TAG(partition_info.a2);
buf_sz = PARTITION_INFO_SZ(partition_info.a2);
if (buf_sz > sizeof(*buffer))
buf_sz = sizeof(*buffer);
regs = (void *)&partition_info.a3;
- for (idx = 0; idx < cur_idx - start_idx + 1; idx++, buf++) {
+ for (idx = 0; idx < nr_desc; idx++, buf++) {
union {
uuid_t uuid;
u64 regs[2];
@@ -379,7 +397,7 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
uuid_copy(&buf->uuid, &uuid_regs.uuid);
regs += 3;
}
- prev_idx = cur_idx;
+ start_idx = cur_idx + 1;
} while (cur_idx < (count - 1));
@@ -1189,7 +1207,7 @@ static int
ffa_sched_recv_cb_update(struct ffa_device *dev, ffa_sched_recv_cb callback,
void *cb_data, bool is_registration)
{
- struct ffa_dev_part_info *partition = NULL, *tmp;
+ struct ffa_dev_part_info *partition = NULL;
struct list_head *phead;
bool cb_valid;
@@ -1202,11 +1220,11 @@ ffa_sched_recv_cb_update(struct ffa_device *dev, ffa_sched_recv_cb callback,
return -EINVAL;
}
- list_for_each_entry_safe(partition, tmp, phead, node)
+ list_for_each_entry(partition, phead, node)
if (partition->dev == dev)
break;
- if (!partition) {
+ if (&partition->node == phead) {
pr_err("%s: No such partition ID 0x%x\n", __func__, dev->vm_id);
return -EINVAL;
}
@@ -1445,20 +1463,25 @@ static int ffa_notify_send(struct ffa_device *dev, int notify_id,
static void handle_notif_callbacks(u64 bitmap, enum notify_type type)
{
+ ffa_notifier_cb cb;
+ void *cb_data;
int notify_id;
- struct notifier_cb_info *cb_info = NULL;
for (notify_id = 0; notify_id <= FFA_MAX_NOTIFICATIONS && bitmap;
notify_id++, bitmap >>= 1) {
if (!(bitmap & 1))
continue;
- read_lock(&drv_info->notify_lock);
- cb_info = notifier_hnode_get_by_type(notify_id, type);
- read_unlock(&drv_info->notify_lock);
+ scoped_guard(read_lock, &drv_info->notify_lock) {
+ struct notifier_cb_info *cb_info;
- if (cb_info && cb_info->cb)
- cb_info->cb(notify_id, cb_info->cb_data);
+ cb_info = notifier_hnode_get_by_type(notify_id, type);
+ cb = cb_info ? cb_info->cb : NULL;
+ cb_data = cb_info ? cb_info->cb_data : NULL;
+ }
+
+ if (cb)
+ cb(notify_id, cb_data);
}
}
@@ -1466,39 +1489,56 @@ static void handle_fwk_notif_callbacks(u32 bitmap)
{
void *buf;
uuid_t uuid;
+ void *fwk_cb_data;
int notify_id = 0, target;
+ ffa_fwk_notifier_cb fwk_cb;
struct ffa_indirect_msg_hdr *msg;
- struct notifier_cb_info *cb_info = NULL;
+ size_t min_offset = offsetof(struct ffa_indirect_msg_hdr, uuid);
/* Only one framework notification defined and supported for now */
if (!(bitmap & FRAMEWORK_NOTIFY_RX_BUFFER_FULL))
return;
- mutex_lock(&drv_info->rx_lock);
+ scoped_guard(mutex, &drv_info->rx_lock) {
+ u32 offset, size;
- msg = drv_info->rx_buffer;
- buf = kmemdup((void *)msg + msg->offset, msg->size, GFP_KERNEL);
- if (!buf) {
- mutex_unlock(&drv_info->rx_lock);
- return;
- }
+ msg = drv_info->rx_buffer;
+ offset = msg->offset;
+ size = msg->size;
- target = SENDER_ID(msg->send_recv_id);
- if (msg->offset >= sizeof(*msg))
- uuid_copy(&uuid, &msg->uuid);
- else
- uuid_copy(&uuid, &uuid_null);
+ if (!size || (offset != min_offset && offset < sizeof(*msg)) ||
+ offset > drv_info->rxtx_bufsz ||
+ size > drv_info->rxtx_bufsz - offset) {
+ pr_err("invalid framework notification message\n");
+ ffa_rx_release();
+ return;
+ }
- mutex_unlock(&drv_info->rx_lock);
+ buf = kmemdup((void *)msg + offset, size, GFP_KERNEL);
+ if (!buf) {
+ ffa_rx_release();
+ return;
+ }
- ffa_rx_release();
+ target = SENDER_ID(msg->send_recv_id);
+ if (offset >= sizeof(*msg))
+ uuid_copy(&uuid, &msg->uuid);
+ else
+ uuid_copy(&uuid, &uuid_null);
+ ffa_rx_release();
+ }
- read_lock(&drv_info->notify_lock);
- cb_info = notifier_hnode_get_by_vmid_uuid(notify_id, target, &uuid);
- read_unlock(&drv_info->notify_lock);
+ scoped_guard(read_lock, &drv_info->notify_lock) {
+ struct notifier_cb_info *cb_info;
+
+ cb_info = notifier_hnode_get_by_vmid_uuid(notify_id, target,
+ &uuid);
+ fwk_cb = cb_info ? cb_info->fwk_cb : NULL;
+ fwk_cb_data = cb_info ? cb_info->cb_data : NULL;
+ }
- if (cb_info && cb_info->fwk_cb)
- cb_info->fwk_cb(notify_id, cb_info->cb_data, buf);
+ if (fwk_cb)
+ fwk_cb(notify_id, fwk_cb_data, buf);
kfree(buf);
}
@@ -1542,7 +1582,7 @@ static void notif_pcpu_irq_work_fn(struct work_struct *work)
struct ffa_drv_info *info = container_of(work, struct ffa_drv_info,
notif_pcpu_work);
- ffa_self_notif_handle(smp_processor_id(), true, info);
+ notif_get_and_handle(info);
}
static const struct ffa_info_ops ffa_drv_info_ops = {
@@ -1629,6 +1669,15 @@ static struct notifier_block ffa_bus_nb = {
.notifier_call = ffa_bus_notifier,
};
+static void ffa_bus_notifier_unregister(void)
+{
+ if (!drv_info->bus_notifier_registered)
+ return;
+
+ bus_unregister_notifier(&ffa_bus_type, &ffa_bus_nb);
+ drv_info->bus_notifier_registered = false;
+}
+
static int ffa_xa_add_partition_info(struct ffa_device *dev)
{
struct ffa_dev_part_info *info;
@@ -1712,6 +1761,8 @@ static void ffa_partitions_cleanup(void)
struct list_head *phead;
unsigned long idx;
+ ffa_bus_notifier_unregister();
+
/* Clean up/free all registered devices */
ffa_devices_unregister();
@@ -1739,11 +1790,14 @@ static int ffa_setup_partitions(void)
ret = bus_register_notifier(&ffa_bus_type, &ffa_bus_nb);
if (ret)
pr_err("Failed to register FF-A bus notifiers\n");
+ else
+ drv_info->bus_notifier_registered = true;
}
count = ffa_partition_probe(&uuid_null, &pbuf);
if (count <= 0) {
pr_info("%s: No partitions found, error %d\n", __func__, count);
+ ffa_bus_notifier_unregister();
return -EINVAL;
}
@@ -2063,11 +2117,12 @@ static int __init ffa_init(void)
rxtx_bufsz = SZ_4K;
}
+ rxtx_bufsz = PAGE_ALIGN(rxtx_bufsz);
drv_info->rxtx_bufsz = rxtx_bufsz;
drv_info->rx_buffer = alloc_pages_exact(rxtx_bufsz, GFP_KERNEL);
if (!drv_info->rx_buffer) {
ret = -ENOMEM;
- goto free_pages;
+ goto free_drv_info;
}
drv_info->tx_buffer = alloc_pages_exact(rxtx_bufsz, GFP_KERNEL);
@@ -2078,7 +2133,7 @@ static int __init ffa_init(void)
ret = ffa_rxtx_map(virt_to_phys(drv_info->tx_buffer),
virt_to_phys(drv_info->rx_buffer),
- PAGE_ALIGN(rxtx_bufsz) / FFA_PAGE_SIZE);
+ rxtx_bufsz / FFA_PAGE_SIZE);
if (ret) {
pr_err("failed to register FFA RxTx buffers\n");
goto free_pages;