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!
I have followup question on this topic. The usecase is same wherein all the NS Interrupts in Secure world needs masking.
Followed the same approach as described in ARM v8M. But the Interrupts not fired in NS. For test purpose, the NS application only has infinite while loop and nothing else.Do i need to explicitly initialize any banked registers(VTOR) for the NS Interrupt to fire and any additional registers to consider for NS Interrupts.
In the sequence described below, neither the interrupts fire in NS world nor the Secure Faults trigerred.
For Ex: relevent parts of the Test code in Boot sequence(secure) is shown below.
BASEPRI_S = 0x80;
/* Setting up the Int pend for ex ExtInt#6 */
/* Setup Stack pointers for NS and branching to simple NS code with a infinite while loop
I've just have a quick look and doesn't know if you have program ITNS to set IRQ#6 as Non-secure (it might have been done in SystemInit() but it is not clear.
Yes, you do need to have both Secure vector table and Non-secure vector table (and both VTOR_S and VTOR_NS need to be configured correctly - this might be done at hardware level).I am travelling right now and will be in Embedded World next week so won't be able to follow up quickly. If your company is support contact with Arm, please contact Arm support team if you have anything urgent.
Not using SystemInit() but have a customised init.
Yes both the VTOR_S and VTOR_NS are setup before entering Normal world. This i verified by trigerring the interrupts within the secure world withour boosting priority.
But with Boosting priorityWhat could prevent the "NS Interrupt pended in Secure world( due to priority boosting) to fire on entering the NS world" ?
Can you route this converstaions to the appropriate contact in your absence. Alternatively will try to establish contact with ARM support from the company.
Simply modified the ARMV8M_TZ example project of bare metal secure and non-secure software with FVP simulation as follows
Setup AIRCR.PRIS=1, BASEPRI_S=0x80 and setup UART1 interrupt to Non-secure CPU( ITNS, Enable and Setpending) Setup the VTOR_NS and NS Main ( just loop).
Observed: NS Interrupt pending bit is set but not serviced
In the NVIC window from Keil: Modified the BASEPRI_S = 0, the pended NS interrupt got serviced. This is not the expected behaviour because NS should refer only to BASEPRI_NS which is 0x00 indicating no interrupt masking in NS CPU.
Thanks and Regards,sanjeev.
Just read through you message again and realized you might have misunderstood one thing:
The interrupt priority level space is "shared" between Secure and Non-secure world:
- Secure masking registers can affect both Secure and Non-secure interrupts. Effective masking level in Non-secure side is adjusted based on value of AIRCR.PRIS.
- Non-secure masking registers can also affect Secure and Non-secure interrupts. Effective masking level in Secure side is adjusted based on value of AIRCR.PRIS.
Hence setting PRIS to 1 and BASEPRI_S to 0x80 means blocking Non-Secure interrupts of priority level 0x00 to 0xFF.
If BASEPRI_NS is set to 0x80, and if PRIS is set to 1, it blocks Secure interrupts with priority levels 0xC0 to 0xFF.
So your observation is normal.The banking of these masking registers is needed as Non-secure ISRs can change the values of the Non-secure masking registers, so if a Secure process need to have the priority level masking protected, it cannot rely on the Non-secure BASEPRI/PRIMASK to handle the masking.
Regarding support channel, assumed that you work for a semiconductor company that licensed Cortex-M33, then Arm support is the best way to handle your enquiries. Arm community page is not a replacement for support.
Thanks for the response. That explanation pretty much explains everything. The ARM manuals maybe improved with such explanations.
Yes, since the settings of BASEPRI/PRIMASK can manipulate the priority level masking in either world, the best way of handling seems to be manipulating the settings of BASEPRI/PRIMASK at the entry and exit points of secure code.
Ex: BASEPRI_S = 0x80 at entry and BASEPRI_S = 0x00 at the exit of the secure code.
View all questions in TrustZone for Armv8-M forum