Recover from Hard Fault on Cortex M0+ - c

Until now I had a Hard fault handler in C that I defined in the vector table:
.sect ".intvecs"
.word _top_of_main_stack
.word _c_int00
.word NMI
.word Hard_Fault
.word Reserved
.word Reserved
.word Reserved
.word Reserved
.word Reserved
.word Reserved
.word Reserved
.word Reserved
.word Reserved
.word Reserved
.word Reserved
.word Reserved
....
....
....
One of our tests triggers a hard fault (on purpose) by writing to a non existing address. Once the test is done, the handler returns to the calling function and the cortex recovers from the fault. Worth mentioning that the handler does not have any arguments.
Now I'm in the phase of writing a real handler.
I created a struct for the stack frame so we can print PC, LR, and xPSR in case of a fault:
typedef struct
{
int R0 ;
int R1 ;
int R2 ;
int R3 ;
int R12 ;
int LR ;
int ReturnAddress ;
int xPSR ;
} InterruptStackFrame_t ;
My hard fault handler in C is defined:
void Hard_Fault(InterruptStackFrame_t* p_stack_frame)
{
// Write to external memory that I can read from outside
/* prints a message containing information about stack frame:
* p_stack_frame->LR, p_stack_frame->PC, p_stack_frame->xPSR,
* (uint32_t)p_stack_frame (SP)
*/
}
I created an assembly function:
.thumbfunc _hard_fault_wrapper
_hard_fault_wrapper: .asmfunc
MRS R0, MSP ; store pointer to stack frame
BL Hard_Fault ; go to C function handler
POP {R0-R7} ; pop out all stack frame
MOV PC, R5 ; jump to LR that was in the stack frame (the calling function before the fault)
.endasmfunc
This is the right time to say that I don't have an OS, so I do not have to check bit[2] of LR because I definitely know that I use MSP and not PSP.
The program compiles and runs properly and I used JTAG to ensure that all registers restore to the wanted values.
When executing the last command (MOV PC, R5) the PC returns to the correct address, but at some point, the debugger indicates that the M0 is locked in a hard fault and cannot recover.
I do not understand the difference between using a C function as a handler or an assembly function that calls a C function.
Does anyone know what is the problem?
Eventually, I will use an assert function that will stuck the processor, but I want it to be optional and up to my decision.

