Files
ladybird/Kernel/Arch/riscv64/trap_handler.S
Sönke Holz 66f8d0f031 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.
2024-03-25 14:14:43 -06:00

195 lines
4.8 KiB
ArmAsm

/*
* Copyright (c) 2024, Sönke Holz <sholz830@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
.section .text.asm_trap_handler
#define REGISTER_STATE_SIZE (36 * 8)
#if REGISTER_STATE_SIZE % 16 != 0
# error "REGISTER_STATE_SIZE is not a multiple of 16 bytes!"
#endif
#define SSTATUS_SLOT (31 * 8)
#define SEPC_SLOT (32 * 8)
#define SCAUSE_SLOT (33 * 8)
#define STVAL_SLOT (34 * 8)
.extern trap_handler
.macro save_gpr_state_except_sp_on_stack
sd x1, 0*8(sp)
// sp
sd x3, 2*8(sp)
sd x4, 3*8(sp)
sd x5, 4*8(sp)
sd x6, 5*8(sp)
sd x7, 6*8(sp)
sd x8, 7*8(sp)
sd x9, 8*8(sp)
sd x10, 9*8(sp)
sd x11, 10*8(sp)
sd x12, 11*8(sp)
sd x13, 12*8(sp)
sd x14, 13*8(sp)
sd x15, 14*8(sp)
sd x16, 15*8(sp)
sd x17, 16*8(sp)
sd x18, 17*8(sp)
sd x19, 18*8(sp)
sd x20, 19*8(sp)
sd x21, 20*8(sp)
sd x22, 21*8(sp)
sd x23, 22*8(sp)
sd x24, 23*8(sp)
sd x25, 24*8(sp)
sd x26, 25*8(sp)
sd x27, 26*8(sp)
sd x28, 27*8(sp)
sd x29, 28*8(sp)
sd x30, 29*8(sp)
sd x31, 30*8(sp)
.endm
.macro load_gpr_state_except_sp_from_stack
ld x1, 0*8(sp)
// sp
ld x3, 2*8(sp)
ld x4, 3*8(sp)
ld x5, 4*8(sp)
ld x6, 5*8(sp)
ld x7, 6*8(sp)
ld x8, 7*8(sp)
ld x9, 8*8(sp)
ld x10, 9*8(sp)
ld x11, 10*8(sp)
ld x12, 11*8(sp)
ld x13, 12*8(sp)
ld x14, 13*8(sp)
ld x15, 14*8(sp)
ld x16, 15*8(sp)
ld x17, 16*8(sp)
ld x18, 17*8(sp)
ld x19, 18*8(sp)
ld x20, 19*8(sp)
ld x21, 20*8(sp)
ld x22, 21*8(sp)
ld x23, 22*8(sp)
ld x24, 23*8(sp)
ld x25, 24*8(sp)
ld x26, 25*8(sp)
ld x27, 26*8(sp)
ld x28, 27*8(sp)
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