Skip to content

Commit be4d454

Browse files
committed
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: 3522be4 ("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>
1 parent f183b1d commit be4d454

1 file changed

Lines changed: 29 additions & 4 deletions

File tree

drivers/firmware/arm_ffa/driver.c

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -985,10 +985,27 @@ static void __do_sched_recv_cb(u16 part_id, u16 vcpu, bool is_per_vcpu)
985985
}
986986
}
987987

988+
/*
989+
* Map logical ID index to the u16 index within the packed ID list.
990+
*
991+
* For native responses (FF-A width == kernel word size), IDs are
992+
* tightly packed: idx -> idx.
993+
*
994+
* For 32-bit responses on a 64-bit kernel, each 64-bit register
995+
* contributes 4 x u16 values but only the lower 2 are defined; the
996+
* upper 2 are garbage. This mapping skips those upper halves:
997+
* 0,1,2,3,4,5,... -> 0,1,4,5,8,9,...
998+
*/
999+
static int list_idx_to_u16_idx(int idx, bool is_native_resp)
1000+
{
1001+
return is_native_resp ? idx : idx + 2 * (idx >> 1);
1002+
}
1003+
9881004
static void ffa_notification_info_get(void)
9891005
{
990-
int idx, list, max_ids, lists_cnt, ids_processed, ids_count[MAX_IDS_64];
991-
bool is_64b_resp;
1006+
int ids_processed, ids_count[MAX_IDS_64];
1007+
int idx, list, max_ids, lists_cnt;
1008+
bool is_64b_resp, is_native_resp;
9921009
ffa_value_t ret;
9931010
u64 id_list;
9941011

@@ -1005,6 +1022,7 @@ static void ffa_notification_info_get(void)
10051022
}
10061023

10071024
is_64b_resp = (ret.a0 == FFA_FN64_SUCCESS);
1025+
is_native_resp = (ret.a0 == FFA_FN_NATIVE(SUCCESS));
10081026

10091027
ids_processed = 0;
10101028
lists_cnt = FIELD_GET(NOTIFICATION_INFO_GET_ID_COUNT, ret.a2);
@@ -1021,12 +1039,16 @@ static void ffa_notification_info_get(void)
10211039

10221040
/* Process IDs */
10231041
for (list = 0; list < lists_cnt; list++) {
1042+
int u16_idx;
10241043
u16 vcpu_id, part_id, *packed_id_list = (u16 *)&ret.a3;
10251044

10261045
if (ids_processed >= max_ids - 1)
10271046
break;
10281047

1029-
part_id = packed_id_list[ids_processed++];
1048+
u16_idx = list_idx_to_u16_idx(ids_processed,
1049+
is_native_resp);
1050+
part_id = packed_id_list[u16_idx];
1051+
ids_processed++;
10301052

10311053
if (ids_count[list] == 1) { /* Global Notification */
10321054
__do_sched_recv_cb(part_id, 0, false);
@@ -1038,7 +1060,10 @@ static void ffa_notification_info_get(void)
10381060
if (ids_processed >= max_ids - 1)
10391061
break;
10401062

1041-
vcpu_id = packed_id_list[ids_processed++];
1063+
u16_idx = list_idx_to_u16_idx(ids_processed,
1064+
is_native_resp);
1065+
vcpu_id = packed_id_list[u16_idx];
1066+
ids_processed++;
10421067

10431068
__do_sched_recv_cb(part_id, vcpu_id, true);
10441069
}

0 commit comments

Comments
 (0)