From ba0bcaf41b470c7a573ebf4c6ccfe31fad94f4a1 Mon Sep 17 00:00:00 2001 From: Stephanos Ioannidis Date: Tue, 17 Mar 2020 09:27:33 +0900 Subject: [PATCH] arch: arm: aarch32: Fix arch_cpu_idle interrupt masking The current AArch32 `arch_cpu_idle` implementation enables interrupt before executing the WFI instruction, and this has the side effect of allowing interruption and thereby calling wake-up notification functions before the CPU enters sleep. This commit fixes the problem described above by ensuring that interrupt is disabled when the WFI instruction is executed and re-enabled only after the processor wakes up. For ARMv6-M, ARMv8-M Baseline and ARM-R, the PRIMASK (ARM-M)/ CPSR.I (ARM-R) is used to lock interrupts and therefore it is not necessary to do anything before executing the WFI instruction. For ARMv7-M and ARMv8-M Mainline, the BASEPRI is used to lock interrupts and the PRIMASK is always cleared in non-interrupt context; therefore, it is necessary to set the PRIMASK to mask interrupts, before clearing the BASEPRI to configure wake-up interrupt priority to the lowest. Signed-off-by: Stephanos Ioannidis --- arch/arm/core/aarch32/cpu_idle.S | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/arch/arm/core/aarch32/cpu_idle.S b/arch/arm/core/aarch32/cpu_idle.S index e1b9a87cc99..ca5ff63c638 100644 --- a/arch/arm/core/aarch32/cpu_idle.S +++ b/arch/arm/core/aarch32/cpu_idle.S @@ -62,19 +62,36 @@ SECTION_FUNC(TEXT, arch_cpu_idle) #endif /* CONFIG_ARMV6_M_ARMV8_M_BASELINE */ #endif /* CONFIG_TRACING */ -#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) \ - || defined(CONFIG_ARMV7_R) - cpsie i -#elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) - /* clear BASEPRI so wfi is awakened by incoming interrupts */ +#if defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) + /* + * PRIMASK is always cleared on ARMv7-M and ARMv8-M Mainline (not used + * for interrupt locking), and configuring BASEPRI to the lowest + * priority to ensure wake-up will cause interrupts to be serviced + * before entering low power state. + * + * Set PRIMASK before configuring BASEPRI to prevent interruption + * before wake-up. + */ + cpsid i + + /* Set wake-up interrupt priority to the lowest */ eors.n r0, r0 msr BASEPRI, r0 #else -#error Unknown ARM architecture -#endif /* CONFIG_ARMV6_M_ARMV8_M_BASELINE */ + /* + * For all the other ARM architectures that do not implement BASEPRI, + * PRIMASK is used as the interrupt locking mechanism, and it is not + * necessary to set PRIMASK here, as PRIMASK would have already been + * set by the caller as part of interrupt locking if necessary + * (i.e. if the caller sets _kernel.idle). + */ +#endif /* CONFIG_ARMV7_M_ARMV8_M_MAINLINE */ wfi + /* Clear PRIMASK to service any pending interrupt */ + cpsie i + bx lr SECTION_FUNC(TEXT, arch_cpu_atomic_idle)