summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSudeep Holla <sudeep.holla@arm.com>2025-12-18 14:20:01 +0000
committerSudeep Holla <sudeep.holla@arm.com>2025-12-18 14:22:07 +0000
commitbe4d4543f78074fbebd530ba5109d39a2a34e668 (patch)
treedffcc26b27d4d7fe2d0af9918a5d8705d542a978
parentf183b1dda4fc0348c4016f6289588d5853ab7936 (diff)
firmware: arm_ffa: Correct 32-bit response handling in NOTIFICATION_INFO_GET
The FF-A specification allows NOTIFICATION_INFO_GET to return either a 64-bit (FFA_FN64_SUCCESS) or a 32-bit (FFA_SUCCESS) response, depending on whether the firmware chooses the SMC64 or SMC32 calling convention. The driver previously detected the response format by checking ret.a0, but still interpreted the returned ID lists (x3..x17 or w3..w7) as if they always followed the 64-bit SMC64 layout. In the SMC32 case, the upper 32 bits of each argument register are undefined by the calling convention, meaning the driver could read stale or garbage values when parsing notification IDs. This resulted in incorrectly decoded partition/VCPU IDs whenever the FF-A firmware used an SMC32 return path. Fix the issue by: - Introducing logic to map list indices to the correct u16 offsets, depending on whether the response width matches the kernel word size or is a 32-bit response on a 64-bit kernel. - Ensuring that the packed ID list is parsed using the proper layout, avoiding reads from undefined upper halves in the SMC32 case. With this change, NOTIFICATION_INFO_GET now correctly interprets ID list entries regardless of the response width, aligning the driver with the FF-A specification. Fixes: 3522be48d82b ("firmware: arm_ffa: Implement the NOTIFICATION_INFO_GET interface") Reported-by: Sourav Mohapatra <sourav.mohapatra@arm.com> Message-Id: <20251218142001.2457111-1-sudeep.holla@arm.com> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
-rw-r--r--drivers/firmware/arm_ffa/driver.c33
1 files changed, 29 insertions, 4 deletions
diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c
index 351780ac8d72..8144f6a9f0e9 100644
--- a/drivers/firmware/arm_ffa/driver.c
+++ b/drivers/firmware/arm_ffa/driver.c
@@ -985,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;
@@ -1005,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);
@@ -1021,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);
@@ -1038,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);
}