summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2026-01-21 16:19:24 +0100
committerArnd Bergmann <arnd@arndb.de>2026-01-21 16:19:29 +0100
commit7a391243d4a5e2e48ffd2b0bb20f69b652ef2791 (patch)
tree42302a7df17ad8c2c3466f5b7bf7b25ee4d40c64
parent4e2c045f1043aee67c6a3f1fbba609fba2f70532 (diff)
parentbe4d4543f78074fbebd530ba5109d39a2a34e668 (diff)
Merge tag 'ffa-updates-7.0' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux into soc/drivers
Arm FF-A updates for v7.0 A small set of updates to the Arm FF-A driver: 1. Fix a correctness issue in NOTIFICATION_INFO_GET handling of 32-bit firmware responses, avoiding reads from undefined register bits. 2. Improve clarity and robustness of FF-A feature checks by tying them to explicit version requirements. 3. Ensure Rx/Tx buffers are unmapped on FF-A initialization failure to prevent resource leaks. * tag 'ffa-updates-7.0' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux: firmware: arm_ffa: Correct 32-bit response handling in NOTIFICATION_INFO_GET firmware: arm_ffa: Tie FF-A version checks to specific features firmware: arm_ffa: Unmap Rx/Tx buffers on init failure Signed-off-by: Arnd Bergmann <arnd@arndb.de>
-rw-r--r--drivers/firmware/arm_ffa/driver.c48
1 files changed, 39 insertions, 9 deletions
diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c
index c72ee4756585..8144f6a9f0e9 100644
--- a/drivers/firmware/arm_ffa/driver.c
+++ b/drivers/firmware/arm_ffa/driver.c
@@ -246,6 +246,11 @@ static int ffa_features(u32 func_feat_id, u32 input_props,
}
#define PARTITION_INFO_GET_RETURN_COUNT_ONLY BIT(0)
+#define FFA_SUPPORTS_GET_COUNT_ONLY(version) ((version) > FFA_VERSION_1_0)
+#define FFA_PART_INFO_HAS_SIZE_IN_RESP(version) ((version) > FFA_VERSION_1_0)
+#define FFA_PART_INFO_HAS_UUID_IN_RESP(version) ((version) > FFA_VERSION_1_0)
+#define FFA_PART_INFO_HAS_EXEC_STATE_IN_RESP(version) \
+ ((version) > FFA_VERSION_1_0)
/* buffer must be sizeof(struct ffa_partition_info) * num_partitions */
static int
@@ -255,7 +260,7 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
int idx, count, flags = 0, sz, buf_sz;
ffa_value_t partition_info;
- if (drv_info->version > FFA_VERSION_1_0 &&
+ if (FFA_SUPPORTS_GET_COUNT_ONLY(drv_info->version) &&
(!buffer || !num_partitions)) /* Just get the count for now */
flags = PARTITION_INFO_GET_RETURN_COUNT_ONLY;
@@ -273,12 +278,11 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
count = partition_info.a2;
- if (drv_info->version > FFA_VERSION_1_0) {
+ if (FFA_PART_INFO_HAS_SIZE_IN_RESP(drv_info->version)) {
buf_sz = sz = partition_info.a3;
if (sz > sizeof(*buffer))
buf_sz = sizeof(*buffer);
} else {
- /* FFA_VERSION_1_0 lacks size in the response */
buf_sz = sz = 8;
}
@@ -981,10 +985,27 @@ static void __do_sched_recv_cb(u16 part_id, u16 vcpu, bool is_per_vcpu)
}
}
+/*
+ * Map logical ID index to the u16 index within the packed ID list.
+ *
+ * For native responses (FF-A width == kernel word size), IDs are
+ * tightly packed: idx -> idx.
+ *
+ * For 32-bit responses on a 64-bit kernel, each 64-bit register
+ * contributes 4 x u16 values but only the lower 2 are defined; the
+ * upper 2 are garbage. This mapping skips those upper halves:
+ * 0,1,2,3,4,5,... -> 0,1,4,5,8,9,...
+ */
+static int list_idx_to_u16_idx(int idx, bool is_native_resp)
+{
+ return is_native_resp ? idx : idx + 2 * (idx >> 1);
+}
+
static void ffa_notification_info_get(void)
{
- int idx, list, max_ids, lists_cnt, ids_processed, ids_count[MAX_IDS_64];
- bool is_64b_resp;
+ int ids_processed, ids_count[MAX_IDS_64];
+ int idx, list, max_ids, lists_cnt;
+ bool is_64b_resp, is_native_resp;
ffa_value_t ret;
u64 id_list;
@@ -1001,6 +1022,7 @@ static void ffa_notification_info_get(void)
}
is_64b_resp = (ret.a0 == FFA_FN64_SUCCESS);
+ is_native_resp = (ret.a0 == FFA_FN_NATIVE(SUCCESS));
ids_processed = 0;
lists_cnt = FIELD_GET(NOTIFICATION_INFO_GET_ID_COUNT, ret.a2);
@@ -1017,12 +1039,16 @@ static void ffa_notification_info_get(void)
/* Process IDs */
for (list = 0; list < lists_cnt; list++) {
+ int u16_idx;
u16 vcpu_id, part_id, *packed_id_list = (u16 *)&ret.a3;
if (ids_processed >= max_ids - 1)
break;
- part_id = packed_id_list[ids_processed++];
+ u16_idx = list_idx_to_u16_idx(ids_processed,
+ is_native_resp);
+ part_id = packed_id_list[u16_idx];
+ ids_processed++;
if (ids_count[list] == 1) { /* Global Notification */
__do_sched_recv_cb(part_id, 0, false);
@@ -1034,7 +1060,10 @@ static void ffa_notification_info_get(void)
if (ids_processed >= max_ids - 1)
break;
- vcpu_id = packed_id_list[ids_processed++];
+ u16_idx = list_idx_to_u16_idx(ids_processed,
+ is_native_resp);
+ vcpu_id = packed_id_list[u16_idx];
+ ids_processed++;
__do_sched_recv_cb(part_id, vcpu_id, true);
}
@@ -1706,7 +1735,7 @@ static int ffa_setup_partitions(void)
struct ffa_device *ffa_dev;
struct ffa_partition_info *pbuf, *tpbuf;
- if (drv_info->version == FFA_VERSION_1_0) {
+ if (!FFA_PART_INFO_HAS_UUID_IN_RESP(drv_info->version)) {
ret = bus_register_notifier(&ffa_bus_type, &ffa_bus_nb);
if (ret)
pr_err("Failed to register FF-A bus notifiers\n");
@@ -1733,7 +1762,7 @@ static int ffa_setup_partitions(void)
continue;
}
- if (drv_info->version > FFA_VERSION_1_0 &&
+ if (FFA_PART_INFO_HAS_EXEC_STATE_IN_RESP(drv_info->version) &&
!(tpbuf->properties & FFA_PARTITION_AARCH64_EXEC))
ffa_mode_32bit_set(ffa_dev);
@@ -2068,6 +2097,7 @@ static int __init ffa_init(void)
pr_err("failed to setup partitions\n");
ffa_notifications_cleanup();
+ ffa_rxtx_unmap(drv_info->vm_id);
free_pages:
if (drv_info->tx_buffer)
free_pages_exact(drv_info->tx_buffer, rxtx_bufsz);