diff --git a/arch/arm/include/armv6-m/irq.h b/arch/arm/include/armv6-m/irq.h index 14f6cc1bb0..be851aa938 100644 --- a/arch/arm/include/armv6-m/irq.h +++ b/arch/arm/include/armv6-m/irq.h @@ -150,7 +150,7 @@ /* This structure represents the return state from a system call */ -#ifdef CONFIG_NUTTX_KERNEL +#ifdef CONFIG_LIB_SYSCALL struct xcpt_syscall_s { uint32_t excreturn; /* The EXC_RETURN value */ @@ -192,7 +192,7 @@ struct xcptcontext # endif #endif -#ifdef CONFIG_NUTTX_KERNEL +#ifdef CONFIG_LIB_SYSCALL /* The following array holds the return address and the exc_return value * needed to return from each nested system call. */ diff --git a/arch/arm/include/armv7-a/irq.h b/arch/arm/include/armv7-a/irq.h index a7cccf607d..f21aba7345 100755 --- a/arch/arm/include/armv7-a/irq.h +++ b/arch/arm/include/armv7-a/irq.h @@ -53,7 +53,7 @@ #endif /**************************************************************************** - * Definitions + * Pre-processor Definitions ****************************************************************************/ /* IRQ Stack Frame Format: @@ -193,6 +193,20 @@ * Public Types ****************************************************************************/ +#ifndef __ASSEMBLY__ + +/* This structure represents the return state from a system call */ + +#ifdef CONFIG_LIB_SYSCALL +struct xcpt_syscall_s +{ +#ifdef CONFIG_NUTTX_KERNEL + uint32_t cpsr; /* The CPSR value */ +#endif + uint32_t sysreturn; /* The return PC */ +}; +#endif + /* This struct defines the way the registers are stored. We need to save: * * 1 CPSR @@ -242,6 +256,15 @@ struct xcptcontext uintptr_t far; #endif +#ifdef CONFIG_LIB_SYSCALL + /* The following array holds the return address and the exc_return value + * needed to return from each nested system call. + */ + + uint8_t nsyscalls; + struct xcpt_syscall_s syscall[CONFIG_SYS_NNEST]; +#endif + #ifdef CONFIG_ARCH_ADDRENV /* This table holds the physical address of the level 2 page table used * to map the thread's stack memory. This array will be initially of @@ -256,6 +279,8 @@ struct xcptcontext }; #endif +#endif /* __ASSEMBLY__ */ + /**************************************************************************** * Inline functions ****************************************************************************/ diff --git a/arch/arm/include/armv7-m/irq.h b/arch/arm/include/armv7-m/irq.h index d369fd7346..818af65609 100644 --- a/arch/arm/include/armv7-m/irq.h +++ b/arch/arm/include/armv7-m/irq.h @@ -108,7 +108,7 @@ /* This structure represents the return state from a system call */ -#ifdef CONFIG_NUTTX_KERNEL +#ifdef CONFIG_LIB_SYSCALL struct xcpt_syscall_s { uint32_t excreturn; /* The EXC_RETURN value */ @@ -154,7 +154,7 @@ struct xcptcontext # endif #endif -#ifdef CONFIG_NUTTX_KERNEL +#ifdef CONFIG_LIB_SYSCALL /* The following array holds the return address and the exc_return value * needed to return from each nested system call. */ diff --git a/arch/arm/src/armv6-m/up_svcall.c b/arch/arm/src/armv6-m/up_svcall.c index ad9da62d48..b88260281b 100644 --- a/arch/arm/src/armv6-m/up_svcall.c +++ b/arch/arm/src/armv6-m/up_svcall.c @@ -273,7 +273,7 @@ int up_svcall(int irq, FAR void *context) * unprivileged thread mode. */ -#ifdef CONFIG_NUTTX_KERNEL +#ifdef CONFIG_LIB_SYSCALL case SYS_syscall_return: { struct tcb_s *rtcb = sched_self(); @@ -488,11 +488,15 @@ int up_svcall(int irq, FAR void *context) { svcdbg("SVCall Return:\n"); svcdbg(" R0: %08x %08x %08x %08x %08x %08x %08x %08x\n", - current_regs[REG_R0], current_regs[REG_R1], current_regs[REG_R2], current_regs[REG_R3], - current_regs[REG_R4], current_regs[REG_R5], current_regs[REG_R6], current_regs[REG_R7]); + current_regs[REG_R0], current_regs[REG_R1], + current_regs[REG_R2], current_regs[REG_R3], + current_regs[REG_R4], current_regs[REG_R5], + current_regs[REG_R6], current_regs[REG_R7]); svcdbg(" R8: %08x %08x %08x %08x %08x %08x %08x %08x\n", - current_regs[REG_R8], current_regs[REG_R9], current_regs[REG_R10], current_regs[REG_R11], - current_regs[REG_R12], current_regs[REG_R13], current_regs[REG_R14], current_regs[REG_R15]); + current_regs[REG_R8], current_regs[REG_R9], + current_regs[REG_R10], current_regs[REG_R11], + current_regs[REG_R12], current_regs[REG_R13], + current_regs[REG_R14], current_regs[REG_R15]); #ifdef CONFIG_NUTTX_KERNEL svcdbg(" PSR: %08x PRIMASK: %08x EXC_RETURN: %08x\n", current_regs[REG_XPSR], current_regs[REG_PRIMASK], diff --git a/arch/arm/src/armv7-a/arm_syscall.c b/arch/arm/src/armv7-a/arm_syscall.c index a165db1d1f..f030205b34 100644 --- a/arch/arm/src/armv7-a/arm_syscall.c +++ b/arch/arm/src/armv7-a/arm_syscall.c @@ -1,7 +1,7 @@ /**************************************************************************** * arch/arm/src/armv7-a/arm_syscall.c * - * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Copyright (C) 2013-2014 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -36,15 +36,21 @@ /**************************************************************************** * Included Files ****************************************************************************/ - #include #include +#include +#include #include #include +#include -#include "up_arch.h" +#ifdef CONFIG_LIB_SYSCALL +# include +#endif + +#include "syscall.h" #include "up_internal.h" /**************************************************************************** @@ -65,9 +71,58 @@ ****************************************************************************/ /**************************************************************************** - * vectors + * Call the stub function corresponding to the system call. NOTE the non- + * standard parameter passing: + * + * R0 = SYS_ call number + * R1 = parm0 + * R2 = parm1 + * R3 = parm2 + * R4 = parm3 + * R5 = parm4 + * R6 = parm5 + * + * The values of R4-R5 may be preserved in the proxy called by the user + * code if they are used (but otherwise will not be). + * + * Register usage: + * + * R0 - Need not be preserved. + * R1-R3 - Need to be preserved until the stub is called. The values of + * R0 and R1 returned by the stub must be preserved. + * R4-R11 must be preserved to support the expectations of the user-space + * callee. R4-R6 may have been preserved by the proxy, but don't know + * for sure. + * R12 - Need not be preserved + * R13 - (stack pointer) + * R14 - Need not be preserved + * R15 - (PC) + * ****************************************************************************/ +#ifdef CONFIG_LIB_SYSCALL +static void dispatch_syscall(void) naked_function; +static void dispatch_syscall(void) +{ + __asm__ __volatile__ + ( + " sub sp, sp, #16\n" /* Create a stack frame to hold 3 parms + lr */ + " str r4, [sp, #0]\n" /* Move parameter 4 (if any) into position */ + " str r5, [sp, #4]\n" /* Move parameter 5 (if any) into position */ + " str r6, [sp, #8]\n" /* Move parameter 6 (if any) into position */ + " str lr, [sp, #12]\n" /* Save lr in the stack frame */ + " ldr ip, =g_stublookup\n" /* R12=The base of the stub lookup table */ + " ldr ip, [ip, r0, lsl #2]\n" /* R12=The address of the stub for this syscall */ + " blx ip\n" /* Call the stub (modifies lr)*/ + " ldr lr, [sp, #12]\n" /* Restore lr */ + " add sp, sp, #16\n" /* Destroy the stack frame */ + " mov r2, r0\n" /* R2=Save return value in R2 */ + " mov r0, #3\n" /* R0=SYS_syscall_return */ + " svc 0" /* Return from the syscall */ + ); +} +#endif + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -88,9 +143,294 @@ * ****************************************************************************/ +#ifdef CONFIG_LIB_SYSCALL +uint32_t *arm_syscall(uint32_t *regs) +{ + uint32_t cmd; +#ifdef CONFIG_NUTTX_KERNEL + uint32_t cpsr; +#endif + + DEBUGASSERT(regs && regs == current_regs); + cmd = regs[REG_R0]; + + /* The SVCall software interrupt is called with R0 = system call command + * and R1..R7 = variable number of arguments depending on the system call. + */ + +#if defined(CONFIG_DEBUG_SYSCALL) + svcdbg("SYSCALL Entry: regs: %p cmd: %d\n", regs, cmd); + svcdbg(" R0: %08x %08x %08x %08x %08x %08x %08x %08x\n", + regs[REG_R0], regs[REG_R1], regs[REG_R2], regs[REG_R3], + regs[REG_R4], regs[REG_R5], regs[REG_R6], regs[REG_R7]); + svcdbg(" R8: %08x %08x %08x %08x %08x %08x %08x %08x\n", + regs[REG_R8], regs[REG_R9], regs[REG_R10], regs[REG_R11], + regs[REG_R12], regs[REG_R13], regs[REG_R14], regs[REG_R15]); + svcdbg("CPSR: %08x\n", regs[REG_CPSR]); +#endif + + /* Handle the SVCall according to the command in R0 */ + + switch (cmd) + { + /* R0=SYS_syscall_return: This a syscall return command: + * + * void up_syscall_return(void); + * + * At this point, the following values are saved in context: + * + * R0 = SYS_syscall_return + * + * We need to restore the saved return address and return in + * unprivileged thread mode. + */ + +#ifdef CONFIG_NUTTX_KERNEL + case SYS_syscall_return: + { + struct tcb_s *rtcb = sched_self(); + int index = (int)rtcb->xcp.nsyscalls - 1; + + /* Make sure that there is a saved syscall return address. */ + + DEBUGASSERT(index >= 0); + + /* Setup to return to the saved syscall return address in + * the original mode. + */ + + regs[REG_PC] = rtcb->xcp.syscall[index].sysreturn; + regs[REG_CPSR] = rtcb->xcp.syscall[index].cpsr; + 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]; + } + break; +#endif + + /* R0=SYS_task_start: This a user task start + * + * void up_task_start(main_t taskentry, int argc, FAR char *argv[]) noreturn_function; + * + * At this point, the following values are saved in context: + * + * R0 = SYS_task_start + * R1 = taskentry + * R2 = argc + * R3 = argv + */ + +#ifdef CONFIG_NUTTX_KERNEL + case SYS_task_start: + { + /* Set up to return to the user-space task start-up function in + * unprivileged mode. + */ + + regs[REG_PC] = (uint32_t)USERSPACE->task_startup; + regval = regs[REG_CPSR] & ~PSR_MODE_MASK; + regs[REG_CPSR] = regval | PSR_MODE_USR; + + /* Change the parameter ordering to match the expectation of struct + * userpace_s task_startup: + */ + + regs[REG_R0] = regs[REG_R1]; /* Task entry */ + regs[REG_R1] = regs[REG_R2]; /* argc */ + regs[REG_R2] = regs[REG_R3]; /* argv */ + } + break; +#endif + + /* R0=SYS_pthread_start: This a user pthread start + * + * void up_pthread_start(pthread_startroutine_t entrypt, pthread_addr_t arg) noreturn_function; + * + * At this point, the following values are saved in context: + * + * R0 = SYS_pthread_start + * R1 = entrypt + * R2 = arg + */ + +#if defined(CONFIG_NUTTX_KERNEL) && !defined(CONFIG_DISABLE_PTHREAD) + case SYS_pthread_start: + { + /* Set up to return to the user-space pthread start-up function in + * unprivileged mode. + */ + + regs[REG_PC] = (uint32_t)USERSPACE->pthread_startup; + regval = regs[REG_CPSR] & ~PSR_MODE_MASK; + regs[REG_CPSR] = regval | PSR_MODE_USR; + + /* Change the parameter ordering to match the expectation of struct + * userpace_s pthread_startup: + */ + + regs[REG_R0] = regs[REG_R1]; /* pthread entry */ + regs[REG_R1] = regs[REG_R2]; /* arg */ + } + break; +#endif + + /* R0=SYS_signal_handler: This a user signal handler callback + * + * void signal_handler(_sa_sigaction_t sighand, int signo, + * FAR siginfo_t *info, FAR void *ucontext); + * + * At this point, the following values are saved in context: + * + * R0 = SYS_signal_handler + * R1 = sighand + * R2 = signo + * R3 = info + * ucontext (on the stack) + */ + +#if defined(CONFIG_NUTTX_KERNEL) && !defined(CONFIG_DISABLE_SIGNALS) + case SYS_signal_handler: + { + struct tcb_s *rtcb = sched_self(); + + /* Remember the caller's return address */ + + DEBUGASSERT(rtcb->xcp.sigreturn == 0); + rtcb->xcp.sigreturn = regs[REG_PC]; + + /* Set up to return to the user-space pthread start-up function in + * unprivileged mode. + */ + + regs[REG_PC] = (uint32_t)USERSPACE->signal_handler; + regval = regs[REG_CPSR] & ~PSR_MODE_MASK; + regs[REG_CPSR] = regval | PSR_MODE_USR; + + /* Change the parameter ordering to match the expectation of struct + * userpace_s signal_handler. + */ + + regs[REG_R0] = regs[REG_R1]; /* sighand */ + regs[REG_R1] = regs[REG_R2]; /* signal */ + regs[REG_R2] = regs[REG_R3]; /* info */ + + /* The last parameter, ucontext, is trickier. The ucontext + * parameter will reside at an offset of 4 from the stack pointer. + */ + + regs[REG_R3] = *(uint32_t*)(regs[REG_SP+4]); + } + break; +#endif + + /* R0=SYS_signal_handler_return: This a user signal handler callback + * + * void signal_handler_return(void); + * + * At this point, the following values are saved in context: + * + * R0 = SYS_signal_handler_return + */ + +#if defined(CONFIG_NUTTX_KERNEL) && !defined(CONFIG_DISABLE_SIGNALS) + case SYS_signal_handler_return: + { + struct tcb_s *rtcb = sched_self(); + + /* Set up to return to the kernel-mode signal dispatching logic. */ + + DEBUGASSERT(rtcb->xcp.sigreturn != 0); + + regs[REG_PC] = rtcb->xcp.sigreturn; + regval = regs[REG_CPSR] & ~PSR_MODE_MASK; + regs[REG_CPSR] = regval | PSR_MODE_SVC; + rtcb->xcp.sigreturn = 0; + } + break; +#endif + + /* This is not an architecture-specific system call. If NuttX is built + * as a standalone kernel with a system call interface, then all of the + * additional system calls must be handled as in the default case. + */ + + default: + { +#ifdef CONFIG_LIB_SYSCALL + FAR struct tcb_s *rtcb = sched_self(); + int index = rtcb->xcp.nsyscalls; + + /* Verify that the SYS call number is within range */ + + DEBUGASSERT(cmd >= CONFIG_SYS_RESERVED && cmd < SYS_maxsyscall); + + /* Make sure that there is a no saved syscall return address. We + * cannot yet handle nested system calls. + */ + + DEBUGASSERT(index < CONFIG_SYS_NNEST); + + /* Setup to return to dispatch_syscall in privileged mode. */ + + rtcb->xcp.syscall[index].sysreturn = regs[REG_PC]; +#ifdef CONFIG_NUTTX_KERNEL + rtcb->xcp.syscall[index].cpsr = regs[REG_CPSR]; +#endif + rtcb->xcp.nsyscalls = index + 1; + + regs[REG_PC] = (uint32_t)dispatch_syscall; +#ifdef CONFIG_NUTTX_KERNEL + regval = regs[REG_CPSR] & ~PSR_MODE_MASK; + regs[REG_CPSR] = regval | PSR_MODE_SVC; +#endif + /* Offset R0 to account for the reserved values */ + + regs[REG_R0] -= CONFIG_SYS_RESERVED; +#else + slldbg("ERROR: Bad SYS call: %d\n", regs[REG_R0]); +#endif + } + break; + } + + /* Report what happened. That might difficult in the case of a context switch */ + +#if defined(CONFIG_DEBUG_SYSCALL) + if (regs != current_regs) + { + svcdbg("SYSCALL Return: current_regs: %p\n", current_regs); + svcdbg(" R0: %08x %08x %08x %08x %08x %08x %08x %08x\n", + current_regs[REG_R0], current_regs[REG_R1], + current_regs[REG_R2], current_regs[REG_R3], + current_regs[REG_R4], current_regs[REG_R5], + current_regs[REG_R6], current_regs[REG_R7]); + svcdbg(" R8: %08x %08x %08x %08x %08x %08x %08x %08x\n", + current_regs[REG_R8], current_regs[REG_R9], + current_regs[REG_R10], current_regs[REG_R11], + current_regs[REG_R12], current_regs[REG_R13], + current_regs[REG_R14], current_regs[REG_R15]); + svcdbg("CPSR: %08x\n", current_regs[REG_CPSR]); + } + else + { + svcdbg("SYSCALL Return: %d\n", regs[REG_R0]); + } +#endif + + return OK; +} + +#else + uint32_t *arm_syscall(uint32_t *regs) { lldbg("Syscall from 0x%x\n", regs[REG_PC]); current_regs = regs; PANIC(); } + +#endif diff --git a/arch/arm/src/armv7-m/up_svcall.c b/arch/arm/src/armv7-m/up_svcall.c index 9909057d3f..4a87401c15 100644 --- a/arch/arm/src/armv7-m/up_svcall.c +++ b/arch/arm/src/armv7-m/up_svcall.c @@ -272,7 +272,7 @@ int up_svcall(int irq, FAR void *context) * unprivileged thread mode. */ -#ifdef CONFIG_NUTTX_KERNEL +#ifdef CONFIG_LIB_SYSCALL case SYS_syscall_return: { struct tcb_s *rtcb = sched_self(); @@ -487,11 +487,15 @@ int up_svcall(int irq, FAR void *context) { svcdbg("SVCall Return:\n"); svcdbg(" R0: %08x %08x %08x %08x %08x %08x %08x %08x\n", - current_regs[REG_R0], current_regs[REG_R1], current_regs[REG_R2], current_regs[REG_R3], - current_regs[REG_R4], current_regs[REG_R5], current_regs[REG_R6], current_regs[REG_R7]); + current_regs[REG_R0], current_regs[REG_R1], + current_regs[REG_R2], current_regs[REG_R3], + current_regs[REG_R4], current_regs[REG_R5], + current_regs[REG_R6], current_regs[REG_R7]); svcdbg(" R8: %08x %08x %08x %08x %08x %08x %08x %08x\n", - current_regs[REG_R8], current_regs[REG_R9], current_regs[REG_R10], current_regs[REG_R11], - current_regs[REG_R12], current_regs[REG_R13], current_regs[REG_R14], current_regs[REG_R15]); + current_regs[REG_R8], current_regs[REG_R9], + current_regs[REG_R10], current_regs[REG_R11], + current_regs[REG_R12], current_regs[REG_R13], + current_regs[REG_R14], current_regs[REG_R15]); # ifdef REG_EXC_RETURN svcdbg(" PSR: %08x EXC_RETURN: %08x\n", current_regs[REG_XPSR], current_regs[REG_EXC_RETURN]);