Article count:561 Read by:1105636

Account Entry

STM32 TrustZone development and debugging skills | HardFault debugging and processing

Latest update time:2023-11-29
    Reads:
Click above to follow STM32

Introduction

In the first two articles on STM32 TrustZone development and debugging skills , we introduced the kernel's SAU/IDAU, address security attribute configuration, resource security attribute configuration, kernel security rules for accessing resources, and common peripherals used in the TrustZone environment. questions, etc. Another common problem you may encounter when developing a TrustZone environment is software-triggered failure errors. The exception model and Fault processing in the TrustZone environment of the ARM CM33 kernel have many changes from those without security extensions. Once a HardFault occurs, inexperienced developers may not have a clue and don't know where to start to find the problem. Therefore, the focus of this article will be around the exception model in the CM33 TrustZone environment and the debugging and processing of HardFault for developers' reference.

1. Anomaly model under CM33 TrustZone architecture

In the second article of STM32 TrustZone development and debugging skills, we introduced the interrupts and interrupt vector tables on the S and NS sides of CM33 with security extensions, so we will not go into details here. Table 1 summarizes the Fault exceptions.

1.1. Fault exception type (with security extension)
1.1.1. Hard Fault

HardFault is the default Fault exception and is always enabled. The reason for triggering may be that the exception handling itself triggers an error, or an exception cannot be handled by other mechanisms and rises to HardFault. It has a higher priority than all other exceptions with configurable priority.

In a TrustZone environment, HardFault is not Bank's. The same exception will either trigger HardFault on the S side or trigger HardFault on the NS side. SCB's AIRCR.BFHFNMINS determines whether to enable NS's BusFault, HardFault and NMI. If AIRCR.BFHFNMINS=0 of SCB, HardFault always triggers the HardFault Handler on the S side; if AIRCR.BFHFNMINS=1, the fault may trigger the HardFaultHandler on the NS side or the HardFault Handler on the S side. Figure 1 shows the general situation of HardFault Handler triggering when other faults are not enabled.

It should be noted that even if AIRCR.BFHFNMINS=1, exceptions originally targeted to the S side and raised to HardFault will still trigger HardFault on the S side. They are not affected by the AIRCR.BFHFNMINS bit, such as when security code violates MPU protection. According to the rules, when a MemManage error occurs, even if AIRCR.BFHFNMINS=1, the fault will still enter the Secure HardFault Handler. The HardFault on the NS side may be triggered only when AIRCR.BFHFNMINS=1.

Also note that the AIRCR register cannot be modified directly. You need to write the Key value first to change the register content. The sample code to set or clear the AIRCR.BFHFNMINS bit is as follows (can only be used in secure code):
void SECURE_SetNMIHFBFTarget(int NS){ uint32_t reg_value;uint32_t target = (NS==1)?1:0;/* read old register configuration */reg_value = SCB->AIRCR; /* clear bits to change */reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_BFHFNMINS_Msk)); /* insert write key and target bit */reg_value = (reg_value | ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | (target << SCB_AIRCR_BFHFNMINS_Pos) ); SCB->AIRCR = reg_value;}

Note : Sometimes, software may need to set the AIRCR.PRIS bit to reduce the overall priority of NS interrupts (for example, this mechanism is used in the implementation of TF-M). At this time, if AIRCR.PRIS=1 and AIRCR.BFHFNMINS=1 are set at the same time, the behavior of the kernel will be unpredictable. Therefore, if you need to set AIRCR.PRIS=1, it is recommended to keep AIRCR.BFHFNMINS=0.

1.1.2. Bus Fault

Bus Fault usually occurs during instruction or data access, and may be caused . Bus Fault is not enabled by default, which means that a bus fault will trigger the HardFault Handler by default. If you need to enable Bus Fault separately, you can set the SHCSR.BUSFAULTENA bit of SCB to 1.

In the TrustZone environment, the Bus Fault does not belong to the Bank. Whether the BusFault Handler on the S or NS side is triggered is related to AIRCR.BFHFNMINS of SCB. If AIRCR.BFHFNMINS=0, BusFault always targets the S safe state; otherwise, if AIRCR.BFHFNMINS=1, the target is NS non-safe state.

