Adding signal handing for SEGV/BUS/...
I have been investigating signal handling in NuttX for SEGV/BUS/… for armv7-m. I have got some PoC code written (for a backlevel NuttX) that, while I’m in the interrupt handler, will schedule a signal handler. However, when it returns from the interrupt in exception_common and the next instruction is executed:
343 bx r14 /* And return */
(gdb) p $r14
$1 = 0xfffffffd
(gdb) stepi
up_sigdeliver () at armv7-m/up_sigdeliver.c:74
74 struct tcb_s *rtcb = this_task();
(gdb) bt
#0 up_sigdeliver () at armv7-m/up_sigdeliver.c:74
#1 <signal handler called>
I get a hardfault with a cfault of 0x1 (instruction access violation) and an hfault of 0x40000000 (“FORCED”):
[ 9.439000] [12] up_hardfault: IRQ: 3 regs: 0xc0327078
[ 9.439000] [12] up_hardfault: BASEPRI: 00000080 PRIMASK: 00000000 IPSR: 00000003 CONTROL: 00000001
[ 9.439000] [12] up_hardfault: CFAULTS: 00000001 HFAULTS: 40000000 DFAULTS: 0000000b BFAULTADDR: 00000000 AFAULTS: 00000000
The registers at the time of the fault:
lr 0xfffffff9 0xfffffff9
pc 0x8161ef0 0x8161ef0 <up_sigdeliver>
xPSR 0x1000000 0x1000000
fpscr 0x80000010 0x80000010
msp 0xc03275e8 0xc03275e8
psp 0xc03276b8 0xc03276b8
primask 0x0 0x0
basepri 0x80 0x80
faultmask 0x0 0x0
control 0x3 0x3
In the interrupt handler I thought I was clearing/acknowledging the interrupt in the correct way in up_memfault:
uint32_t cfsr = getreg32(NVIC_CFAULTS);
uint32_t *mfsr = (uintptr_t) NVIC_CFAULTS;
:
*mfsr |= cfsr; /* Acknowledge interrupt */
I have looked through the ARM-7M Architecture Reference but I can’t work out what the proper way of exiting the handler is (assuming this is the root cause of the problem).
@masayuki2009 @pkarashchenko @xiaoxiang781216 any idea why issue is happening?
@nealef do you want to continue after memfault? the default memfault handler doesn't support this feature yet, I think you may need skip the broken instruction before return.
@xiaoxiang781216 I've scheduled a signal handler (if one exists for that tid). If not we panic as before. It's when we leave exception_common that there's a problem.
@nealef do you want to continue after memfault? the default memfault handler doesn't support this feature yet, I think you may need skip the broken instruction before return.
I suppose I should ask a more fundamental question: when a userland program issues a signal() what is the mechanism of returning from that syscall after the kernel has scheduled a signal handler? That is, the syscall is handled by checking if there is a corresponding signal handler and if so using nxsig_tcbdispatch() to schedule the handler it then returns from the syscall. What is the mechanism used to return from that call?
As you can see in my case when I leave exception_common the up_sigdeliver() function is what gets branched to. I am assuming this guy is supposed to run (in my configuration) in protected mode but is in userland mode instead (for wont of a better term). up_sigdeliver (or arm_sigdeliver as it is now) appears to do the returning to userland. So should my code set up the return register to be 0xfffffff[1|9] so that the signal deliver is invoked in the correct mode?