ARM Cortex-M23 Interrupt Target Configuration and HardFault During Secure-to-Non-Secure Transition
The ARM Cortex-M23 processor, with its TrustZone security extension, introduces a robust mechanism for separating secure and non-secure worlds. However, this separation adds complexity to interrupt handling, especially when transitioning interrupts from the secure world to the non-secure world. A common issue arises when configuring the Interrupt Target Non-Secure Register (ITNS) and enabling interrupts, leading to a HardFault. This post delves into the root causes of this behavior, the architectural constraints, and the steps to resolve the issue.
The core problem manifests when attempting to route an interrupt from the secure world to the non-secure world. Specifically, setting the ITNS register (0xE000E380) to target a non-secure handler results in a HardFault. This behavior is observed even when the vector table for the non-secure world is correctly configured. The issue is not limited to a specific peripheral but is a systemic challenge tied to the Cortex-M23’s TrustZone implementation.
Misconfigured Non-Secure Stack Pointer and Memory Attribution
One of the primary causes of the HardFault is the misconfiguration of the non-secure stack pointer (MSP_NS) and memory attribution. The Cortex-M23 requires that the non-secure world has a properly initialized stack pointer before any non-secure interrupt can be handled. If the non-secure world has not booted up or the MSP_NS is not set, the processor will trigger a HardFault when attempting to handle the interrupt in the non-secure world.
Additionally, the memory attribution unit (SAU, Security Attribution Unit) must be configured to mark the non-secure handler’s memory region as non-secure. If the handler resides in secure memory or the memory region is not correctly attributed, the processor will generate a security fault, which escalates to a HardFault. This is particularly critical for the vector table and the interrupt handler code, which must reside in non-secure memory for non-secure interrupts.
Another contributing factor is the improper enabling of interrupts in the secure world for non-secure targets. When an interrupt is targeted to the non-secure world, it must be enabled in the non-secure world, not the secure world. Enabling it in the secure world can lead to a fault condition, as the secure world does not have the authority to handle non-secure interrupts directly.
Analyzing HardFault Registers and Implementing Correct Configuration
To resolve the HardFault issue, follow these detailed steps:
-
Initialize the Non-Secure Stack Pointer (MSP_NS):
Ensure that the non-secure stack pointer is correctly initialized before any non-secure interrupt is enabled. This can be done in the secure world during the boot process. Use the__TZ_set_MSP_NS
function to set the MSP_NS to the appropriate value, typically the top of the non-secure RAM region.__TZ_set_MSP_NS(*((uint32_t *)(NON_SECURE_RAM_START)));
-
Configure the Security Attribution Unit (SAU):
Verify that the SAU is configured to mark the non-secure memory regions correctly. The SAU configuration must include the non-secure vector table and interrupt handler regions. For example, if the non-secure vector table is located at 0x00008000, ensure that this region is marked as non-secure in the SAU.SAU->RNR = 0; // Region number SAU->RBAR = 0x00008000; // Base address SAU->RLAR = 0x0000FFFF | (1 << 0); // Limit address and enable region
-
Enable Interrupts in the Non-Secure World:
Ensure that interrupts targeted to the non-secure world are enabled in the non-secure world, not the secure world. This can be achieved by calling a non-secure callable function from the secure world to enable the interrupt in the non-secure context.void __attribute__((cmse_nonsecure_entry)) enable_nonsecure_irq() { NVIC_EnableIRQ(TRNG_IRQn); }
-
Verify the Interrupt Target Configuration:
Double-check the ITNS register configuration to ensure that the interrupt is correctly targeted to the non-secure world. Use the NVIC_ITNS0 register to set the target state for the specific interrupt.NVIC_ITNS0 |= (1 << TRNG_IRQn); // Target TRNG interrupt to non-secure world
-
Analyze HardFault Registers:
If a HardFault still occurs, analyze the HardFault registers to determine the exact cause. The HardFault Status Register (HFSR), Configurable Fault Status Register (CFSR), and Fault Address Register (MMAR/BFAR) provide detailed information about the fault.uint32_t hfsr = SCB->HFSR; uint32_t cfsr = SCB->CFSR; uint32_t mmfar = SCB->MMFAR; uint32_t bfar = SCB->BFAR;
By following these steps, you can systematically address the HardFault issue when transitioning interrupts from the secure world to the non-secure world on the ARM Cortex-M23. Proper initialization of the non-secure stack pointer, correct SAU configuration, and careful management of interrupt enablement are critical to ensuring reliable operation in a TrustZone environment.