diff --git a/arch/risc-v/include/irq.h b/arch/risc-v/include/irq.h index a8a3bb4db31..638e056b152 100644 --- a/arch/risc-v/include/irq.h +++ b/arch/risc-v/include/irq.h @@ -517,6 +517,22 @@ struct xcptcontext 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 + * stack. Why? Because when we instantiate and initialize the address + * environment of the new user process, we will temporarily lose the + * address environment of the old user process, including its stack + * contents. The kernel C logic will crash immediately with no valid + * stack in place. + */ + + uintptr_t *ustkptr; /* Saved user stack pointer */ + uintptr_t *kstack; /* Allocate base of the (aligned) kernel stack */ + uintptr_t *kstkptr; /* Saved kernel stack pointer */ +#endif #endif /* Register save area */ diff --git a/arch/risc-v/src/common/addrenv.h b/arch/risc-v/src/common/addrenv.h new file mode 100644 index 00000000000..fc891389843 --- /dev/null +++ b/arch/risc-v/src/common/addrenv.h @@ -0,0 +1,52 @@ +/**************************************************************************** + * arch/risc-v/src/common/addrenv.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_RISC_V_SRC_COMMON_ADDRENV_H +#define __ARCH_RISC_V_SRC_COMMON_ADDRENV_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include "riscv_internal.h" + +#ifdef CONFIG_ARCH_ADDRENV + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Aligned size of the kernel stack */ + +#ifdef CONFIG_ARCH_KERNEL_STACK +# define ARCH_KERNEL_STACKSIZE STACK_ALIGN_UP(CONFIG_ARCH_KERNEL_STACKSIZE) +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#endif /* CONFIG_ARCH_ADDRENV */ +#endif /* __ARCH_RISC_V_SRC_COMMON_ADDRENV_H */ diff --git a/arch/risc-v/src/common/riscv_addrenv_kstack.c b/arch/risc-v/src/common/riscv_addrenv_kstack.c new file mode 100644 index 00000000000..c0fd0f0a155 --- /dev/null +++ b/arch/risc-v/src/common/riscv_addrenv_kstack.c @@ -0,0 +1,111 @@ +/**************************************************************************** + * arch/risc-v/src/common/riscv_addrenv_kstack.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "addrenv.h" +#include "riscv_internal.h" + +#if defined(CONFIG_ARCH_ADDRENV) && defined(CONFIG_ARCH_KERNEL_STACK) + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_addrenv_kstackalloc + * + * Description: + * This function is called when a new thread is created to allocate + * 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. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int up_addrenv_kstackalloc(FAR struct tcb_s *tcb) +{ + DEBUGASSERT(tcb && tcb->xcp.kstack == NULL); + + /* Allocate the kernel stack */ + + tcb->xcp.kstack = (uintptr_t *)kmm_memalign(STACK_ALIGNMENT, + ARCH_KERNEL_STACKSIZE); + if (!tcb->xcp.kstack) + { + berr("ERROR: Failed to allocate the kernel stack\n"); + return -ENOMEM; + } + + return OK; +} + +/**************************************************************************** + * Name: up_addrenv_kstackfree + * + * Description: + * This function is called when any thread exits. This function frees + * the kernel stack. + * + * Input Parameters: + * tcb - The TCB of the thread that no longer requires the kernel stack. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int up_addrenv_kstackfree(FAR struct tcb_s *tcb) +{ + DEBUGASSERT(tcb); + + /* 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; + } + + return OK; +} + +#endif /* CONFIG_ARCH_ADDRENV && CONFIG_ARCH_KERNEL_STACK */ diff --git a/arch/risc-v/src/common/riscv_swint.c b/arch/risc-v/src/common/riscv_swint.c index 95d20e86a3a..0bff030028d 100644 --- a/arch/risc-v/src/common/riscv_swint.c +++ b/arch/risc-v/src/common/riscv_swint.c @@ -41,6 +41,7 @@ #include "signal/signal.h" #include "riscv_internal.h" +#include "addrenv.h" /**************************************************************************** * Pre-processor Definitions @@ -286,6 +287,19 @@ int riscv_swint(int irq, void *context, void *arg) 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; @@ -415,6 +429,24 @@ int riscv_swint(int irq, void *context, void *arg) regs[REG_A1] = regs[REG_A2]; /* signal */ regs[REG_A2] = regs[REG_A3]; /* info */ regs[REG_A3] = regs[REG_A4]; /* ucontext */ + +#ifdef CONFIG_ARCH_KERNEL_STACK + /* If we are signalling a user process, then we must be operating + * on the kernel stack now. We need to switch back to the user + * stack before dispatching the signal handler to the user code. + * The existence of an allocated kernel stack is sufficient + * information to make this decision. + */ + + if (rtcb->xcp.kstack != NULL) + { + DEBUGASSERT(rtcb->xcp.kstkptr == NULL && + rtcb->xcp.ustkptr != NULL); + + rtcb->xcp.kstkptr = (uintptr_t *)regs[REG_SP]; + regs[REG_SP] = (uintptr_t)rtcb->xcp.ustkptr; + } +#endif } break; #endif @@ -440,6 +472,22 @@ int riscv_swint(int irq, void *context, void *arg) regs[REG_INT_CTX] |= MSTATUS_MPPM; /* Machine mode */ rtcb->xcp.sigreturn = 0; + +#ifdef CONFIG_ARCH_KERNEL_STACK + /* We must enter here be using the user stack. We need to switch + * to back to the kernel user stack before returning to the kernel + * mode signal trampoline. + */ + + if (rtcb->xcp.kstack != NULL) + { + DEBUGASSERT(rtcb->xcp.kstkptr != NULL && + (uintptr_t)rtcb->xcp.ustkptr == regs[REG_SP]); + + regs[REG_SP] = (uintptr_t)rtcb->xcp.kstkptr; + rtcb->xcp.kstkptr = NULL; + } +#endif } break; #endif @@ -490,6 +538,19 @@ int riscv_swint(int irq, void *context, void *arg) #else svcerr("ERROR: Bad SYS call: %" PRIdPTR "\n", regs[REG_A0]); #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 = (uintptr_t *)regs[REG_SP]; + regs[REG_SP] = (uintptr_t)rtcb->xcp.kstack + + ARCH_KERNEL_STACKSIZE; + } +#endif } break; } diff --git a/arch/risc-v/src/mpfs/Make.defs b/arch/risc-v/src/mpfs/Make.defs index e46bf5e10ab..58b0ca54086 100755 --- a/arch/risc-v/src/mpfs/Make.defs +++ b/arch/risc-v/src/mpfs/Make.defs @@ -81,6 +81,10 @@ ifeq ($(CONFIG_ARCH_USE_MMU),y) CMN_CSRCS += riscv_mmu.c endif +ifeq ($(CONFIG_ARCH_KERNEL_STACK),y) +CMN_CSRCS += riscv_addrenv_kstack.c +endif + ifeq ($(CONFIG_SPI),y) CHIP_CSRCS += mpfs_spi.c endif