fix(sdei): return SDEI_EINVAL if signaling state is incorrect
In case a step is omitted when an SDEI event is registered,
enabled, unmasked and then signaled, incorrect code paths
may be reached.
This patch adds additional checks to return early from such
an incorrect state.
Change-Id: Ia2753e9a1b95544e1afa72603574fe830f51ea9f
Signed-off-by: Igor Podgainõi <igor.podgainoi@arm.com>
Signed-off-by: Mark Dykes <mark.dykes@arm.com>
diff --git a/services/std_svc/sdei/sdei_event.c b/services/std_svc/sdei/sdei_event.c
index e0c7971..cc8f557 100644
--- a/services/std_svc/sdei/sdei_event.c
+++ b/services/std_svc/sdei/sdei_event.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2022, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2017-2025, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -19,14 +19,14 @@
* Both shared and private maps are stored in single-dimensional array. Private
* event entries are kept for each PE forming a 2D array.
*/
-sdei_entry_t *get_event_entry(sdei_ev_map_t *map)
+sdei_entry_t *get_event_entry(const sdei_ev_map_t *map)
{
const sdei_mapping_t *mapping;
sdei_entry_t *cpu_priv_base;
unsigned int base_idx;
long int idx;
- if (is_event_private(map)) {
+ if ((map->map_flags & BIT_32(SDEI_MAPF_PRIVATE_SHIFT_)) != 0U) {
/*
* For a private map, find the index of the mapping in the
* array.
@@ -52,6 +52,39 @@
}
/*
+ * Retrieve the SDEI entry for the given mapping and target PE.
+ *
+ * on success : Returns a pointer to the SDEI entry
+ *
+ * On error, returns NULL
+ *
+ * Both shared and private maps are stored in single-dimensional array. Private
+ * event entries are kept for each PE forming a 2D array.
+ */
+sdei_entry_t *get_event_entry_target_pe(long int mapsub, unsigned int nm, uint64_t target_pe)
+{
+ sdei_entry_t *cpu_priv_base;
+ unsigned int base_idx;
+ long int idx;
+
+ /*
+ * For a private map, find the index of the mapping in the
+ * array.
+ */
+ idx = mapsub;
+
+ /* Base of private mappings for this CPU */
+ base_idx = (unsigned int) plat_core_pos_by_mpidr(target_pe);
+ base_idx *= nm;
+ cpu_priv_base = &sdei_private_event_table[base_idx];
+ /*
+ * Return the address of the entry at the same index in the
+ * per-CPU event entry.
+ */
+ return &cpu_priv_base[idx];
+}
+
+/*
* Find event mapping for a given interrupt number: On success, returns pointer
* to the event mapping. On error, returns NULL.
*/
diff --git a/services/std_svc/sdei/sdei_intr_mgmt.c b/services/std_svc/sdei/sdei_intr_mgmt.c
index c58adba..4854b2e 100644
--- a/services/std_svc/sdei/sdei_intr_mgmt.c
+++ b/services/std_svc/sdei/sdei_intr_mgmt.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2017-2025, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -29,7 +29,8 @@
#define MAX_EVENT_NESTING 2U
/* Per-CPU SDEI state access macro */
-#define sdei_get_this_pe_state() (&cpu_state[plat_my_core_pos()])
+#define sdei_get_this_pe_state() (&cpu_state[plat_my_core_pos()])
+#define sdei_get_target_pe_state(_pe) (&cpu_state[plat_core_pos_by_mpidr(_pe)])
/* Structure to store information about an outstanding dispatch */
typedef struct sdei_dispatch_context {
@@ -58,6 +59,13 @@
/* SDEI states for all cores in the system */
static sdei_cpu_state_t cpu_state[PLATFORM_CORE_COUNT];
+bool sdei_is_target_pe_masked(uint64_t target_pe)
+{
+ const sdei_cpu_state_t *state = sdei_get_target_pe_state(target_pe);
+
+ return state->pe_masked;
+}
+
int64_t sdei_pe_mask(void)
{
int64_t ret = 0;
diff --git a/services/std_svc/sdei/sdei_main.c b/services/std_svc/sdei/sdei_main.c
index 01cc131..bbc9f73 100644
--- a/services/std_svc/sdei/sdei_main.c
+++ b/services/std_svc/sdei/sdei_main.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2017-2025, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -910,25 +910,54 @@
/* Send a signal to another SDEI client PE */
static int sdei_signal(int ev_num, uint64_t target_pe)
{
+ unsigned int i;
sdei_ev_map_t *map;
+ sdei_ev_map_t *map_priv;
+ sdei_entry_t *se;
/* Only event 0 can be signalled */
- if (ev_num != SDEI_EVENT_0)
+ if (ev_num != SDEI_EVENT_0) {
return SDEI_EINVAL;
+ }
/* Find mapping for event 0 */
map = find_event_map(SDEI_EVENT_0);
- if (map == NULL)
+ if (map == NULL) {
return SDEI_EINVAL;
+ }
/* The event must be signalable */
- if (!is_event_signalable(map))
+ if (!is_event_signalable(map)) {
return SDEI_EINVAL;
+ }
/* Validate target */
- if (!is_valid_mpidr(target_pe))
+ if (!is_valid_mpidr(target_pe)) {
+ return SDEI_EINVAL;
+ }
+
+ /* The event must be unmasked */
+ if (sdei_is_target_pe_masked(target_pe)) {
return SDEI_EINVAL;
+ }
+ /* The event must be registered and enabled */
+ if (is_event_private(map)) {
+ map_priv = SDEI_PRIVATE_MAPPING()->map;
+ for (i = 0; i < SDEI_PRIVATE_MAPPING()->num_maps; i++) {
+ if (map_priv->ev_num == SDEI_EVENT_0) {
+ se = get_event_entry_target_pe((long int) i,
+ (unsigned int) SDEI_PRIVATE_MAPPING()->num_maps, target_pe);
+ if (!(GET_EV_STATE((se), REGISTERED))) {
+ return SDEI_EINVAL;
+ }
+ if (!(GET_EV_STATE((se), ENABLED))) {
+ return SDEI_EINVAL;
+ }
+ }
+ map_priv++;
+ }
+ }
/* Raise SGI. Platform will validate target_pe */
plat_ic_raise_el3_sgi((int) map->intr, (u_register_t) target_pe);
diff --git a/services/std_svc/sdei/sdei_private.h b/services/std_svc/sdei/sdei_private.h
index 44a7301..d48db46 100644
--- a/services/std_svc/sdei/sdei_private.h
+++ b/services/std_svc/sdei/sdei_private.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2017-2025, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -232,13 +232,15 @@
sdei_ev_map_t *find_event_map_by_intr(unsigned int intr_num, bool shared);
sdei_ev_map_t *find_event_map(int ev_num);
-sdei_entry_t *get_event_entry(sdei_ev_map_t *map);
+sdei_entry_t *get_event_entry(const sdei_ev_map_t *map);
+sdei_entry_t *get_event_entry_target_pe(long int mapsub, unsigned int nm, uint64_t target_pe);
int64_t sdei_event_context(void *handle, unsigned int param);
int sdei_event_complete(bool resume, uint64_t pc);
-void sdei_pe_unmask(void);
+bool sdei_is_target_pe_masked(uint64_t target_pe);
int64_t sdei_pe_mask(void);
+void sdei_pe_unmask(void);
int sdei_intr_handler(uint32_t intr_raw, uint32_t flags, void *handle,
void *cookie);