diff --git a/arch/risc-v/include/irq.h b/arch/risc-v/include/irq.h index ed3da17edb6..f23d7729fc1 100644 --- a/arch/risc-v/include/irq.h +++ b/arch/risc-v/include/irq.h @@ -96,14 +96,6 @@ /* Configuration ************************************************************/ -/* If this is a kernel build, how many nested system calls should we - * support? - */ - -#ifndef CONFIG_SYS_NNEST -# define CONFIG_SYS_NNEST 2 -#endif - /* Processor PC */ #define REG_EPC_NDX 0 @@ -539,18 +531,6 @@ #ifndef __ASSEMBLY__ -/* This structure represents the return state from a system call */ - -#ifdef CONFIG_LIB_SYSCALL -struct xcpt_syscall_s -{ - uintptr_t sysreturn; /* The return PC */ -#ifndef CONFIG_BUILD_FLAT - uintptr_t int_ctx; /* Interrupt context (i.e. m-/sstatus) */ -#endif -}; -#endif - /* The following structure is included in the TCB and defines the complete * state of the thread. */ @@ -582,16 +562,6 @@ struct xcptcontext uintptr_t sigreturn; #endif -#ifdef CONFIG_LIB_SYSCALL - /* The following array holds information needed to return from each nested - * system call. - */ - - uint8_t nsyscalls; - struct xcpt_syscall_s syscall[CONFIG_SYS_NNEST]; - -#endif - #ifdef CONFIG_ARCH_ADDRENV #ifdef CONFIG_ARCH_KERNEL_STACK /* In this configuration, all syscalls execute from an internal kernel diff --git a/arch/risc-v/include/syscall.h b/arch/risc-v/include/syscall.h index c7d5375a44e..9d220067608 100644 --- a/arch/risc-v/include/syscall.h +++ b/arch/risc-v/include/syscall.h @@ -77,15 +77,6 @@ #define SYS_switch_context (2) -#ifdef CONFIG_LIB_SYSCALL -/* SYS call 3: - * - * void riscv_syscall_return(void); - */ - -#define SYS_syscall_return (3) -#endif /* CONFIG_LIB_SYSCALL */ - #ifndef CONFIG_BUILD_FLAT /* SYS call 4: * diff --git a/arch/risc-v/src/common/riscv_exception_common.S b/arch/risc-v/src/common/riscv_exception_common.S index cc08054094f..90359470314 100644 --- a/arch/risc-v/src/common/riscv_exception_common.S +++ b/arch/risc-v/src/common/riscv_exception_common.S @@ -30,6 +30,10 @@ #include +#ifdef CONFIG_LIB_SYSCALL +# include +#endif + #include "chip.h" #include "riscv_percpu.h" @@ -73,6 +77,7 @@ .section EXCEPTION_SECTION .global exception_common .global return_from_exception + .global return_from_syscall .align 8 exception_common: @@ -99,24 +104,69 @@ exception_common: addi sp, sp, -XCPTCONTEXT_SIZE save_ctx sp - csrr s0, CSR_STATUS - REGSTORE s0, REG_INT_CTX(sp) /* status */ + csrr s0, CSR_STATUS /* s0=status */ + csrr s1, CSR_EPC /* s1=exception PC */ + csrr s2, CSR_CAUSE /* s2=cause */ #ifdef CONFIG_ARCH_KERNEL_STACK - csrr s0, CSR_SCRATCH - REGLOAD s0, RISCV_PERCPU_USP(s0) + csrr s3, CSR_SCRATCH + REGLOAD s3, RISCV_PERCPU_USP(s3) #else - addi s0, sp, XCPTCONTEXT_SIZE + addi s3, sp, XCPTCONTEXT_SIZE #endif - REGSTORE s0, REG_X2(sp) /* original SP */ + REGSTORE s0, REG_INT_CTX(sp) + REGSTORE s1, REG_EPC(sp) + REGSTORE s3, REG_SP(sp) - csrr s0, CSR_EPC - REGSTORE s0, REG_EPC(sp) /* exception PC */ +#ifdef CONFIG_LIB_SYSCALL + /* Check whether it is an exception or interrupt */ + + blt s2, x0, handle_irq /* If cause < 0 it is interrupt */ + + /* Is it a system call ? */ + + li s3, RISCV_IRQ_ECALLU /* Is it a system call ? */ + bne s2, s3, handle_irq + + /* Is it one of the reserved system calls ? */ + + li s3, CONFIG_SYS_RESERVED + blt a0, s3, handle_irq /* If a0 < CONFIG_SYS_RESERVED */ + + /* It is a system call, re-enable interrupts if they were enabled */ + + andi s3, s0, STATUS_PIE + beqz s3, 1f + csrs CSR_STATUS, STATUS_IE + +1: + addi s1, s1, 0x4 /* Must move EPC forward by +4 */ + REGSTORE s1, REG_EPC(sp) /* Updated EPC to user context */ + + csrr tp, CSR_SCRATCH /* Load kernel TP */ + REGLOAD tp, RISCV_PERCPU_TCB(tp) + + call x1, dispatch_syscall /* Dispatch the system call */ + +return_from_syscall: + + /* System call is done, disable interrupts */ + + csrc CSR_STATUS, STATUS_IE + + /* Clean up after system call */ + + REGSTORE a0, REG_A0(sp) /* Syscall return value to user context */ + mv a0, sp /* Return to same context */ + tail return_from_exception + +handle_irq: +#endif /* Setup arg0(exception cause), arg1(context) */ - csrr a0, CSR_CAUSE /* exception cause */ + mv a0, s2 /* exception cause */ mv a1, sp /* context = sp */ #if CONFIG_ARCH_INTERRUPTSTACK > 15 @@ -152,9 +202,17 @@ return_from_exception: REGLOAD s0, REG_EPC(sp) /* restore sepc */ csrw CSR_EPC, s0 - REGLOAD s0, REG_INT_CTX(sp) /* restore sstatus */ + REGLOAD s0, REG_INT_CTX(sp) /* restore status */ csrw CSR_STATUS, s0 +#ifdef CONFIG_LIB_SYSCALL + /* Store tcb to scratch register */ + + call x1, nxsched_self + csrr s1, CSR_SCRATCH + REGSTORE a0, RISCV_PERCPU_TCB(s1) +#endif + #ifdef CONFIG_ARCH_KERNEL_STACK /* Returning to userspace ? */ diff --git a/arch/risc-v/src/common/riscv_fork.c b/arch/risc-v/src/common/riscv_fork.c index 0dd41aecdc1..0c19f9f19c2 100644 --- a/arch/risc-v/src/common/riscv_fork.c +++ b/arch/risc-v/src/common/riscv_fork.c @@ -246,29 +246,6 @@ pid_t riscv_fork(const struct fork_s *context) fregs[REG_FS11] = context->fs11; /* Saved register fs11 */ #endif -#ifdef CONFIG_LIB_SYSCALL - /* If we got here via a syscall, then we are going to have to setup some - * syscall return information as well. - */ - - if (parent->xcp.nsyscalls > 0) - { - int index; - for (index = 0; index < parent->xcp.nsyscalls; index++) - { - child->cmn.xcp.syscall[index].sysreturn = - parent->xcp.syscall[index].sysreturn; - -#ifndef CONFIG_BUILD_FLAT - child->cmn.xcp.syscall[index].int_ctx = - parent->xcp.syscall[index].int_ctx; -#endif - } - - child->cmn.xcp.nsyscalls = parent->xcp.nsyscalls; - } -#endif /* CONFIG_LIB_SYSCALL */ - /* And, finally, start the child task. On a failure, nxtask_start_fork() * will discard the TCB by calling nxtask_abort_fork(). */ diff --git a/arch/risc-v/src/common/riscv_internal.h b/arch/risc-v/src/common/riscv_internal.h index d2a09e456e4..abb464604ac 100644 --- a/arch/risc-v/src/common/riscv_internal.h +++ b/arch/risc-v/src/common/riscv_internal.h @@ -438,16 +438,6 @@ void *riscv_perform_syscall(uintptr_t *regs); #define riscv_switchcontext(prev, next) \ sys_call2(SYS_switch_context, (uintptr_t)prev, (uintptr_t)next) -#ifdef CONFIG_BUILD_KERNEL -/* SYS call 3: - * - * void riscv_syscall_return(void); - */ - -#define riscv_syscall_return() sys_call0(SYS_syscall_return) - -#endif /* CONFIG_BUILD_KERNEL */ - #undef EXTERN #ifdef __cplusplus } diff --git a/arch/risc-v/src/common/riscv_swint.c b/arch/risc-v/src/common/riscv_swint.c index 0a262fe7003..5177cec5bd4 100644 --- a/arch/risc-v/src/common/riscv_swint.c +++ b/arch/risc-v/src/common/riscv_swint.c @@ -48,10 +48,16 @@ * Pre-processor Definitions ****************************************************************************/ +#ifdef CONFIG_LIB_SYSCALL +# define TCB_FLAGS_OFFSET offsetof(struct tcb_s, flags) +#endif + /**************************************************************************** * Private Functions ****************************************************************************/ +#ifdef CONFIG_LIB_SYSCALL + /**************************************************************************** * Name: dispatch_syscall * @@ -69,14 +75,38 @@ * ****************************************************************************/ -#ifdef CONFIG_LIB_SYSCALL -static void dispatch_syscall(void) naked_function; -static void dispatch_syscall(void) +uintptr_t dispatch_syscall(unsigned int nbr, uintptr_t parm1, + uintptr_t parm2, uintptr_t parm3, + uintptr_t parm4, uintptr_t parm5, + uintptr_t parm6) { + register long a0 asm("a0") = (long)(nbr); + register long a1 asm("a1") = (long)(parm1); + register long a2 asm("a2") = (long)(parm2); + register long a3 asm("a3") = (long)(parm3); + register long a4 asm("a4") = (long)(parm4); + register long a5 asm("a5") = (long)(parm5); + register long a6 asm("a6") = (long)(parm6); + + uintptr_t ret; + + /* Valid system call ? */ + + if (a0 > SYS_maxsyscall) + { + /* Nope, get out */ + + return -ENOSYS; + } + + /* ra gets clobbered below, but it does not matter */ + asm volatile ( - "addi sp, sp, -" STACK_FRAME_SIZE "\n" /* Create a stack frame to hold ra */ - REGSTORE " ra, 0(sp)\n" /* Save ra in the stack frame */ + REGLOAD " t0, %1(tp)\n" /* Load tcb->flags */ + "ori t0, t0, %2\n" /* tcb->flags |= TCB_FLAG_SYSCALL */ + REGSTORE " t0, %1(tp)\n" + "addi a0, a0, -%3\n" /* Offset a0 to account for the reserved syscalls */ "la t0, g_stublookup\n" /* t0=The base of the stub lookup table */ #ifdef CONFIG_ARCH_RV32 "slli a0, a0, 2\n" /* a0=Offset for the stub lookup table */ @@ -86,16 +116,26 @@ static void dispatch_syscall(void) "add t0, t0, a0\n" /* t0=The address in the table */ REGLOAD " t0, 0(t0)\n" /* t0=The address of the stub for this syscall */ "jalr ra, t0\n" /* Call the stub (modifies ra) */ - REGLOAD " ra, 0(sp)\n" /* Restore ra */ - "addi sp, sp, " STACK_FRAME_SIZE "\n" /* Destroy the stack frame */ - "mv a2, a0\n" /* a2=Save return value in a0 */ - "li a0, 3\n" /* a0=SYS_syscall_return (3) */ -#ifdef CONFIG_ARCH_USE_S_MODE - "j sys_call2" /* Return from the syscall */ -#else - "ecall" /* Return from the syscall */ -#endif + REGLOAD " t0, %1(tp)\n" /* Load tcb->flags */ + "andi t0, t0, ~%2\n" /* tcb->flags &= ~TCB_FLAG_SYSCALL */ + REGSTORE " t0, %1(tp)\n" + : "+r"(a0) + : "i"(TCB_FLAGS_OFFSET), + "i"(TCB_FLAG_SYSCALL), + "i"(CONFIG_SYS_RESERVED), + "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5), "r"(a6) + : "t0", "memory" ); + + /* a0 gets clobbered below, save it locally here */ + + ret = a0; + + /* Unmask any pending signals now */ + + nxsig_unmask_pendingsignal(); + + return ret; } #endif @@ -185,70 +225,6 @@ int riscv_swint(int irq, void *context, void *arg) } break; - /* A0=SYS_syscall_return: This is a SYSCALL return command: - * - * void up_sycall_return(void); - * - * At this point, the following values are saved in context: - * - * A0 = SYS_syscall_return - * - * We need to restore the saved return address and return in - * unprivileged thread mode. - */ - -#ifdef CONFIG_LIB_SYSCALL - case SYS_syscall_return: - { - struct tcb_s *rtcb = nxsched_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_EPC] = rtcb->xcp.syscall[index].sysreturn; -#ifndef CONFIG_BUILD_FLAT - regs[REG_INT_CTX] = rtcb->xcp.syscall[index].int_ctx; -#endif - - /* The return value must be in A0-A1. - * dispatch_syscall() temporarily moved the value for R0 into A2. - */ - - regs[REG_A0] = regs[REG_A2]; - -#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] = (uintptr_t)rtcb->xcp.ustkptr; - rtcb->xcp.ustkptr = NULL; - } -#endif - - /* Save the new SYSCALL nesting level */ - - rtcb->xcp.nsyscalls = index; - - /* Handle any signal actions that were deferred while processing - * the system call. - */ - - rtcb->flags &= ~TCB_FLAG_SYSCALL; - nxsig_unmask_pendingsignal(); - } - break; -#endif - /* R0=SYS_task_start: This a user task start * * void up_task_start(main_t taskentry, int argc, @@ -391,6 +367,7 @@ int riscv_swint(int irq, void *context, void *arg) if (rtcb->xcp.kstack != NULL) { uintptr_t usp; + uintptr_t *usr_regs; /* Store the current kernel stack pointer so it is not lost */ @@ -398,7 +375,9 @@ int riscv_swint(int irq, void *context, void *arg) /* Copy "info" into user stack */ - usp = rtcb->xcp.saved_regs[REG_SP]; + usr_regs = (uintptr_t *)((uintptr_t)rtcb->xcp.ktopstk - + XCPTCONTEXT_SIZE); + usp = usr_regs[REG_SP]; /* Create a frame for info and copy the kernel info */ @@ -454,66 +433,9 @@ int riscv_swint(int irq, void *context, void *arg) break; #endif - /* This is not an architecture-specify 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 - struct tcb_s *rtcb = nxsched_self(); - int index = rtcb->xcp.nsyscalls; - /* Verify that the SYS call number is within range */ - - DEBUGASSERT(CURRENT_REGS[REG_A0] < SYS_maxsyscall); - - /* Make sure that we got here 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_EPC]; -#ifndef CONFIG_BUILD_FLAT - rtcb->xcp.syscall[index].int_ctx = regs[REG_INT_CTX]; -#endif - - rtcb->xcp.nsyscalls = index + 1; - - regs[REG_EPC] = (uintptr_t)dispatch_syscall; - -#ifndef CONFIG_BUILD_FLAT - regs[REG_INT_CTX] |= STATUS_PPP; /* Privileged mode */ -#endif - - /* Offset A0 to account for the reserved values */ - - regs[REG_A0] -= CONFIG_SYS_RESERVED; - - /* Indicate that we are in a syscall handler. */ - - rtcb->flags |= TCB_FLAG_SYSCALL; -#else - svcerr("ERROR: Bad SYS call: %" PRIdPTR "\n", regs[REG_A0]); -#endif - -#ifdef CONFIG_ARCH_KERNEL_STACK - /* If this is the first level system call, we must store the user - * stack pointer so it doesn't get lost. - */ - - if (index == 0 && rtcb->xcp.kstack != NULL) - { - DEBUGASSERT(rtcb->xcp.ustkptr == NULL); - rtcb->xcp.ustkptr = (uintptr_t *)regs[REG_SP]; - regs[REG_SP] = (uintptr_t)regs; - } -#endif - } + DEBUGPANIC(); break; }