diff --git a/Kernel/Arch/riscv64/RegisterState.h b/Kernel/Arch/riscv64/RegisterState.h index 9480051c741..7398c377b91 100644 --- a/Kernel/Arch/riscv64/RegisterState.h +++ b/Kernel/Arch/riscv64/RegisterState.h @@ -18,7 +18,7 @@ VALIDATE_IS_RISCV64() namespace Kernel { -struct RegisterState { +struct alignas(16) RegisterState { u64 x[31]; RISCV64::CSR::SSTATUS sstatus; @@ -26,10 +26,10 @@ struct RegisterState { RISCV64::CSR::SCAUSE scause; u64 stval; - u64 user_sp; - - FlatPtr userspace_sp() const { return user_sp; } - void set_userspace_sp(FlatPtr value) { user_sp = value; } + // x86_64 uses its additional RegisterState member "userspace_rsp" here, which is also invalid if no privilege mode change happened. + // On RISC-V, we only have one sp member, and regardless of the previous privilege mode, we always use this member here. + FlatPtr userspace_sp() const { return x[1]; } + void set_userspace_sp(FlatPtr value) { x[1] = value; } FlatPtr ip() const { return sepc; } void set_ip(FlatPtr value) { sepc = value; } @@ -68,7 +68,6 @@ inline void copy_kernel_registers_into_ptrace_registers(PtraceRegisters& ptrace_ for (auto i = 0; i < 31; i++) ptrace_regs.x[i] = kernel_regs.x[i]; - ptrace_regs.sp = kernel_regs.userspace_sp(); ptrace_regs.pc = kernel_regs.ip(); } @@ -77,7 +76,6 @@ inline void copy_ptrace_registers_into_kernel_registers(RegisterState& kernel_re for (auto i = 0; i < 31; i++) kernel_regs.x[i] = ptrace_regs.x[i]; - kernel_regs.set_userspace_sp(ptrace_regs.sp); kernel_regs.set_ip(ptrace_regs.pc); } diff --git a/Kernel/Arch/riscv64/boot.S b/Kernel/Arch/riscv64/boot.S index 7a9b03bc358..3bae821edc1 100644 --- a/Kernel/Arch/riscv64/boot.S +++ b/Kernel/Arch/riscv64/boot.S @@ -71,4 +71,8 @@ Lclear_bss_done: li t5, 0 li t6, 0 + // The trap handler expects sscratch to be zero if we are in supervisor mode. + // sscratch contains the kernel stack pointer if we are in user mode. + csrw sscratch, zero + tail pre_init diff --git a/Kernel/Arch/riscv64/mcontext.h b/Kernel/Arch/riscv64/mcontext.h index a1c9048fbe8..19ee3c8840a 100644 --- a/Kernel/Arch/riscv64/mcontext.h +++ b/Kernel/Arch/riscv64/mcontext.h @@ -14,7 +14,6 @@ extern "C" { struct __attribute__((packed)) __mcontext { uint64_t x[31]; - uint64_t sp; uint64_t pc; }; diff --git a/Kernel/Arch/riscv64/trap_handler.S b/Kernel/Arch/riscv64/trap_handler.S index 87cc226da48..42d24a31335 100644 --- a/Kernel/Arch/riscv64/trap_handler.S +++ b/Kernel/Arch/riscv64/trap_handler.S @@ -15,23 +15,12 @@ #define SEPC_SLOT (32 * 8) #define SCAUSE_SLOT (33 * 8) #define STVAL_SLOT (34 * 8) -#define USERSPACE_SP_SLOT (35 * 8) .extern trap_handler -.p2align 2 -.global asm_trap_handler -asm_trap_handler: - // FIXME: Handle traps from userspace - - // Save the current register state to the current stack - // and enter the C++ trap handler - - // Allocate stack space for trap frame - addi sp, sp, -REGISTER_STATE_SIZE - +.macro save_gpr_state_except_sp_on_stack sd x1, 0*8(sp) - sd x2, 1*8(sp) + // sp sd x3, 2*8(sp) sd x4, 3*8(sp) sd x5, 4*8(sp) @@ -61,47 +50,9 @@ asm_trap_handler: sd x29, 28*8(sp) sd x30, 29*8(sp) sd x31, 30*8(sp) +.endm - // Let's save some special registers - csrr t0, sstatus - sd t0, SSTATUS_SLOT(sp) - csrr t0, sepc - sd t0, SEPC_SLOT(sp) - - // We also have to save those registers as interrupts are enabled during the page fault handling code. - // A page fault exception may be reported as an interrupt in the register dump, if we wouldn't do that. - csrr t0, scause - sd t0, SCAUSE_SLOT(sp) - csrr t0, stval - sd t0, STVAL_SLOT(sp) - - // TODO - sd zero, USERSPACE_SP_SLOT(sp) - - // Set up TrapFrame struct on the stack - mv t0, sp - addi sp, sp, -16 - sd t0, 1*8(sp) - sd zero, 0*8(sp) - - // Move stack pointer into first argument register - // and jump to the C++ trap handler - mv a0, sp - - call trap_handler - -.global restore_context_and_sret -restore_context_and_sret: - - // Remove TrapFrame from the stack - addi sp, sp, 16 - - // Restore special registers first - ld t0, SSTATUS_SLOT(sp) - csrw sstatus, t0 - ld t0, SEPC_SLOT(sp) - csrw sepc, t0 - +.macro load_gpr_state_except_sp_from_stack ld x1, 0*8(sp) // sp ld x3, 2*8(sp) @@ -133,6 +84,111 @@ restore_context_and_sret: ld x29, 28*8(sp) ld x30, 29*8(sp) ld x31, 30*8(sp) +.endm +.p2align 2 +.global asm_trap_handler +asm_trap_handler: + // We entered here from either the kernel or userland, + // so we have to find out if we came here from userland and if so, switch to the kernel stack. + + // Swap the contents of sscratch and sp. + csrrw sp, sscratch, sp + + // sp now contains the value of sscratch when we entered the trap handler. + // When this value is 0, we were already in supervisor (kernel) mode. + // Otherwise, the value in sp is now the kernel stack and sscratch contains the user stack pointer. + beqz sp, .Ltrap_is_from_kernel + + j .Ltrap_is_from_userland + +.Ltrap_is_from_kernel: + // Store 0 in sscratch and write the value inside sscratch (the kernel stack pointer) to sp. + csrrw sp, sscratch, zero + +.Ltrap_is_from_userland: + // sscratch now contains the user stack pointer, or 0 if the trap was from supervisor mode. + // sp points to the kernel stack. + + // Save the current register state on the kernel stack. + + // Allocate stack space for a RegisterState struct. + addi sp, sp, -REGISTER_STATE_SIZE + + save_gpr_state_except_sp_on_stack + + // Save some CSRs to correctly handle the trap. + csrr t0, sepc + sd t0, SEPC_SLOT(sp) + csrr t0, sstatus + sd t0, SSTATUS_SLOT(sp) + + // Also store these CSRs to be able to display the state of them before trap entry. + // We also might get an interrupt while handling page faults, so scause and stval would be changed by the interrupt. + csrr t0, scause + sd t0, SCAUSE_SLOT(sp) + csrr t0, stval + sd t0, STVAL_SLOT(sp) + + // Read the saved stack pointer from sscratch (which is 0 if the trap is from supervisor mode) + // and set sscratch to 0, as we are currently in the kernel. + csrrw t0, sscratch, zero + + // Save the user or kernel stack pointer in the RegisterState struct. + bnez t0, 1f + mv t0, sp +1: + sd t0, 1*8(sp) + + // Set up a TrapFrame struct on the stack. + mv t0, sp + addi sp, sp, -16 + sd zero, 0*8(sp) + sd t0, 1*8(sp) + + // Move the stack pointer into the first argument register + // and jump to the C++ trap handler. + mv a0, sp + + call trap_handler + +.global restore_context_and_sret +restore_context_and_sret: + + // Remove the TrapFrame from the stack. + addi sp, sp, 16 + + // Restore some CSRs first. + ld t0, SSTATUS_SLOT(sp) + csrw sstatus, t0 + ld t0, SEPC_SLOT(sp) + csrw sepc, t0 + + // Find out to which privilege mode we have to return to. + csrr t0, sstatus + srl t0, t0, 8 // SPP (previous privilege mode) + andi t0, t0, 1 + beqz t0, .Lreturn_to_user + + // Return to supervisor mode. + + csrw sscratch, zero + + load_gpr_state_except_sp_from_stack + + // Remove the RegisterState struct from the kernel stack. addi sp, sp, REGISTER_STATE_SIZE + + sret + +.Lreturn_to_user: + // Store sp with the RegisterState struct removed to sscratch. + addi t0, sp, REGISTER_STATE_SIZE + csrw sscratch, t0 + + load_gpr_state_except_sp_from_stack + + // Load the user stack pointer from the RegisterState struct on the kernel stack. + ld sp, 1*8(sp) + sret