diff options
| author | Arnd Bergmann <arnd@arndb.de> | 2026-01-21 16:19:24 +0100 |
|---|---|---|
| committer | Arnd Bergmann <arnd@arndb.de> | 2026-01-21 16:19:29 +0100 |
| commit | 7a391243d4a5e2e48ffd2b0bb20f69b652ef2791 (patch) | |
| tree | 42302a7df17ad8c2c3466f5b7bf7b25ee4d40c64 | |
| parent | 4e2c045f1043aee67c6a3f1fbba609fba2f70532 (diff) | |
| parent | be4d4543f78074fbebd530ba5109d39a2a34e668 (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.c | 48 |
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); |
