How do I exit from a ARM fault handler? - arm

I am using STM32F746, an ARM cortex-M7 based processor. I am trying to do something hacky, which requires me to return to the program from a MemManage Fault handler.
When entering MemManage Fault handler, the PC before the fault and everything I need is stored on the stack. So I thought I can simply recover those to return to the previous execution point.
However, I cannot properly restore the xPSR.
The previous CPSR before the fault handler is saved in the stack, so I tried restoring it using MSR instruction.
I tried both MSR, xpsr, r12 and MSR, apsr, r12
However, it would only restore the flags and not the other parts of the CPSR , such as the GE or the system mode bits.
(and my mode bits seems also weird.. my xPSR shows as: 0x61070004, but this tells me that the last 5 bits cannot be 0x04)
How can I go back to the program point before the fault handler? I also tried popping the pc but it does not work and I think the problem is CPSR not getting properly restored.

When a Cortex M7 enters an exception handler, the execution context is saved as follows and of course restored when exiting the handler (from ARM Cortex M7 Programming Manual):
As you see, the xPSR is restored after the return from exception.
Furthermore
faults are a subset of the exceptions.
You can do a simple test: dereference on purpose an unvalid pointer. It will trigger a HardFault. Modify your HardFault handler to just return and do nothing. You can check that the context is restored. I tried on STM32H753, it works fine, xPSR latest bits (ISR_NUMBER) are indeed 0 (thread mode).
Be careful though: I don't know for MemManage but HardFault returns to the very same instruction that triggered the fault (and not to the following instruction like a regular exception). It means you will execute again the same instruction after the Hardfault.

Related

Cortex-M0+ not responding to PendSV

I'm running on a Raspberry Pi Pico (RP2040, Cortex-M0+ core, debugging via VSCode cortex-debug using JLink SWD), and I'm seeing strange behaviour regarding PendSV.
Immediately prior, the SVCall exception handler requested PendSV via the ICSR register. But on exception return, rather than tail-chaining the PendSV, execution instead returns to the calling code and continues non-exception execution.
All the while the ICSR register shows the pending PendSV, even while thread code instructions are repeatedly stepped. System handler priorities are all zero, IRQ priorities are lower.
According to the ARMv6-M reference manual, PendSV cannot be disabled.
So, what am I missing that would cause this behaviour?
Edited to add:
Perhaps it's a debugger interaction? The JLink software (v4.95d) is still in Beta...
I see that the debugger can actually disable PendSV and Systick - C1.5.1 Debug Stepping: "Optionally, the debugger can set DHCSR.C_MASKINTS to 1 to prevent PendSV, SysTick, and external configurable interrupts from occurring. This is described as masking these interrupts. Table C1-7 on page C1-326 summarizes instruction stepping control."
It turns out that the problem is caused by single-stepping the instruction that writes to the PENDSVSET bit in the ICSR: the bit is set, and the VECTPENDING field shows 0xe, but the PendSV never fires.
Free-running over that instruction to a later breakpoint sees the PendSV fire correctly.
So it is indeed a debugger interaction.
Whether that's to do with interrupts being inhibited as #cooperised suggests isn't clear - the DHCSR's C_MASKINTS bit reads as zero throughout, but how that bit is manipulated during the actual step operation isn't visible at this level.
Which makes me wonder whether the way the JLink is performing the step induces unpredictable/indeterminate behaviour - e.g. as per the warning in the C_MASKINTS description. Or perhaps this is simply what happens in an M0+ under these circumstances, and I've never single-stepped this instruction before.
In any case, the workaround is simply to not single-step the instruction that sets PENDSVSET.
Edited to add:
Finally, #cooperised was correct.
On taking more care to distinguish exactly between stepping (including stepping over function calls) and running (including running to the very next instruction), it's clear that stepping disables interrupts including PendSV.
The same thing happened to me but I found that the reason was that I was not closing the previous PensSV interrupt by returning through LR containing 0xFFFFFFF9. Instead I was returning via the PC to a previous routine's return address.
Since I did not return via 0xFFFFFFF9 it was not properly closing the previous PendSV and did not recognize subsequent ones.

If the main function is called inside the reset handler, how other interrupts are handled?