When a Bus error occurs, which Fault Handler will actually be triggered will depend on the settings of AIRCR.BFHFNMINS and SHCSR.BUSFAULTENA of SCB_S/NS. Figure 2 shows the general situation where a Bus error triggers the Fault Handler (for example, the situation where the safe side Vector error still rises to Secure HardFault is not considered here).

Normally, bus fault information is marked in the CFSR/BFSR and BFAR registers of the SCB. In the TrustZone environment, some registers of the SCB and some bits of the register belong to the Bank. The respective SCB registers can be seen from both the secure side and the non-secure side, but the BFSR field of the CFSR register and the BFAR register do not belong to the Bank. The Bus fault may target the S safe side or the NS non-safe side. When a bus error occurs, if the Bus Fault information is read from the relevant registers of SCB_S and SCB_NS respectively, different results can be seen.

If AIRCR.BFHFNMINS=0, only the secure side can see the real data of BFSR and BFAR. If the non-secure side reads BFSR, BFAR, or reads BFSR_NS from the secure side, BFAR_NS can only read the value of all 0s.

If AIRCR.BFHFNMINS=1, the values ​​of BFAR_NS and BFAR_S will generally read the same value. Usually, when the code needs to handle BusFault, if the default configuration is used, that is, keeping the BusFault target to the S side and AIRCR.BFHFNMINS=0, the Fault Handler can obtain the bus fault information from the CFSR.BFSR and BFAR registers of SCB_S; and if AIRCR is set .BFHFNMINS=1, then when a Bus error occurs, the Fault Handler on the non-safety side can directly obtain fault information from the CFSR.BFSR and BFAR registers of SCB_NS.

BusFault is not enabled separately by default. If you need to enable BusFault, you can set the BUSFAULTENA bit in the SHCSR register. The sample code to enable or disable BusFault is as follows:
void EnableBusFault(int enable){ if( enable == 1) SCB->SHCSR |= SCB_SHCSR_BUSFAULTENA_Msk; else SCB->SHCSR &= (~SCB_SHCSR_BUSFAULTENA_Msk);}

This code is the same for both the security and non-security sides, but it should be noted that since BusFault does not belong to the Bank, when AIRCR.BFHFNMINS=0, this code can only be used on the security side, that is, the S security side is modified. BusFault of SCB SHCSR, writing the SHCSR.BUSFAULTENA bit of SCB_NS is invalid at this time.

If the non-secure side application uses this code to enable BusFault, the premise is that the secure side has set AIRCR.BFHFNMINS=1.

1.1.3. Usage Fault

UsageFault is related to errors during instruction execution, including undefined instructions, non-aligned access, invalid status when executing instructions, errors during interrupt return, division by 0, etc.

In the TrustZone environment, UsageFault belongs to Bank, so respective UsageFault may occur in S and NS states, and respective S UsageFault Handler and NS UsageFault Handler may be triggered. UsageFault is not enabled by default, so it will rise to HardFault by default. Whether to trigger the S or NS HardFault Handler depends on whether the value of AIRCR.BFHFNMINS is 0 or 1.

Enabling UsageFault requires setting SHCSR.USGFAULTENA of SCB_S and SCB_NS respectively. SHCSR.USGFAULTENA=1 of SCB_S is used to enable Usage Fault on the S safe side; SHCSR.USGFAULTENA=1 of SCB_NS is used to enable Usage Fault on the NS non-safe side.

In addition, usually the division by 0 operation will not trigger UsageFault. If you want the division by 0 operation to trigger UsageFault, you need to set the CCR.DIV_0_TRP bit corresponding to SCB_S/NS to 1.

Figure 3 summarizes the general situation in which Usage errors trigger the Fault Handler.

As long as SHCSR.USGFAULTENA=1, UsageFault will always trigger the UsageFault Handler corresponding to the security state of the software. Otherwise, it will rise to HardFault. UsageFault on the security side will always rise to Secure HardFault. For UsageFault on the non-security side, if AIRCR.BFHFNMINS=0, it rises to Secure HardFault, otherwise it rises to Non-Secure HardFault.

