summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2014-09-15 11:38:48 -0600
committerGregory Nutt <gnutt@nuttx.org>2014-09-15 11:38:48 -0600
commit8fc8a50b327626fb6bf3e034e5ad9494f6d3f418 (patch)
treefcf74e3dbb41493ce9e1fecdbb1051955b131e5e
parent0f95d236d32cb70cdb1114ef67fc682c42406e3d (diff)
downloadnuttx-8fc8a50b327626fb6bf3e034e5ad9494f6d3f418.tar.gz
nuttx-8fc8a50b327626fb6bf3e034e5ad9494f6d3f418.tar.bz2
nuttx-8fc8a50b327626fb6bf3e034e5ad9494f6d3f418.zip
If we are configured to use a kernel stack while in SYSCALL handling, then we need to switch back to the user stack to deliver a signal
-rwxr-xr-xnuttx/arch/arm/include/armv7-a/irq.h3
-rw-r--r--nuttx/arch/arm/src/armv7-a/arm_syscall.c42
2 files changed, 45 insertions, 0 deletions
diff --git a/nuttx/arch/arm/include/armv7-a/irq.h b/nuttx/arch/arm/include/armv7-a/irq.h
index 3813e919a..5688593ff 100755
--- a/nuttx/arch/arm/include/armv7-a/irq.h
+++ b/nuttx/arch/arm/include/armv7-a/irq.h
@@ -296,6 +296,9 @@ struct xcptcontext
FAR uint32_t *ustkptr; /* Saved user stack pointer */
FAR uint32_t *kstack; /* Allocate base of the (aligned) kernel stack */
+#ifndef CONFIG_DISABLE_SIGNALS
+ FAR uint32_t *kstkptr; /* Saved kernel stack pointer */
+#endif
#endif
#endif
};
diff --git a/nuttx/arch/arm/src/armv7-a/arm_syscall.c b/nuttx/arch/arm/src/armv7-a/arm_syscall.c
index 7be976ae0..fdd05badb 100644
--- a/nuttx/arch/arm/src/armv7-a/arm_syscall.c
+++ b/nuttx/arch/arm/src/armv7-a/arm_syscall.c
@@ -352,6 +352,9 @@ uint32_t *arm_syscall(uint32_t *regs)
case SYS_signal_handler:
{
FAR struct tcb_s *rtcb = sched_self();
+#ifdef CONFIG_ARCH_KERNEL_STACK
+ int depth;
+#endif
/* Remember the caller's return address */
@@ -379,6 +382,24 @@ uint32_t *arm_syscall(uint32_t *regs)
*/
regs[REG_R3] = *(uint32_t*)(regs[REG_SP]+4);
+
+#ifdef CONFIG_ARCH_KERNEL_STACK
+ /* If this is a nested SYSCALL and if there is an allocated kernel
+ * stack, then we must be operating on the kernal stack now. We
+ * need to switch back to the user stack before dispatching the
+ * signal handler to the user code.
+ */
+
+ depth = rtcb->xcp.nsyscalls;
+ if (depth > 0 && rtcb->xcp.kstack != NULL)
+ {
+ DEBUGASSERT(rtcb->xcp.kstkptr == NULL &&
+ rtcb->xcp.ustkptr[depth - 1] != 0);
+
+ rtcb->xcp.kstkptr = (FAR uint32_t *)regs[REG_SP];
+ regs[REG_SP] = (uint32_t)rtcb->xcp.ustkptr[depth - 1];
+ }
+#endif
}
break;
#endif
@@ -396,6 +417,9 @@ uint32_t *arm_syscall(uint32_t *regs)
case SYS_signal_handler_return:
{
FAR struct tcb_s *rtcb = sched_self();
+#ifdef CONFIG_ARCH_KERNEL_STACK
+ int depth;
+#endif
/* Set up to return to the kernel-mode signal dispatching logic. */
@@ -405,6 +429,24 @@ uint32_t *arm_syscall(uint32_t *regs)
cpsr = regs[REG_CPSR] & ~PSR_MODE_MASK;
regs[REG_CPSR] = cpsr | PSR_MODE_SVC;
rtcb->xcp.sigreturn = 0;
+
+#ifdef CONFIG_ARCH_KERNEL_STACK
+ /* If this is a nested SYSCALL and if there is an allocated kernel
+ * stack, we must be using the user stack to dispatch to the signal
+ * handler. We need to switch to back to the kernel user stack
+ * before returning to the kernel mode signal trampoline.
+ */
+
+ depth = rtcb->xcp.nsyscalls;
+ if (depth > 0 && rtcb->xcp.kstack != NULL)
+ {
+ DEBUGASSERT(rtcb->xcp.kstkptr != NULL &&
+ (uint32_t)rtcb->xcp.ustkptr[depth - 1] == regs[REG_SP]);
+
+ regs[REG_SP] = (uint32_t)rtcb->xcp.kstkptr;
+ rtcb->xcp.kstkptr = NULL;
+ }
+#endif
}
break;
#endif