This is a sample code from a start up code for Tiva C, as you can see the main function is called inside the reset handler, and as i understand it's is the highest priority, so my question is how any other interrupt can be handled if we are still inside the reset handler?
```
; Reset Handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
```
The reset is "special". When the reset handler is invoked by a processor reset, instructions are executed in thread mode. Necessarily so, since the reset vector is invoked on a power-on-reset (POR) - if the handler had to "return" where would it return to?
Also on reset in any case registers are reset to their defined reset state, and the stack pointer set to the address at the start of the table (in the case of am ARM Cortex-M at least), so there would be nowhere from which to fetch a return address - in fact the reset signal does not cause a return address to be stacked in any case.
The whole point of a reset is to restart the processor in a known state.
Returning to the point at which the reset occurred makes little sense, and would not be likely to work given that the reset state of the processor is unlikely to be a suitable run-state for the "interrupted" code.
From the ARM Cortex-M3 User Guide (my emphasis) other ARM architectures may differ in the details, but not the general point.
2.3.2. Exception types The exception types are:
Reset
Reset is invoked on power up or a warm reset. The exception model treats reset as a special form of exception. When reset is asserted,
the operation of the processor stops, potentially at any point in an
instruction. When reset is deasserted, execution restarts from the
address provided by the reset entry in the vector table. Execution
restarts as privileged execution in Thread mode.
[...]
I've found the pseudocode in the ARM architecture reference manuals to be quite helpful for answering this type of question. By "tiva c", I assume you are talking about the TM4C line of microcontrollers which are Cortex-M4 based MCUs. This means we will want to look at the ARMv7-M architecture reference manual.
Section "B1.5.5 Reset Behavior" has the pseudocode we are interested in. Here's a snippet (with the parts not relevant to the question elided out):
Asserting reset causes the processor to abandon the current execution
state without saving it. On the deassertion of reset, all registers
that have a defined reset value contain that value, and the processor
performs the actions described by the TakeReset() pseudocode.
// TakeReset()
// ============
TakeReset()
CurrentMode = Mode_Thread;
PRIMASK<0> = '0'; /* priority mask cleared at reset */
FAULTMASK<0> = '0'; /* fault mask cleared at reset */
BASEPRI<7:0> = Zeros(8); /* base priority disabled at reset */
// [...]
From the description we can note:
If the system is running and a reset is issued, the processor will always "abandon the current execution". So it is the "highest priority" thing that can happen if the MCU is running.
However, after the MCU restarts and the "TakeReset" logic starts to run, the "CurrentMode" the processor enters is actually Thread mode. ARMv7-M has two operation modes known as Thread Mode and Handler Mode. All interrupts/exceptions run in Handler Mode and normal code runs in Thread Mode. This tells us the reset path does not actually start like an interrupt/exception would. It's just running like normal code would.

Cortex-m0 pop instruction fails when executing in vector area?

Is there some official reason why this instruction would fail when executing from the vector area (i.e., below address 0xC0) on Cortex-M0 but works correctly in higher FLASH?
pop {r4,pc}
When this code is executed from an address below 0xC0, the PC is not actually popped from the stack and execution proceeds with the next instruction after 'pop' (which is garbage).
It sounds like you're trying to return from an exception handler, or change the program counter from within an exception handler.
A pop instruction which loads a value to the pc, or a bx instruction will cause the processor to return from the exception, however, it won't return to the address you pop into the pc, instead it will unwind the stack and return to the location which was pushed during exception entry. This is detailed further in the M0 User guide - exception entry and return.
If you want to return to a different location from within the exception, you could modify the stack frame which was pushed, overwriting the pushed pc with your desired location.
Problem solved...it is a vendor-specific issue. The FLASH space for this SoC at addresses <256 is protected and "virtual" -- it functions as exception vectors but it does not function normally (by design) for code execution or for general FLASH storage.
Thanks all for the help.

Arm Exception handling mechanics

Was going through basics of arm exception handling, and come to this doubt:
(I am taking example of abort exception)
Lets assume processor is running in user mode.
When an exception happens the processor will save the current pc to abort_mode LR and CPSR to abort mode SPSR and the execution jumps to vector address for the abort, so PC will now contain the vector address for the abort.
So what I get here is before transferring the flow to abort mode(having pc to abort vector).
CPU will perform below task:
"Current pc to abort_mode LR and CPSR to abort mode SPSR and then execution jumps to vector address for the abort," as mentioned above.
In which mode and from where instructions are executed to perform above task?
When an abort exception happens, the text you are quoting is correct. Other exceptions use different modes.
The copying of PC to the exception mode LR and the CPSR to SPSR and setting the PC to the exception vector is what the processor does for you automatically on taking the exception. There are no instructions required. It also switches to the SP used by that exception mode.
The exception handler is what comes next. and is completely in software.

ARM modes: User and System

