ARM Cortex-M33 Non-Secure Mode Execution Behavior and NOP-Like Symptoms
When attempting to execute a test program on the ARM Cortex-M33 processor using the DS-5 MPS2 Fixed Virtual Platform (FVP) in non-secure mode, the program fails to execute instructions correctly. Instead, the processor appears to treat every instruction as a NOP (No Operation), with only the Program Counter (PC) incrementing by 2 after each instruction. This behavior suggests that the processor is not executing the intended operations, such as branch instructions or register updates, and instead is effectively stalling or ignoring the instructions.
The test program is loaded at address 0x800, and the FVP parameters are configured to enable non-secure mode by setting cpu0.SAU=0
and cpu0.SAU_CTRL.ALLNS=1
. Despite these configurations, the program does not execute as expected. The code includes a non-secure function call (nsfunc
) and attempts to initialize the non-secure world by modifying the Vector Table Offset Register (VTOR) and the Main Stack Pointer (MSP) for the non-secure environment. However, the program fails to transition correctly into non-secure mode, and the printf
statement in the main
function is reached, indicating an unexpected return from the non-secure initialization routine.
This issue is indicative of a misconfiguration or misunderstanding of the ARM Cortex-M33’s security architecture, particularly in the transition between secure and non-secure states. The Cortex-M33 implements the ARMv8-M architecture, which introduces the concept of TrustZone for ARMv8-M, enabling hardware-enforced isolation between secure and non-secure states. The Security Attribution Unit (SAU) and the Implementation Defined Attribution Unit (IDAU) play critical roles in defining the memory and peripheral security attributes, and any misconfiguration in these units can lead to unexpected behavior, such as the observed NOP-like execution.
Misconfigured SAU, Faulty VTOR Setup, and Incorrect Non-Secure Call Handling
The root cause of the issue lies in a combination of misconfigurations and potential oversights in the setup of the non-secure environment. The primary factors contributing to the problem include:
-
Misconfigured Security Attribution Unit (SAU): The SAU is responsible for defining the memory regions as secure or non-secure. While the FVP parameters
cpu0.SAU=0
andcpu0.SAU_CTRL.ALLNS=1
are set to enable non-secure mode, these settings alone may not be sufficient to ensure proper execution. The SAU configuration must align with the memory map of the application, and any mismatch can result in the processor treating instructions as NOPs due to access violations or incorrect security attributes. -
Faulty Vector Table Offset Register (VTOR) Setup: The VTOR in the non-secure world must point to a valid vector table in non-secure memory. In the provided code, the VTOR is set to
0x30000000
, and the MSP and reset vector are manually updated. However, this setup may not account for the alignment requirements of the VTOR or the proper initialization of the non-secure stack pointer. Additionally, the use of__TZ_set_MSP_NS
is commented out, which may prevent the correct initialization of the non-secure stack pointer. -
Incorrect Non-Secure Call Handling: The non-secure function call (
nsfunc
) is defined using thecmse_nonsecure_call
attribute, but the function pointer is derived from the VTOR’s reset vector. This approach may not correctly handle the transition to non-secure state, as the function pointer must be validated and the transition must adhere to the ARMv8-M security model. The lack of proper validation or incorrect handling of the non-secure call can lead to the processor executing instructions in an undefined state. -
Potential Memory Access Violations: The program attempts to access memory locations such as
0xE002ED08
and0x30000000
without ensuring that these regions are correctly mapped and accessible in non-secure mode. If these memory regions are not properly configured in the SAU or IDAU, the processor may generate bus faults or memory management faults, resulting in the observed NOP-like behavior. -
Insufficient Synchronization and Barrier Usage: The ARMv8-M architecture requires the use of synchronization barriers to ensure proper ordering of memory accesses and state transitions. The absence of Data Synchronization Barriers (DSB) or Instruction Synchronization Barriers (ISB) after modifying critical registers such as the VTOR or MSP can lead to unpredictable behavior, as the processor may not immediately apply the changes.
Correcting SAU Configuration, Validating VTOR Setup, and Ensuring Proper Non-Secure Transitions
To resolve the issue and ensure proper execution of the test program in non-secure mode, the following steps should be taken:
-
Verify and Correct SAU Configuration: The SAU must be configured to define the appropriate memory regions as non-secure. This includes ensuring that the memory regions containing the test program, stack, and vector table are marked as non-secure in the SAU and IDAU. The SAU configuration can be verified by inspecting the SAU regions and their attributes using the FVP debugger or by adding debug prints in the code to check the SAU settings.
-
Validate VTOR Setup and Stack Initialization: The VTOR in the non-secure world must point to a valid vector table in non-secure memory. The VTOR address must be aligned to the vector table size, and the MSP must be correctly initialized. The
__TZ_set_MSP_NS
function should be used to set the non-secure stack pointer, and the VTOR should be updated using the__TZ_set_VTOR_NS
function. Additionally, the reset vector in the non-secure vector table must point to a valid entry point in the non-secure code. -
Ensure Proper Non-Secure Call Handling: The non-secure function call must be validated using the
cmse_nsfptr_create
function to ensure that the function pointer is valid and points to a non-secure address. The transition to non-secure state should be performed using thecmse_nonsecure_entry
function, which ensures that the processor correctly switches to non-secure state and validates the function pointer. -
Check Memory Access Permissions: The memory regions accessed by the program, such as
0xE002ED08
and0x30000000
, must be verified to ensure that they are accessible in non-secure mode. This includes checking the SAU and IDAU configurations to ensure that these regions are marked as non-secure. If necessary, the memory map should be updated to include these regions in the non-secure address space. -
Add Synchronization Barriers: After modifying critical registers such as the VTOR or MSP, synchronization barriers must be added to ensure that the changes are applied immediately. A Data Synchronization Barrier (DSB) should be used after updating the VTOR or MSP, followed by an Instruction Synchronization Barrier (ISB) to ensure that the processor fetches the updated instructions. This can be achieved using the
__DSB()
and__ISB()
intrinsics provided by the ARM CMSIS headers. -
Debug and Verify Execution: The execution of the program should be debugged using the FVP debugger to verify that the processor correctly transitions to non-secure mode and executes the instructions as expected. Breakpoints can be set at key points in the code, such as the entry to the non-secure function, to verify that the transition is successful. Additionally, the debugger can be used to inspect the SAU, VTOR, and MSP registers to ensure that they are correctly configured.
By following these steps, the issue of NOP-like execution in non-secure mode can be resolved, and the test program can be executed correctly on the ARM Cortex-M33 processor using the DS-5 MPS2 FVP. Proper configuration of the SAU, validation of the VTOR setup, and correct handling of non-secure transitions are critical to ensuring reliable execution in non-secure mode. Additionally, the use of synchronization barriers and thorough debugging can help identify and resolve any remaining issues in the setup.