diff --git a/arch/arm/include/armv7-a/irq.h b/arch/arm/include/armv7-a/irq.h index 46303c0160c..3813e919a51 100755 --- a/arch/arm/include/armv7-a/irq.h +++ b/arch/arm/include/armv7-a/irq.h @@ -276,7 +276,7 @@ struct xcptcontext #ifdef CONFIG_ARCH_ADDRENV #ifdef CONFIG_ARCH_STACK_DYNAMIC - /* This table holds the physical address of the level 2 page table used + /* This array holds the physical address of the level 2 page table used * to map the thread's stack memory. This array will be initially of * zeroed and would be back-up up with pages during page fault exception * handling to support dynamically sized stacks for each thread. @@ -284,6 +284,7 @@ struct xcptcontext FAR uintptr_t *ustack[ARCH_STACK_NSECTS]; #endif + #ifdef CONFIG_ARCH_KERNEL_STACK /* In this configuration, all syscalls execute from an internal kernel * stack. Why? Because when we instantiate and initialize the address @@ -293,7 +294,8 @@ struct xcptcontext * stack in place. */ - FAR uint32_t *kstack; + FAR uint32_t *ustkptr; /* Saved user stack pointer */ + FAR uint32_t *kstack; /* Allocate base of the (aligned) kernel stack */ #endif #endif }; diff --git a/arch/arm/src/armv7-a/addrenv.h b/arch/arm/src/armv7-a/addrenv.h index 0d830ed06fa..60cfb342a43 100644 --- a/arch/arm/src/armv7-a/addrenv.h +++ b/arch/arm/src/armv7-a/addrenv.h @@ -51,6 +51,12 @@ * Pre-processor Definitions ****************************************************************************/ +/* Aligned size of the kernel stack */ + +#ifdef CONFIG_ARCH_KERNEL_STACK +# define ARCH_KERNEL_STACKSIZE ((CONFIG_ARCH_KERNEL_STACKSIZE + 7) & ~7) +#endif + /* Using a 4KiB page size, each 1MiB section maps to a PTE containing * 256*2KiB entries */ diff --git a/arch/arm/src/armv7-a/arm_addrenv_kstack.c b/arch/arm/src/armv7-a/arm_addrenv_kstack.c index 2102e0ad262..852b03620b9 100644 --- a/arch/arm/src/armv7-a/arm_addrenv_kstack.c +++ b/arch/arm/src/armv7-a/arm_addrenv_kstack.c @@ -117,6 +117,8 @@ #include #include +#include "addrenv.h" + #if defined(CONFIG_ARCH_ADDRENV) && defined(CONFIG_ARCH_KERNEL_STACK) /**************************************************************************** @@ -140,27 +142,27 @@ * * Description: * This function is called when a new thread is created to allocate - * the new thread's kernel stack. + * the new thread's kernel stack. This function may be called for certain + * terminating threads which have no kernel stack. It must be tolerant of + * that case. * * Input Parameters: * tcb - The TCB of the thread that requires the kernel stack. - * stacksize - The size (in bytes) of the kernel stack needed by the - * thread. * * Returned Value: * Zero (OK) on success; a negated errno value on failure. * ****************************************************************************/ -int up_addrenv_kstackalloc(FAR struct tcb_s *tcb, size_t stacksize) +int up_addrenv_kstackalloc(FAR struct tcb_s *tcb) { - bvdbg("tcb=%p stacksize=%lu\n", tcb, (unsigned long)stacksize); + bvdbg("tcb=%p stacksize=%u\n", tcb, ARCH_KERNEL_STACKSIZE); - DEBUGASSERT(tcb && tcb->xcp.kstack == 0 && stackize > 0); + DEBUGASSERT(tcb && tcb->xcp.kstack == 0); /* Allocate the kernel stack */ - tcb->xcp.kstack = (FAR uint32_t *)kmm_memalign(8, stacksize); + tcb->xcp.kstack = (FAR uint32_t *)kmm_memalign(8, ARCH_KERNEL_STACKSIZE); if (!tcb->xcp.kstack) { bdbg("ERROR: Failed to allocate the kernel stack\n"); @@ -188,12 +190,18 @@ int up_addrenv_kstackalloc(FAR struct tcb_s *tcb, size_t stacksize) int up_addrenv_kstackfree(FAR struct tcb_s *tcb) { bvdbg("tcb=%p\n", tcb); - DEBUGASSERT(tcb && tcb->xcp.kstack); + DEBUGASSERT(tcb); - /* Free the kernel stack */ + /* Does the exiting thread have a kernel stack? */ + + if (tcb->xcp.kstack) + { + /* Yes.. Free the kernel stack */ + + kmm_free(tcb->xcp.kstack); + tcb->xcp.kstack = NULL; + } - kmm_free(tcb->xcp.kstack); - tcb->xcp.kstack = NULL; return OK; } diff --git a/arch/arm/src/armv7-a/arm_syscall.c b/arch/arm/src/armv7-a/arm_syscall.c index b5f803402c6..9fb46f38823 100644 --- a/arch/arm/src/armv7-a/arm_syscall.c +++ b/arch/arm/src/armv7-a/arm_syscall.c @@ -51,6 +51,7 @@ #include "arm.h" #include "svcall.h" +#include "addrenv.h" #include "up_internal.h" /**************************************************************************** @@ -219,13 +220,27 @@ uint32_t *arm_syscall(uint32_t *regs) #ifdef CONFIG_BUILD_KERNEL regs[REG_CPSR] = rtcb->xcp.syscall[index].cpsr; #endif - rtcb->xcp.nsyscalls = index; - /* The return value must be in R0-R1. dispatch_syscall() temporarily * moved the value for R0 into R2. */ regs[REG_R0] = regs[REG_R2]; + +#ifdef CONFIG_ARCH_KERNEL_STACK + /* If this is the outermost SYSCALL and if there is a saved user stack + * pointer, then restore the user stack pointer on this final return to + * user code. + */ + + if (index == 0 && rtcb->xcp.ustkptr != NULL) + { + regs[REG_SP] = (uint32_t)rtcb->xcp.ustkptr; + rtcb->xcp.ustkptr = NULL; + } +#endif + /* Save the new SYSCALL nesting level */ + + rtcb->xcp.nsyscalls = index; } break; @@ -421,7 +436,6 @@ uint32_t *arm_syscall(uint32_t *regs) #ifdef CONFIG_BUILD_KERNEL rtcb->xcp.syscall[index].cpsr = regs[REG_CPSR]; #endif - rtcb->xcp.nsyscalls = index + 1; regs[REG_PC] = (uint32_t)dispatch_syscall; #ifdef CONFIG_BUILD_KERNEL @@ -434,6 +448,21 @@ uint32_t *arm_syscall(uint32_t *regs) #else svcdbg("ERROR: Bad SYS call: %d\n", regs[REG_R0]); #endif + +#ifdef CONFIG_ARCH_KERNEL_STACK + /* If this is the first SYSCALL and if there is an allocated + * kernel stack, then switch to the kernel stack. + */ + + if (index == 0 && rtcb->xcp.kstack != NULL) + { + rtcb->xcp.ustkptr = (FAR uint32_t *)regs[REG_SP]; + regs[REG_SP] = (uint32_t)rtcb->xcp.kstack + ARCH_KERNEL_STACKSIZE; + } +#endif + /* Save the new SYSCALL nesting level */ + + rtcb->xcp.nsyscalls = index + 1; } break; }