mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-21 23:20:20 +00:00
Kernel/riscv64: Add support for handling traps from userspace
This commit also removes the unnecessary user_sp RegisterState member. We never use the kernel stack pointer on entry, so we can simply always store the stack pointer of the previous privilege mode in sp. Also remove the sp member from mcontext, as RISC-V doesn't have a dedicated stack pointer register. sp is defined to be x2 (x[1] in our case) by the ABI. I probably accidentally included sp while copying the struct from aarch64.
This commit is contained in:
parent
afe9a12412
commit
66f8d0f031
Notes:
sideshowbarker
2024-07-16 22:51:10 +09:00
Author: https://github.com/spholz Commit: https://github.com/SerenityOS/serenity/commit/66f8d0f031 Pull-request: https://github.com/SerenityOS/serenity/pull/23516 Reviewed-by: https://github.com/ADKaster ✅
4 changed files with 118 additions and 61 deletions
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -14,7 +14,6 @@ extern "C" {
|
|||
|
||||
struct __attribute__((packed)) __mcontext {
|
||||
uint64_t x[31];
|
||||
uint64_t sp;
|
||||
uint64_t pc;
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue