You copied the Doc URL to your clipboard.

How do I test the memory fault handler in a Cortex-M system?

Information in this article applies to:

  • Cortex-M3
  • Cortex-M4
  • Cortex-M7
  • Cortex-M33

Problem/Question

How do I test the memory fault handler in a Cortex-M system?

Scenario

To test the handling of a memory fault (MemManage or BusFault), use an LDR or STR assembly instruction to a faulting address. The LDR and STR instructions always take a register as their base address pointer, for example, [r0]. The faulting access causes the exception and, if the fault is enabled, the handler is called. Typically, LDR is used for this purpose because it always produces a precise error.

The return address for a precise memory fault is the address of the faulting instruction. The fault handler might try to fix the problem, or might take some different action such as requesting that the current task is killed. If the handler tries to fix the problem, then this return address allows the same instruction to be retried.

The fault handler in a test program might record that the handler has been run and then change the system so that the retried access now succeeds. You can change the base address pointer within the handler so that it points to a non-faulting address on the retry.

However, changing [r0] directly does not work.

Answer

The processor architecture defines that registers r0-r3 and r12 are preserved by the exception handling mechanism. For this reason, any change of these register values inside an exception handler will not be visible to the code that was interrupted by the exception.

The simple way to let the handler change the address is to use a base address register in the range r5-r11. These registers are not automatically preserved by the exception handling mechanism. If the handler code does not include the explicit push and pop of these registers, any changes to their values can be seen by the interrupted code.

Alternative methods of adjusting the system (such as finding and modifying the stacked copy of r0 or of the return address, or modifying the instruction opcode) are more complicated.

To do this, you need to identify whether the exception stack frame is on the Main stack or Process stack, and then locate the register that you want to modify in that stack frame.

If you want to modify the opcode, or you want to step directly to the next instruction, you must also identify the size of the opcode by reading the opcode as data, and testing the high order bits of the opcode. 32-bit opcodes start with a 16-bit prefix portion that has 3'b111 in bits[15:13], and a non-zero value in bits[12:11].

The following code fragment in Arm Assembler illustrates a method for identifying the correct stack frame and modifying the ReturnAddress in that stack frame to skip to the next instruction:

        ; Return to next instruction. Check instruction size
        EXPORT  fault_next_instruction 
fault_next_instruction
        TST     lr,#4               ; Thread using PSP or MSP ?
        MRSNE   r0,PSP
        MOVEQ   r0,sp
        LDR     r1, [r0,#24]        ; pc
	LDRH    r2,[r1]             ; instruction at PC
        UBFX    r3,r2,#13,#3        ; Check if 16-bit or 32-bit instruction
        CMP     r3,#7
        BNE     instr_16bit
        UBFX    r3,r2,#11,#2
        CMP     r3,#0
        BNE     instr_32bit 
instr_16bit 
        ADDS    r1,r1,#2
        STR     r1,[r0,#24]
        BX      lr    
instr_32bit                
        ADDS    r1,r1,#4
        STR     r1,[r0,#24]
        BX      lr 
		

Workaround

N/A

Example

N/A

Related Information

Was this page helpful? Yes No