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>
This commit is contained in:
Arnd Bergmann
2026-01-21 16:19:24 +01:00

View File

@@ -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);