xiaoxiang781216 commented on pull request #5782: URL: https://github.com/apache/incubator-nuttx/pull/5782#issuecomment-1076644915
> > > > S-mode can hook ECALL to self, @pussuw could you explain more why we need handle differently in S-mode and M-mode? > > > > > > > > > S-mode can trap ecall from user mode (U-mode) but not from S-mode. So ecall from S-mode to S-mode is not possible. > > > > > > Ok, I get the key point. So is syscall from S-mode to S-mode only used for kernel thread context switch? > > A context switch can occur in two ways: > > 1. A process running thread in privileged mode (either kernel process or user process who's privileges are temporary raised e.g. when executing a system call from usersace), in this case a context switch occurs via `riscv_switchcontext`. In flat mode (M-mode) this is handled via: > > ``` > #define riscv_switchcontext(saveregs, restoreregs) \ > sys_call2(SYS_switch_context, (uintptr_t)saveregs, (uintptr_t)restoreregs) > ``` > > Which uses ecall to enter the trap handler which does all the work. This works, because ecall from M-mode to M-mode enters M-mode. > > This cannot be used to switch context in S-mode, because it would raise privileges to M-mode (ecall from S-mode) which is not what we want. How about we trigger software interrupt(SSIP) instead ECALL? > Instead, the assembly routine below is used to do the same work as was done by the trap handler previously! > > ``` > riscv_switchcontext: > > /* Save old context to arg[0] */ > > save_ctx a0 /* save context */ > > REGSTORE x1, REG_EPC(a0) /* save ra to epc */ > REGSTORE sp, REG_SP(a0) /* original SP */ > > /* Set previous privilege, we are in privileged mode now */ > > csrr s0, CSR_STATUS /* read status register */ > li s1, STATUS_PPP /* set previous privilege */ > or s0, s0, s1 > li s1, ~STATUS_PIE /* clear previous interrupt enable */ > and s0, s0, s1 > REGSTORE s0, REG_INT_CTX(a0) /* store status to context */ > > #ifdef CONFIG_ARCH_FPU > jal x1, riscv_savefpu /* FP registers */ > #endif > > /* Load new context from arg[1] */ > > mv a0, a1 /* load from a1 */ > j riscv_fullcontextrestore /* restore context */ > ``` > Can we fake CPU by modifying CSR_STATUS to match the ECALL condition and ghen jump to exception_common directly? > 2. The second way a context switch can occur is in the trap handler itself, for example if a task is waiting for uart semaphore and the semaphore is posted by the uart interrupt handler, this can dispatch a wake up signal to the task, and the task that was running prior to taking the trap gets preempted and the task that was waiting for the semaphore gets activated instead. > It's different from the active context switch, the passive context switch(e.g. the block thread wake up by an interrupt handler) since the interrupt handle already run in S-mode: 1. The exception entry pointer already save the current thread context for us 2. So we can restore the unblock thread context directly without trap to M-mode > > > Also, we want to use ecall in S-mode to raise privileges to M-mode, to access some machine mode services (mhartid, > > > > > > Yes, but it's still the same ecall instruction. Here is my understanding: > > ``` > > 1. Any syscall(include context switch) issued by U-mode could be implemented by ECALL and raise to S-mode > > ``` > > A user task will never call syscall(SYS_context_switch), or any of the reserved SYS_ call numbers, **from user mode**. If a user task calls for a context switch, it means its privileges are raised to kernel temporarily. My understanding is different from yours: user task need issue syscall here otherwise how the thread can raise the privilege to S-mode? > The reserved syscalls are reserved for the kernel only and thus will only be executed by privileged mode code, i.e. only the kernel does this. No, all syscalls(either reserved or normal) are for user space(U-mode) to initiate the designed functionality to kernel space. > > A user task can however call e.g. sys_call(SYS__exit, ...), which is executed via a proxy function like this: > > ``` > void _exit(int parm1) > { > (void)sys_call1((unsigned int)SYS__exit, (uintptr_t)parm1); > while(1); > } > ``` > > This sys_call will come from U-mode and it is intended to raise the privilege level to privileged mode. > > Vica versa, a task running in kernel mode (be it a user task or a kernel task, what I mean the current privilege level = kernel) will **never** execute any of the old sys_call0-6 functions. They are not needed! The only reason the prototypes / definitions exist, is because the proxy functions mentioned above need their definitions, due to how the build system creates PROXY_ and STUB_ function into the /syscall/proxies and /syscall/stubs folders in non-flat builds. Yes, I agree the behavior your describe above. > We could just as well move the sys_call0-6 prototypes and definitions to the libc folder, under machine/risc-v IMO. > The suggestion look reasonable, sys_call0-6 is directly called by user space code, libc/machine is a better place. But, we can do the change for all arch in a new patch. > In flat builds this will obviously fail horribly, and this is one of the reasons why kernel in S-mode is not really feasible for the flat build (among other things, like PMP needs to be configured to give access to memory and so forth). > Yes, I agree too. It's better to run flat mode in M-mode. > > ``` > > 2. Kernel thread context switch could be implemented by ECALL too, but it will be handled in M-mode instead S-mode > > ``` > > I don't know how to do this, M-mode does not support address translations, so when using address environments (the kernel memory is MMU mapped) it will become quite difficult. Sorry, I forget this. Yes, it's very hard to access the memory pointed by S-mode. It's clear that it isn't a good idea to handle S-mode context switch in M-mode. > > > ``` > > 3. Some special function(e.g. mhartid) which need the highest privilege has to issue ECALL from S-mode to M-mode > > ``` > > Yes, exactly > > > So, I think we can still use the same sys_call0-syscall6 as before, and: > > ``` > > 1. Handle U-mode syscall in S-mode as before > > ``` > > Yes, this is why we need sys_call0-6, for the userspace > > > ``` > > 2. Handle S-mode context switch in M-mode( the code should be same as S-mode) > > ``` > > I don't know how to do this > > > ``` > > 3. Extend the reserved syscall id to cover the special need(e.g. mhardid) > > ``` > > Can be done, but I'd like to keep this separate. What riscv_mcall.c basically implements is a tiny SBI (comparable to openSBI for example but much smaller). > > > > mtimer to name a few, see riscv_mcall.c). > > > > > > Why we can't use S-mode timer instead? > > There is no S-mode timer as far as I know. There is a scounter but this is for performance monitoring / profiling as far as I know. What we need is a ticker with a compare match event, i.e. mtime and mtimecmp. Such memory mapped registers are not available for S-mode. Ok, it just has the S-mode timer interrupt pending bit(STIP), so we have: 1. Let M-mode simulate the software timer and access the functionality through ECALL and trigger the interrupt by STIP 2. Use the external hardware time instead To support cpuid and timer, S-mode has to talk with M-mode. How do you reduce the coupling since we can't assume M-mode is also running NuttX? Do you plan reuse OpenSBI interface? -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: commits-unsubscr...@nuttx.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org