BL31: Program Priority Mask for SMC handling
On GICv3 systems, as a side effect of adding provision to handle EL3
interrupts (unconditionally routing FIQs to EL3), pending Non-secure
interrupts (signalled as FIQs) may preempt execution in lower Secure ELs
[1]. This will inadvertently disrupt the semantics of Fast SMC
(previously called Atomic SMC) calls.
To retain semantics of Fast SMCs, the GIC PMR must be programmed to
prevent Non-secure interrupts from preempting Secure execution. To that
effect, two new functions in the Exception Handling Framework subscribe
to events introduced in an earlier commit:
- Upon 'cm_exited_normal_world', the Non-secure PMR is stashed, and
the PMR is programmed to the highest Non-secure interrupt priority.
- Upon 'cm_entering_normal_world', the previously stashed Non-secure
PMR is restored.
The above sequence however prevents Yielding SMCs from being preempted
by Non-secure interrupts as intended. To facilitate this, the public API
exc_allow_ns_preemption() is introduced that programs the PMR to the
original Non-secure PMR value. Another API
exc_is_ns_preemption_allowed() is also introduced to check if
exc_allow_ns_preemption() had been called previously.
API documentation to follow.
[1] On GICv2 systems, this isn't a problem as, unlike GICv3, pending NS
IRQs during Secure execution are signalled as IRQs, which aren't
routed to EL3.
Change-Id: Ief96b162b0067179b1012332cd991ee1b3051dd0
Signed-off-by: Jeenu Viswambharan <jeenu.viswambharan@arm.com>
diff --git a/include/bl31/ehf.h b/include/bl31/ehf.h
index 142b4c0..be8c957 100644
--- a/include/bl31/ehf.h
+++ b/include/bl31/ehf.h
@@ -55,6 +55,9 @@
/* Priority mask value before any priority levels were active */
uint8_t init_pri_mask;
+
+ /* Non-secure priority mask value stashed during Secure execution */
+ uint8_t ns_pri_mask;
} __aligned(sizeof(uint64_t)) pe_exc_data_t;
typedef int (*ehf_handler_t)(uint32_t intr_raw, uint32_t flags, void *handle,
@@ -79,6 +82,8 @@
void ehf_activate_priority(unsigned int priority);
void ehf_deactivate_priority(unsigned int priority);
void ehf_register_priority_handler(unsigned int pri, ehf_handler_t handler);
+void ehf_allow_ns_preemption(void);
+unsigned int ehf_is_ns_preemption_allowed(void);
#endif /* __ASSEMBLY__ */
diff --git a/include/drivers/arm/gic_common.h b/include/drivers/arm/gic_common.h
index 9e126a8..efa9703 100644
--- a/include/drivers/arm/gic_common.h
+++ b/include/drivers/arm/gic_common.h
@@ -34,10 +34,10 @@
#define GIC_INTR_CFG_EDGE 1
/* Constants to categorise priorities */
-#define GIC_HIGHEST_SEC_PRIORITY 0
-#define GIC_LOWEST_SEC_PRIORITY 127
-#define GIC_HIGHEST_NS_PRIORITY 128
-#define GIC_LOWEST_NS_PRIORITY 254 /* 255 would disable an interrupt */
+#define GIC_HIGHEST_SEC_PRIORITY 0x0
+#define GIC_LOWEST_SEC_PRIORITY 0x7f
+#define GIC_HIGHEST_NS_PRIORITY 0x80
+#define GIC_LOWEST_NS_PRIORITY 0xfe /* 0xff would disable all interrupts */
/*******************************************************************************
* GIC Distributor interface register offsets that are common to GICv3 & GICv2