ARM Cortex-M33 TrustZone Configuration and Secure-Non-Secure Transition Issues
The ARM Cortex-M33 processor, as used in the Nucleo L552ZE-Q board, supports ARM TrustZone technology, which provides hardware-enforced isolation between secure and non-secure states. This isolation is crucial for applications requiring robust security, such as IoT devices, where sensitive data and operations must be protected from unauthorized access. However, transitioning between these states can be complex, and misconfigurations often lead to HardFaults. A HardFault in this context typically indicates a violation of the TrustZone security model, such as an unauthorized access attempt or an incorrect state transition.
The Cortex-M33 implements TrustZone through the Security Attribution Unit (SAU) and the Implementation Defined Attribution Unit (IDAU). The SAU divides the memory map into secure and non-secure regions, while the IDAU provides additional hardware-defined security attributes. When a function is called from the secure state to the non-secure state, the processor must ensure that the transition adheres to the security rules defined by the SAU and IDAU. Failure to configure these units correctly, or to use the appropriate function attributes and calling conventions, can result in a HardFault.
In the case of the Nucleo L552ZE-Q, the HardFault occurs when attempting to call a non-secure function from a secure context. This suggests that either the SAU configuration is incorrect, the function pointer is not properly configured for a non-secure call, or the memory map does not align with the security attributes required for the transition. Additionally, the use of the -mcmse
flag and the cmse_nonsecure_call
attribute indicates that the compiler is aware of the need for secure-to-non-secure transitions, but these alone are insufficient if the underlying hardware and memory configurations are not properly set up.
SAU Misconfiguration and Function Pointer Handling in TrustZone
One of the primary causes of HardFaults during secure-to-non-secure transitions is an incorrect SAU configuration. The SAU defines which memory regions are secure and which are non-secure. If the non-secure function resides in a memory region that the SAU marks as secure, the processor will generate a HardFault when attempting to execute the function. Similarly, if the SAU is not enabled or is improperly configured, the processor may not recognize the non-secure call, leading to a security violation.
Another common issue is the improper handling of function pointers for non-secure calls. When calling a non-secure function from a secure context, the function pointer must be modified to indicate the non-secure state. This is typically done by setting the least significant bit (LSB) of the function pointer to 1. If this bit is not set, the processor will attempt to execute the function in the secure state, resulting in a HardFault. Additionally, the cmse_nonsecure_call
attribute must be applied to the function pointer to ensure that the compiler generates the correct instructions for the transition.
The linker script and scatter file configurations also play a critical role in ensuring that the secure and non-secure code is placed in the correct memory regions. If the linker script does not align with the SAU configuration, the processor may attempt to access non-secure code from a secure context, or vice versa, leading to a HardFault. The use of the --import-cmse-lib-out
option is often necessary to generate the correct import libraries for secure and non-secure code, but this can introduce additional complexity if the build system is not properly configured.
Implementing Correct SAU Configuration and Function Pointer Handling
To resolve the HardFault issue, the first step is to verify the SAU configuration. The SAU should be configured to mark the memory regions containing non-secure code as non-secure. This can be done by setting the appropriate bits in the SAU registers. For example, if the non-secure code is located in the FLASH memory region starting at address 0x08000000, the SAU should be configured to mark this region as non-secure. The following table summarizes the SAU configuration for a typical Cortex-M33 application:
SAU Region | Base Address | Size | Security Attribute |
---|---|---|---|
0 | 0x08000000 | 0x00040000 | Non-Secure |
1 | 0x20000000 | 0x00020000 | Non-Secure |
2 | 0x10000000 | 0x00010000 | Secure |
Next, ensure that the function pointer for the non-secure call is correctly configured. The LSB of the function pointer must be set to 1 to indicate the non-secure state. This can be done using the following code:
typedef void (*ns_function_ptr_t)(void) __attribute__((cmse_nonsecure_call));
ns_function_ptr_t ns_function = (ns_function_ptr_t)((uint32_t)non_secure_function | 0x1);
The cmse_nonsecure_call
attribute ensures that the compiler generates the correct instructions for the secure-to-non-secure transition. Additionally, the linker script must be updated to ensure that the secure and non-secure code is placed in the correct memory regions. The following example shows a simplified linker script configuration:
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 256K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}
SECTIONS
{
.text : { *(.text*) } > FLASH
.non_secure_text : { *(.non_secure_text*) } > FLASH
.data : { *(.data*) } > RAM
.bss : { *(.bss*) } > RAM
}
Finally, ensure that the build system is correctly configured to use the --import-cmse-lib-out
option. This option generates the necessary import libraries for secure and non-secure code. If the build system does not support this option, it may be necessary to manually generate the import libraries or modify the build system to include this option.
By carefully configuring the SAU, correctly handling function pointers, and ensuring that the linker script and build system are properly set up, the HardFault issue can be resolved, allowing for successful secure-to-non-secure transitions on the ARM Cortex-M33 processor.