The sample code to enable or disable UsageFault is as follows:
void EnableUsageFault(int enable){ if( enable == 1) {SCB->SHCSR |= SCB_SHCSR_USGFAULTENA_Msk;/* Enable divide by 0 trap to trigger usage fault (if necessary) */SCB->CCR |= SCB_CCR_DIV_0_TRP_Msk; } else SCB->SHCSR &= (~SCB_SHCSR_USGFAULTENA_Msk);}
If UsageFault needs to be enabled on both the security and non-security sides, the S and NS codes can call this code to enable their respective UsageFault, or the S security side code can also directly control the enabling of UsageFault on the NS non-security side, for example Add the following code on the S security side to determine whether UsageFault on the NS side is enabled.
void EnableNSUsageFault(int enable){ if( enable == 1) SCB_NS->SHCSR |= SCB_SHCSR_USGFAULTENA_Msk; else SCB_NS->SHCSR &= (~SCB_SHCSR_USGFAULTENA_Msk);}
1.1.4. MemManage Fault

MemManage Fault is a fault exception caused by Memory protection, such as violating the access rules defined by the MPU region when fetching instructions or performing data access, or violating the default address protection rules.

MemManageFault is similar to UsageFault and is not enabled by default. If you want to enable MemManageFault on the S or NS side, you need to set the SHCSR.MEMFAULTENA bit of SCB_S or SCB_NS accordingly.

In addition, similar to UsageFault, MemManageFault is also Bank on the S and NS sides, that is, S and NS have their own MemManageFault. Since the MPU unit itself belongs to the Bank, there are two sets of MPU registers MPU_S and MPU_NS in the system. Therefore, the code on the S and NS sides can each define its own MPU region and use different configurations. That is to say, even for the same address, S/NS Both sides can also define different access rules through their respective MPU units. The protection rules configured in MPU_S only apply to the S security side code, that is, to control access when the CPU is in a safe state. This has nothing to do with the security attributes defined in the SAU for the addresses accessed by the CPU. The protection rules configured in MPU_NS only apply to NS non-safe side code, that is, access when the CPU is in a non-safe state, and the two do not affect each other.

Figure 4 shows the general situation of MemManage failure triggering Fault Handler. If the S security code violates the memory access rules, MemManageFault or Secure HardFault on the security side may be triggered. Non-security code that violates memory access rules may trigger MemManageFault on the non-security side, or rise to HardFault. If AIRCR.BFHFNMINS=0, it will rise to Secure HardFault, otherwise it will rise to Non-Secure HardFault.
The sample code to enable or disable MemManage Fault is as follows:
void EnableMemoryFault(int enable){ if( enable == 1) SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk; else SCB->SHCSR &= (~SCB_SHCSR_MEMFAULTENA_Msk);}
If both the security and non-security sides need to enable MemManage Fault, the S and NS applications can call this code to enable their respective MemManage Fault, or the S security side code can also directly enable the NS non-security side MemManage Fault. For example, you can add the following code on the S security side to control the MemManageFault enablement on the NS side.
void EnableNSMemoryFault (int enable){ if( enable == 1) SCB_NS->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk; else SCB_NS->SHCSR &= (~SCB_SHCSR_MEMFAULTENA_Msk);}

In addition, if the code uses the HAL API to enable the MPU, that is, calls HAL_MPU_Enable(), then MemManage Fault will be automatically enabled in the MPU enabling function. At this time, there is no need to call the previously mentioned code to separately enable MemManage Fault.

1.1.5. Secure Fault

Secure Fault only exists in an environment where TrustZone is enabled. SecureFault may be triggered due to various security checks in the kernel, such as jumping from NS to S code without entering from the SG entry instruction, or non-security code trying to access the safe address range specified by SAU/IDAU. Usually when a SecureFault occurs, the software can directly stop or reset the system. This can avoid introducing security vulnerabilities as much as possible.

SecureFault is not Bank-specific and always targets the S side, so only security code can handle SecureFault. SecureFault is not enabled by default. When a Secure error occurs, SecureHardFault is triggered by default. Software can individually enable SecureFault by setting SHCSR.SECUREFAULTENA. After enabling, Secure errors will trigger the SecureFault Handler. Figure 5 shows the general situation when a Secure error triggers the Fault Handler.
The sample code to enable or disable SecureFault is as follows:
void EnableSecureFault(int enable){ if( enable == 1) SCB->SHCSR |= SCB_SHCSR_SECUREFAULTENA_Msk; else SCB->SHCSR &= (~SCB_SHCSR_SECUREFAULTENA_Msk);}


1.2. Fault upgrade and HardFault

Except for HardFault, other fault types have configurable priorities. Software can disable a certain priority-configurable fault exception, but cannot disable HardFault. The priority of the fault exception and the corresponding mask bit determine whether the kernel will enter the handler of a certain fault, and whether a certain fault can preempt another fault.

In some cases, a fault with configurable priority may be treated as a HardFault, that is, the fault is escalated or the petition is interrupted. At this time, this specific Fault will be upgraded to a HardFault fault.
There may be many reasons why a Fault is upgraded to a HardFault, such as:
  • This fault Fault is not enabled;

For example, the code generates a UsageFault by executing an undefined instruction, but UsageFault is not enabled.
  • The fault's FaultHandler priority is not high enough to run;

For example, the system configures and enables the MPU, and the CPU is executing an interrupt operation. When the operation attempts to access an address, it violates the access rules defined by the MPU, thus triggering a fault. However, the priority of the currently executed interrupt is higher than the MemManage fault. priority.
  • The same fault occurred in the faulty FaultHandler;

For example, undefined instructions occurred again in the handler that handles UsageFault.
If the stack push operation causes BusFault when entering the BusFault Handler, in this case BusFault will not be upgraded to HardFault. This means that if a corrupt stack causes a fault, even if the Fault Handler fails to push the stack, the fault handler will still execute, but the stack contents will be corrupted.
Only NMI can preempt HardFault, and HardFault can preempt any exception except Reset, NMI or another HardFault. When BFHFNMINS=1, if the NMI Handler on the NS side generates a security violation error, it will trigger Secure HardFault and be preempted by it.

Bus errors that occur when obtaining the exception vector are always upgraded to HardFault and are handled by HardFault instead of BusFault.

1.3. Fault abnormal security state
In a TrustZone-enabled environment, fault exceptions may target the S safe state or the NS non-safe state, which will cause the behavior of the ARMv8-M kernel to be very different from the previous ARMv6-M and ARMv7-M kernels. TrustZone environment software development Special attention should be paid to this when dealing with Fault.
The situation of Fault exception targeting S and NS has been mentioned in the previous article when introducing several Fault types. It is summarized in Table 2 here.

1.4. Exception entry and return
1.4.1. Abnormal entry and stack frame
When the processor is in thread mode and there is a pending exception with sufficient priority in the system, an exception will be entered, or the priority of the new exception is higher than the exception being processed. At this time, the new exception will preempt the original exception, that is, exception nesting occurs.
When an exception occurs in the processor, unless the exception is a tail chain exception or a delayed arrival exception, the processor will push the context information onto the stack, and the data structure pushed onto the stack is the stack frame.
Usually the content of the stack frame is shown in Figure 6 (a), including the contents . In a TrustZone-enabled environment, if S security code execution is preempted by an NS non-security exception, more information will be pushed onto the stack before entering the non- security exception, as shown in Figure 6 (b), and the hardware will automatically Registers pushed onto the stack are cleared, preventing any safe state data from being exposed to unsafe code.
If the floating point function is used and a floating point context exists, the kernel will automatically push the floating point related context content onto the stack. Since the data content of the floating point part is not very helpful for our usual Fault debugging, we will not go into details here.
Whether MSP or PSP is used for the stack frame push operation before entering the exception depends on the running status of the kernel and the stack used at that time. If the CPU is running in Handler mode at that time, MSP is used to push the stack frame; if the CPU is running in Thread mode, the stack used is marked by the CPU CONTROL.SPSEL bit at that time.

▼▼▼


The length of the WeChat tweet is limited, click the button to download the full text of "STM32 TrustZone Development and Debugging Tips | Frequently Asked Questions on Peripheral Use" .

© THE END


Service number

Follow STM32

Video number


Station B account
Click "Read the original text" to learn more
Your sharing, likes, and watching
I like all of them


Latest articles about

 
EEWorld WeChat Subscription

 
EEWorld WeChat Service Number

 
AutoDevelopers

About Us Customer Service Contact Information Datasheet Sitemap LatestNews

Room 1530, Zhongguancun MOOC Times Building,Block B, 18 Zhongguancun Street, Haidian District,Beijing, China Tel:(010)82350740 Postcode:100190

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号