I came across a weird behaviour when trying out my program on Raspberry Pi 2b (Cortex-A7):
When I try my PABT-handler using BKPT, the handler is entered fine, but on return the program restarts.
The restarted program returns fine from the BKPT and continues as expected.
Any idea what could cause the restart once?
I have MMUs, caches and predictors off, so the memory should be strongly ordered?
And anyway, it's the same code.
void rpi2_pabt_handler() __attribute__ ((naked));
...
void rpi2_pabt_handler(){ asm volatile ( "push {r0 - r12, lr}\n\t" "mov r5, lr\n\t" "bl rpi2_pabt_handler2\n\t" "pop {r0 - r12, lr}\n\t" "subs pc, lr, #0 @ to skip bkpt\n\t" );}#define DEBUG_PABTvoid rpi2_pabt_handler2(){ uint32_t exc_addr; uint32_t exc_cpsr;#ifdef DEBUG_PABT int i; char *pp; static char scratchpad[16];#endif asm volatile ( "mov %[var_reg], r5\n\t" :[var_reg] "=r" (exc_addr) :: ); asm volatile ( "mrs %[var_reg], spsr\n\t" :[var_reg] "=r" (exc_cpsr) :: );#ifdef DEBUG_PABT pp = "\r\nPABT EXCEPTION\r\n"; do {i = serial_raw_puts(pp); pp += i;} while (i); serial_raw_puts("exc_addr: "); util_word_to_hex(scratchpad, exc_addr); serial_raw_puts(scratchpad); serial_raw_puts("\r\nSPSR: "); util_word_to_hex(scratchpad, exc_cpsr); serial_raw_puts(scratchpad); serial_raw_puts("\r\n");#endif // rpi2_trap_handler();}
I totally missed the modes in the last round! You could call it a double fault (in a sense).
Funny - I also had UNDEFINED exception "mined", but no notifications from there.
Anyway, that's back to the manuals about BKPT and debug state (or is it mode...).
#define DEBUG_UNDEF void rpi2_undef_handler2() { uint32_t stack_frame_addr; uint32_t exc_addr; uint32_t exc_cpsr; #ifdef DEBUG_UNDEF static char scratchpad[16]; // scratchpad char *p; int i; #endif // fetch the parameter asm volatile ( "mov %[var_reg], r4\n\t" :[var_reg] "=r" (stack_frame_addr) :: ); asm volatile ( "mov %[var_reg], r5\n\t" :[var_reg] "=r" (exc_addr) :: ); asm volatile ( "mrs %[var_reg], spsr\n\t" :[var_reg] "=r" (exc_cpsr) :: ); #ifdef DEBUG_UNDEF p = "\r\nUNDEFINED EXCEPTION\r\n"; do {i = serial_raw_puts(p); p += i;} while (i); serial_raw_puts("exc_addr: "); util_word_to_hex(scratchpad, exc_addr); serial_raw_puts(scratchpad); serial_raw_puts("\r\nSPSR: "); util_word_to_hex(scratchpad, exc_cpsr); serial_raw_puts(scratchpad); serial_raw_puts("\r\n"); #endif // exception_info = RPI2_EXC_UNDEF; // rpi2_trap_handler(); asm volatile ( "push {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10,lr}\n\t" "1:\n\t" "mov r0, #0x100\n\t" "mov r1, #0x100\n\t" "mov r2, #5\n\t" "bl debug_blink\n\t" "mov r3, #0x2000 @ 2 s pause\n\t" "bl debug_wait\n\t" "mov r0, #0x1000\n\t" "mov r1, #0x1000\n\t" "mov r2, #2\n\t" "bl debug_blink\n\t" "mov r3, #0x5000 @ 5 s pause\n\t" "bl debug_wait\n\t" "b 1b\n\t" "pop {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, lr}\n\t" "@ for now we can't return\n\t" "push {r0, r1}\n\t" "ldr r0, =exception_info\n\t" "mov r1, #1 @ RPI2_EXC_UNDEF\n\t" "str r1, [r0]\n\t" "pop {r0, r1}\n\t" "bl rpi2_trap_handler\n\t" ); } void rpi2_undef_handler() { // rpi2_undef_handler2() // - No C in naked function asm volatile ( "push {r0 - r12, lr}\n\t" "mov r5, lr\n\t" "mov r4, sp\n\t" "bl rpi2_undef_handler2\n\t" "pop {r0 - r12, lr}\n\t" "subs pc, lr, #4\n\t" ); }
And thanks, guys, I was running in circles...
Looks complicated - this BKPT/debug-state/UNDEF-thing...
Just for the record (readers), the debug state was probably not entered at all, and the problem was in serial I/O.
When the transmitter is idle, the UART interrupts need to be disabled. They are turned back on when data is written to the transmit buffer.
At the end, tha transmitter FIFO is filled from the transmit buffer, and the UART interrupts are turned on. Before that, the interrupts need to be disabled, and enabled after the job not to mess with the transmit interrupt.
The problem was restoring the interrupts:
static inline void restore_ints(uint32_t status) { asm volatile ( "msr cpsr_fsxc, %[var_reg]\n\t" :[var_reg] "r" (status):: ); }
The inline assembly tries to return the status into the parameter, and the function is supposed to be void.
That seemed to have messed up the stack.