Can you explain how the ARM mode get changed in case of a system call handling?
I heard ARM mode change can happen only in privileged mode, but in case of a system call handling while the ARM is in user mode (which is a non-privileged mode), how does the ARM mode change?
Can anybody explain the whole action flow for the user mode case, and also more generally the system call handling (especially how the ARM mode change)?
Thanks in advance.
In the case of system calls on ARM, normally the system call causes a SWI instruction to be executed. Anytime the processor executes a SWI (software interrupt) instruction, it goes into SVC mode, which is privileged, and jumps to the SWI exception handler. The SWI handler then looks at the cause of the interrupt (embedded in the instruction) and then does whatever the OS programmer decided it should do. The other exceptions - reset, undefined instruction, prefetch abort, data abort, interrupt, and fast interrupt - all also cause the processor to enter privileged modes.
How file handling works is entirely up to whoever wrote your operating system - there's nothing ARM specific about that at all.
You need to get a copy of the ARM ARM (Architectural Reference Manual).
http://infocenter.arm.com -> ARM Architecture -> Reference Manuals -> ARMv5 Architectural Reference Manual then download the pdf.
It used to be a single ARM ARM for the ARM world but there are too many cores and starting to diverge so they split off the old one as ARMv5 ARM and made new Architectural Reference Manuals for each of the major ARM processor families.
In the Programmers Model chapter it talks about the modes, it says that you can change freely among the modes other than user. ARM startup code will often go through a series of mode changes so that the stack pointers, etc can be configured. Then as needed go back to System mode or User mode.
In that same chapter look at the Exceptions section, this describes the exceptions and what mode the processor switches to for each exception.
The Software interrupt exception which happens when an SWI instruction is executed, is a way to implement system calls. The processor is put in Supervisor mode and if in thumb mode switches to arm mode.
There needs to be code to support that exception handler of course. You need to verify with the operating system, if any, you are running, what is supported and what the calling convention is, etc.
Not all ARM processors work this way. The Cortex-M (ARMv7-M) does not have the same modes and same exception table, etc. As with any time you are using an ARM (at this level) you need to get the ARM ARM for the family you are using and you need to get the TRM (Techincal Reference Manual) for the core(s) you are using, ideally the exact revision, even if ARM marks the TRM as having been replaced by a newer version the chip manufacturer has purchased and uses a specific rev of the core and there can be enough differences between revs that you will want the correct manual.
When an SVC instruction is encountered by the PC, the following behaviour takes place:
The current status (CPSR) is saved (to the supervisor SPSR)
The mode is switched to supervisor mode
Normal (IRQ) interrupts are disabled
ARM mode is entered (if not already in use)
The address of the following instruction (the return address) is saved into the link register (R14) - It's worth noting that this is
the link register that belongs to the supervisor mode
The PC is altered to jump to address 0x00000008
An exception vector (just a branch instruction) should be at the address 0x0000008, which will branch the program to another area of code used to determine which supervisor call has been made.
Determining which supervisor call has been made is usually accomplished by loading the SVC instruction into a register (by offsetting the LR by one word - since the LR is still pointing to the instruction next to the supervisor call), bit clearing the last 8 bits and using the value in the remaining 24 bits of the register to calculate an offset in a jump table, to branch to the corresponding SVC code.
When the supervisor call code wishes to return to the user application, the processor needs to context switch back into user mode and return to the address contained within the LR (which is only available in supervisor mode, since certain registers are banked for both modes). This problem is overcome using the MOVS instruction, as illustrated below:
(consider this to also be your explanation on how to change mode)
MRS R0, CPSR ; load CPSR into R0
BIC R0, R0, #&1F ; clear mode field
ORR R0, R0, #&10 ; user mode code
MSR SPSR, R0 ; store modified CPSR into SPSR
MOVS PC, LR ; context switch and branch
The MRS and MSR instructions are used to transfer content between an ARM register and the CPSR or SPSR.
The MOVS instruction is a special instruction, which operates as a standard MOV instruction, but also sets the CPSR equal to the SPSR upon branching. This allows the processor to branch back (since we're moving the LR into the PC) and change mode to the mode specified by the SPSR.
I quote from the ARM documentation available here:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0471c/BABDCIEH.html
When an exception is generated, the processor performs the following
actions:
Copies the CPSR into the appropriate SPSR. This saves the current mode, interrupt mask, and condition flags.
Switches state automatically if the current state does not match the instruction set used in the exception vector table.
Changes the appropriate CPSR mode bits to:
Change to the appropriate mode, and map in the appropriate banked out registers for that mode.
Disable interrupts. IRQs are disabled when any exception occurs. FIQs are disabled when an FIQ occurs and on reset.
Sets the appropriate LR to the return address.
Sets the PC to the vector address for the exception.
where, CPSR refers to Current Program Status Register and SPSR to Saved Program Status register used to restore the state of the process that was interrupted. Thus, as seen in point 3, the processor circuitry is designed in a way that the hardware itself changes the mode when user mode executes a Supervisor call instruction.
"I heard ARM mode change can happen only in privileged mode". You are partly right here. By partly I mean the control field of the CPSR register can be manually modified (manually modified means through code) in the privileged modes only not in the unprivileged mode (i.e. user mode). When a system call happens in the user mode it happens because of SWI instruction. An SWI instruction has inbuilt mechanism to change the mode to supervisor mode.
So to conclude , there are two ways to change the mode:
1) Explicitly through code. Only allowed in a privileged mode.
2) Implicitly through IRQ, FIQ, SWI, RESET, undefined instruction encountered, data abort, prefetch abort. This is allowed in all the modes.

Resources