I'm wondering what the correct way to mask non secure interrupts is, on entering secure world on an ARMv8-M processor, with Main and Security extensions. The scenario I have is as follows:
The SOC has 1 M33 core. I have a non secure OS that has an RTOS and schedules threads etc.(RTX RTOS2). The core has 2 Systick timers implemented, 1 for each world. The RTOS configures the non secure timer with priority 0. The secure world sets up AIRCR.PRIS=1 during boot, which means, the NS view of the timer priority is 0, but the view from secure world(SHPR*_NS) is 0x80.
The secure side exposes a bunch of secure function calls, that are called from RTX/NS threads. Some of these functions are not thread safe, so I would like to disable interrupts during execution of these functions, so that the NS timer does not fire and secure world execution is not preempted.
On ARMv8-A, on entry into secure world through SMC, the processor automatically masks interrupts. However, the SG instruction does no such masking. My questions are as follows:
1) Is there a way to make the core automatically boost the current execution priority to mask interrupts on entry into the secure world ? Or is there some banked register, whose state is retained and has the effect of masking interrupts only when executing in a particular world ?
2) I tried setting PRIMASK to 1 and it works, but it seems like I have to set PRIMASK back to 0 before leaving secure world. If not the execution priority does not drop back and the non secure interrupts never fire again. Is this expected behavior? I thought that the PRIMASK would only affect the secure world, since the PRIMASK register is banked between security states.
3) I tried writing the BASEPRI register but the write was ignored, even though the core was in privileged/handler mode. How would I use the BASEPRI register to address my scenario ?
It seems like the best approach to solve my issue would be to implement secure world mutex's that set PRIMASK to 1 on mutex acquire and PRIMASK to 0 on mutex release. Is this the correct approach ? it seems like using BASEPRI to mask non secure interrupts only would be a better approach, in case I have secure interrupts that need to be handled.
There is no automatic way - as you can imagine there are different use cases and setting the interrupt masking automatically could be bad for other scenarios, so this is software controlled. (FAULTMASK can be clear automatically at exception exit but this is not what you wanted).
The banking of interrupt masking registers does NOT mean that when in Secure state, use PRIMASK_S, and when in Non-secure state, use PRIMASK_NS. Both of them can affect software in the other world, as they set the current priority level to specific level (just like an interrupt that is being service, its current priority level is visible in both worlds. In your case, since AIRCR.PRIS is set, then PRIMASK_NS raise current priority level to 0x80. Setting PRIMASK_S raises current priority level to 0x0.
If you need to block all Non-secure interrupts, you can set BASEPRI_S to 0x80 (because you have PRIS set to 1, the highest Non-secure interrupt is in priority level 0x80).
Regarding (3), can you show the BASEPRI code, and what is the width of the priority level registers? (Note: __set_BASEPRI() does not shift the register before writing, unlike NVIC_SetPriority().)
Thanks for your response Joseph. Width was the issue for BASEPRI. We were using inline assembly to write to it. We were using the lower bits which were tied to 0. Once we used the bits that were implemented, we were able to use it as expected!
View all questions in TrustZone for Armv8-M forum