To explain "old_timer"'s comment:
When entering an exception or interrupt handler on the Cortex the LR register has a special value.
Normally you return from the exception handler by simply jumping to that value (by writing that value to the PC register).
The Cortex CPU will then automatically pop all the registers from the stack and it will reset the interrupt logic.
When directly jumping to the PC stored on the stack however you will destroy some registers and you don't restore the interrupt logic.
Therefore this is not a good idea.
Instead I'd do something like this:
.thumbfunc _hard_fault_wrapper
_hard_fault_wrapper: .asmfunc
MRS R0, MSP
B Hard_Fault
EDIT
Using the B instruction may not work because the "distance" allowed for the B instruction is more limited than for the BL instruction.
However there are two possibilities you could use (unfortunately I'm not sure if these will definitely work).
The first one will return to the address that had been passed in the LR register when entering the assembler handler:
.thumbfunc _hard_fault_wrapper
_hard_fault_wrapper: .asmfunc
MRS R0, MSP
PUSH {LR}
BL Hard_Fault
POP {PC}
The second one will indirectly do the jump:
.thumbfunc _hard_fault_wrapper
_hard_fault_wrapper: .asmfunc
MRS R0, MSP
LDR R1, =Hard_Fault
MOV PC, R1
EDIT 2
You cannot use LR because it holds EXC_RETURN value. ... You have to read the LR from stack and you must clean the stack from the stack frame, because the interrupted program doesn't know that a frame was stored.
According to the Cortex M3 manual you must exit from an exception handler by writing one of the three EXC_RETURN values to the PC register.
If you simply jump to the LR value stored in the stack frame you remain in the exception handler!
If something stupid happens during the program the CPU will assume that an exception happened inside the exception handler and it hangs.
I assume that the Cortex M0 works the same way as the M3 in this point.
If you want to modify some CPU register during the exception handler you can modify the stack frame. Thc CPU will automatically pop all registers from the stack frame when you are writing the EXC_RETURN value to the PC register.
If you want to modify one of the registers not present in the stack frame (such as R5) you can directly modify it in the exception handler.
And this shows another problem of your interrupt handler:
The instruction POP {R0-R7} will set registers R4 to R7 to values that do not match the program that has been interrupted. R12 will also be destroyed depending on the C code. This means that in the program being interrupted these four registers suddenly change while the program is not prepared for that!

Related

Stack Pointer reading incorrect value from register

Why is Stack-pointer register not reading correct value from another register? When I move a value from register (r0) to stack pointer (r13), the SP reads incorrect value.
This is what is mean:
MOV R0, 10
MOV R13, R0
In this case, "A" should move to R13 but instead it gets 8.
Similarly,
MOV R0, 9
MOV R13, R0
In this case R13 stores 8 instead of 9.
Here's a simple program program that demonstrates the problem,
void Init()
{
__asm(
"LDR R0, =0x3FFFFDA7\n"
"MOV R13, R0\n"
);
}
int main(void)
{
Init();
return (1);
}
void SystemInit(void)
{
}
Nothing much is going on here. Just a simple function call. Inside the function I moved the address to r0. Then I moved the address to R13(SP), but instead of actual address i.e. 0x3FFFFDA7, SP received 0x3FFFFDA4.
The images shows the disassembly,
So what is going on here? Why is Stack pointer Register reading incorrect values?
I am using ARM inline Assembly with C. The IDE is KEIL.
Thanks in Advance.
For those who might find this helpful.
Stack-Pointer for armv7 must be 4 bytes aligned. You can write there 0,4,8,12,16 etc but not 9,10,F etc.
So if you want to move any value to Stack-Pointer, make sure it is 4 bytes aligned.

Bootloader. ARM CORTEX M0+ relocating Interrupt Table assembly ERROR

I am currently trying to develop my own bootloader for an Atmel SAM R21.
My idea is to run the bootloader firstly, so it will decide if an update is needed to be performed or just jumping to the application. The main problem is that the Interrupt Vector Table is located at the 0x0000_0000 address, so it needs to be relocated just before the application code, so if the bootloader has a 8KB space set in the linker file and using the BOOTPROT fuse in that way (setting this fuse it is supposed that there will be some protection to the amount of memory selected through the fuse), the vector table should start at the 0x0000_2000 address.
In order to relocate the vector table I pretend to use the VTOR register, which is an offset applied to the original table address (0x0000_0000).
The assembly code is the following:
asm(" LDR R0,=0xE000ED08 "); //VTOR ADDRESS
asm("LDR R1,=0x00002000"); //OFFSET
asm(" STR R1, [R0]");
asm(" LDR R0,[R1] ");
asm(" MOV SP, R0");
asm(" LDR R0,[R1, #4]");
asm(" BX R0");
LDR instruction gives me the following error:
Error[Og006]: Syntax error in inline assembly: "Error[401]: Operand syntax error"
What am I doing wrong? Maybe I am trying to use ARM instruction instead of a Thumb one?
I will very appreciate any advise.
I am also doubting if once I get the Interrup Vector Table relocated, should I count with the Initial MSP value also? I want to mean, if the Interrupt Vector table starts at address 0x0000_2000 after being relocated, I should count 4(bytes) * Interrupt in order to know which should be the initial application address, shouldn't I? If someone knows something about this it would be nice. I know I am close (or I think so), but I need to clarify those points.
Edited 27/06/16 at 13:04.
This instruction works LDR R0,[R1]
So I guess it is something related to receive the 32 bits address into the register, but I don't understand why it is complaining about this.
SOLUTION:
As an answer to my question, someone posted that not all assembly directives can be used inlined, so I needed to create an assembler file, my_file.s
In this file should be created a function to be called from outside, something like this:
#define _PORT_ASM_ARM_SRC
#define __ASSEMBLY__
;/****************************************************************************
;** **
;** ASSEMBLY FUNCTIONS **
;** **
;****************************************************************************/
NAME start_app
RSEG CODE:CODE(2)
THUMB
PUBLIC jump_to_app
;/***************************************************************************/
;/***************************************************************************/
;/* jump_to_app()
; * Jump to application function.
; */
jump_to_app:
LDR R0,=0xE000ED08 ; Set R0 to VTOR address
LDR R1,=0x00010000 ; User’s flash memory based address
STR R1, [R0] ; Define beginning of user’s flash memory as vector table
LDR R0,[R1] ; Load initial MSP value
MOV SP, R0 ; Set SP value (assume MSP is selected)
LDR R0,[R1, #4] ; Load reset vector
BX R0 ; Branch to reset handler in user’s flash
END
After doing this, the function prototipe should be included into a .h file of your project as a normal function, using something like this:
void jump_to_app(void);
Best regards,
Iván.
There is nothing syntactically wrong with the assembly code above. If you put the assembly code into an asm file and assemble it, it will build (as to whether it does what you intended I have not checked).
For some reason the inline assembler does not like LDR Rd, =expr.
See the following quote from the IAR Embedded Workbench Help:
The pseudo-instruction LDR Rd, =expr is not available from inline assembler
Also from ARM:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0472j/chr1359124248868.html
SOLUTION:
As an answer to my question, someone posted that not all assembly directives can be used inlined, so I needed to create an assembler file, my_file.s In this file should be created a function to be called from outside, something like this:
#define _PORT_ASM_ARM_SRC
#define __ASSEMBLY__
;/****************************************************************************
;** **
;** ASSEMBLY FUNCTIONS **
;** **
;****************************************************************************/
NAME start_app
RSEG CODE:CODE(2)
THUMB
PUBLIC jump_to_app
;/***************************************************************************/
;/***************************************************************************/
;/* jump_to_app()
; * Jump to application function.
; */
jump_to_app:
LDR R0,=0xE000ED08 ; Set R0 to VTOR address
LDR R1,=0x00010000 ; User’s flash memory based address
STR R1, [R0] ; Define beginning of user’s flash memory as vector table
LDR R0,[R1] ; Load initial MSP value
MOV SP, R0 ; Set SP value (assume MSP is selected)
LDR R0,[R1, #4] ; Load reset vector
BX R0 ; Branch to reset handler in user’s flash
END
After doing this, the function prototipe should be included into a .h file of your project as a normal function, using something like this:
void jump_to_app(void);
Best regards,
Iván.

Setting up interrupts in protected mode (x86)

What is the process of setting up interrupts for protected mode?
This link says one should:
Make space for the interrupt descriptor table
Tell the CPU where that space is (see GDT Tutorial: lidt works the very same way as lgdt)
Tell the PIC that you no longer want to use the BIOS defaults (see Programming the PIC chips)
Write a couple of ISR handlers (see Interrupt Service Routines) for both IRQs and exceptions
Put the addresses of the ISR handlers in the appropriate descriptors
Enable all supported interrupts in the IRQ mask (of the PIC)
The third step makes no sense to me (I looked at this link but there wasn't anything about telling the PIC anything) so I ignored it and completed the next two steps, only to be clueless once again when I reached the final step. However, from my understanding of interrupts, both of the steps I didn't understand relate to hardware interrupts from the PIC controller and shouldn't affect the interrupts raised by the PIT on IRQ 0. I therefore ignored this step as well.
When I ran my code it compiled fine and even ran in a virtual machine, but the interrupt seemed to fire only once. I then realised that I wasn't sending EOI to the PIC, preventing it from raising any more interrupts. However, adding mov al, 0x20 and out 0x20, al just before the iret instruction makes the virtual machine crash.
Here's my IDT:
; idt
idt_start :
dw 0x00 ; The interrupt handler is located at absolute address 0x00
dw CODE_SEG ; CODE_SEG points to the GDT entry for code
db 0x0 ; The unused byte
db 0b11101001 ; 1110 Defines a 32 bit Interrupt gate, 0 is mandatory, privilege level = 0 (0b00), the last bit is one so that the CPU knows that the interrupt will be used
dw 0x00 ; The higher part of the offset (0x00) is 0x00
idt_end:
idt_descriptor :
dw idt_end - idt_start - 1 ; Size of our idt, always one less than the actual size
dd idt_start ; Start address of our idt
Here's my interrupt handler (located at absolute location 0x00 in memory):
ISR_0:
push eax
add [0x300], byte
mov al, 0x20
out 0x20, al
pop eax
iret
times 512-($-$$) db 0
This is the code I use to enter protected mode and load the GDT and IDT into memory:
[bits 16]
switch_to_pm:
cli
lgdt [gdt_descriptor]
lidt [idt_descriptor]
mov eax, cr0
or eax, 1
mov cr0,eax
jmp CODE_SEG:init_pm
[bits 32]
init_pm :
mov ax, DATA_SEG
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ebp, 0x90000
mov esp, ebp
sti
call BEGIN_PM
My main function (that checks the value of 0x300) is as follows:
void main() {
char iii[15];
int * aa = (int *)0x300;
for (;;)
{
setCursor(0, 0);
print(itoab(*aa, iii));
}
}
By the way, I have verified using a memory dump that everything is loaded at the correct address and everything is exactly where it is expected. For example, 0x300 is a free part of memory used simply to simplify my code.
Let's look at how some comparably small kernel, i.e., Linux 0.01 does it!
Make space for the interrupt descriptor table
This is done two times (well, technically only one time): first, the bootloader (the path is /boot/boot.s) initializes the IDTR, so the CPU is happy when jumping into Protected Mode. The IDTR content is as follows:
idt_48:
.word 0 | idt limit=0
.word 0,0 | idt base=0L
The IDTR is loaded like this:
lidt idt_48 | load idt with 0,0
Now, the jump can be performed.
Note that there is no IDT here. It's just a dummy, so no error occurs somewhere in the kernel.
Afterwards, the real IDT is initialized (the path is /boot/head.s). The space is allocated like this:
_idt: .fill 256,8,0 # idt is uninitialized
Tell the CPU where that space is (see GDT Tutorial: lidt works the very same way as lgdt)
lidt expects a linear address containing the content of the IDTR. That content looks like this:
idt_descr:
.word 256*8-1 # idt contains 256 entries
.long _idt
The IDTR is initialized as follows:
lidt idt_descr
Tell the PIC that you no longer want to use the BIOS defaults (see Programming the PIC chips)
As #RossRidge mentioned in the comments to your question, that means remapping the IRQ interrupt vectors (IVs).
Since the PIC IVs overlap with the Intel x86 exception addresses, we have to remap one of them. The exception addresses are hard-wired, so we need to remap the PIC vectors.
See also this comment right above the corresponding code by Linus:
| well, that went ok, I hope. Now we have to reprogram the interrupts :-(
| we put them right after the intel-reserved hardware interrupts, at
| int 0x20-0x2F. There they won't mess up anything. Sadly IBM really
| messed this up with the original PC, and they haven't been able to
| rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,
| which is used for the internal hardware interrupts as well. We just
| have to reprogram the 8259's, and it isn't fun.
Now, here's the real code. The jmps in between are for synchronizing CPU and PIC, so the CPU won't send data the PIC cannot receive yet. This is comparable to wait states when writing to memory: when the CPU is faster than the memory/memory arbiter, it needs to wait some time before accessing memory the next time.
mov al,#0x11 | initialization sequence
out #0x20,al | send it to 8259A-1
.word 0x00eb,0x00eb | jmp $+2, jmp $+2
out #0xA0,al | and to 8259A-2
.word 0x00eb,0x00eb
mov al,#0x20 | start of hardware int's (0x20)
out #0x21,al
.word 0x00eb,0x00eb
mov al,#0x28 | start of hardware int's 2 (0x28)
out #0xA1,al
.word 0x00eb,0x00eb
mov al,#0x04 | 8259-1 is master
out #0x21,al
.word 0x00eb,0x00eb
mov al,#0x02 | 8259-2 is slave
out #0xA1,al
.word 0x00eb,0x00eb
mov al,#0x01 | 8086 mode for both
out #0x21,al
.word 0x00eb,0x00eb
out #0xA1,al
.word 0x00eb,0x00eb
mov al,#0xFF | mask off all interrupts for now
out #0x21,al
.word 0x00eb,0x00eb
out #0xA1,al
Write a couple of ISR handlers (see Interrupt Service Routines) for both IRQs and exceptions
For exceptions, you can find the handler code in /kernel/traps.c and /kernel/asm.s.
Some exceptions push an error code on the stack prior to jumping to the handler, which you have to pop off or the iret instruction will fail. A page fault also writes the corresponding virtual address to cr2 in addition.
The IRQ handlers are spread across the whole system. -.- The timer and disk interrupt handlers are in /kernel/system_call.s, the keyboard interrupt handler is in /kernel/keyboard.s, for example.
Put the addresses of the ISR handlers in the appropriate descriptors
The initialization for exceptions is done in /kernel/traps.c in the trap_init function:
void trap_init(void)
{
int i;
set_trap_gate(0,&divide_error);
set_trap_gate(1,&debug);
set_trap_gate(2,&nmi);
set_system_gate(3,&int3); /* int3-5 can be called from all */
set_system_gate(4,&overflow);
set_system_gate(5,&bounds);
set_trap_gate(6,&invalid_op);
set_trap_gate(7,&device_not_available);
set_trap_gate(8,&double_fault);
set_trap_gate(9,&coprocessor_segment_overrun);
set_trap_gate(10,&invalid_TSS);
set_trap_gate(11,&segment_not_present);
set_trap_gate(12,&stack_segment);
set_trap_gate(13,&general_protection);
set_trap_gate(14,&page_fault);
set_trap_gate(15,&reserved);
set_trap_gate(16,&coprocessor_error);
for (i=17;i<32;i++)
set_trap_gate(i,&reserved);
/* __asm__("movl $0x3ff000,%%eax\n\t"
"movl %%eax,%%db0\n\t"
"movl $0x000d0303,%%eax\n\t"
"movl %%eax,%%db7"
:::"ax");*/
}
The IRQ handler entry initializations are again spread across several files. sched_init from /kernel/sched.c initializes the timer interrupt handler's address, for instance.
Enable all supported interrupts in the IRQ mask (of the PIC)
This is done in /init/main.c in the main function with the macro sti. It is defined in /asm/system.h as follows:
#define sti() __asm__ ("sti"::)

Debugging Hard Fault on ARM Cortex-M0+ (using CMSIS DSP library)

I'm using the CMSIS DSP library on a Cortex-M0+.
Some functions, such as sqrt and FFT, are resulting in hard faults.
The arm_sqrt_f32 function calls sqrtf:
arm_sqrt_f32(
float32_t in,
float32_t * pOut)
[...]
*pOut = sqrtf(in);
part of the generated code:
0x00003914: bl 0x49e8 <sqrtf>
0x00003918: adds r2, r0, #0
0x0000391a: ldr r3, [r7, #0]
0x0000391c: str r2, [r3, #0]
The hard fault happens on the str instruction at address 0x0000391c. When at this line, the registers are:
$r1 0x0
$r2 0x40000000
$r3 0x0
$r4 0x0
$r5 0x200017fc
$r6 0x0
$r7 0x200017e0
$r8 0xfff7ffff
$r9 0xefbffffe
$r10 0xff7fffff
$r11 0x0
$r12 0x0
the SP register is 0x200017e0, an address containing 0.
I can't figure out why I'm getting this hard fault. What should I do?
Thanks!
Lets look at exactly what your str call is doing by looking at this page
your str call is doing str r2,[r3, #0] which translates to (if i'm not mistaken) :
store r2 in the address r3 offset by #0
Looking at those register values, you are trying to put 0x40000000 into location 0x0 offset by 0, so 0x0 still. It is the equivalent of a segmentation fault, you are trying to access memory that is not avaliable to you thus causing the hard fault.
Seeing as how that code is generated, I'm assuming you are giving it a faulty pOut pointer.
Make sure you aren't calling the function by doing arm_sqrt_f32(float32_t foo, float32_t* pOut) , you'll want to call it by doing arm_sqrt_f32(float32_t foo, float32_t &pOut) where pOut may be delcared as float32_t pOut = bar; since, as a pointer arguement, its looking for an address
If the Cortex-M0 fault mechanism is the same as the Cortex-M3/4/7 fault mechanism, then the following page provides detailed information on how to decode the fault stack, giving you the address of the faulting instruction, as well as the register values at the time.
http://www.freertos.org/Debugging-Hard-Faults-On-Cortex-M-Microcontrollers.html

AM335x FreeRTOS port, unable to handle IRQs and SWI

I'm currently trying to port FreeRTOS to the TI AM335x processor, best known for being used on the BeagleBones. I am able to boot, run GPIOs and setup a compare match timer for running the system ticks. If I disable interrupts, i can see how the interrupt get set after a correct amount of time after the timer was started. And if I enable interrupts, my application dies after that same given time. The application also dies if I try to yield a task, aka calling the SWI handler. This makes me belive that the vector table is unavailable or incorrectly setup. The ROM Exception Vectors for SWI and IRQ has the contenct 4030CE08h and 4030CE18h. Which again in RAM executes some branching, the TRM says:
User code can redirect any exception to a custom handler either by writing its address to the appropriate location from 4030CE24h to 4030CE3Ch or by overriding the branch (load into PC) instruction between addresses from 4030CE04h to 4030CE1Ch.
My vIRQHandler function address is therefore written to 4030CE38h. One would hope this was enough, but sadly no. I suspect that there is something wrong in my boot.s file, however my assembly has never been that great and i'm struggling to understand the code. The boot.s and the rest of the project was started from a OMAP3 port.
Boot.s:
.section .startup,"ax"
.code 32
.align 0
b _start /* reset - _start */
ldr pc, _undf /* undefined - _undf */
ldr pc, _swi /* SWI - _swi */
ldr pc, _pabt /* program abort - _pabt */
ldr pc, _dabt /* data abort - _dabt */
nop /* reserved */
ldr pc, _irq /* IRQ - read the VIC */
ldr pc, _fiq /* FIQ - _fiq */
_undf: .word 0x4030CE24 /* undefined */
_swi: .word 0x4030CE28 /* SWI */
_pabt: .word 0x4030CE2C /* program abort */
_dabt: .word 0x4030CE30 /* data abort */
_irq: .word 0x4030CE38
_fiq: .word 0x4030CE3C /* FIQ */
The branch to start sets up a stack for each mode and clears the bss, not sure if that is relevant. This is the code which seems relevant to me, and I have updated the words to fit the AM335 instead of the OMAP3.
The setting IRQ handler:
#define E_IRQ (*(REG32 (0x4030CE38)))
....
/* Setup interrupt handler */
E_IRQ = ( long ) vIRQHandler;
If anyone have any pointers to an assembly newbie it would be much appriciated, because i'm completely stuck :)
U-boot had moved the exception vector table. However, instead of recompiling u-boot I just reset the exception vector table in my own start script.
Added this right before branching to main:
/* Set V=0 in CP15 SCTRL register - for VBAR to point to vector */
mrc p15, 0, r0, c1, c0, 0 # Read CP15 SCTRL Register
bic r0, #(1 << 13) # V = 0
mcr p15, 0, r0, c1, c0, 0 # Write CP15 SCTRL Register
/* Set vector address in CP15 VBAR register */
ldr r0, =_vector_table
mcr p15, 0, r0, c12, c0, 0 #Set VBAR
bl main
And put in the _vector_table label at the start of my exception vector table:
.section .startup,"ax"
.code 32
.align 0
_vector_table: b _start /* reset - _start */
ldr pc, _undf /* undefined - _undf */
ldr pc, _swi /* SWI - _swi */
ldr pc, _pabt /* program abort - _pabt */
ldr pc, _dabt /* data abort - _dabt */
nop /* reserved */
ldr pc, _irq /* IRQ - read the VIC */
ldr pc, _fiq /* FIQ - _fiq */
Now all the exceptions gets redirected to my code. Hopefully this will help anyone in the same situation that I was in:)

Resources