diff options
Diffstat (limited to 'arch/riscv/trap/trap_vector.S')
| -rw-r--r-- | arch/riscv/trap/trap_vector.S | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/arch/riscv/trap/trap_vector.S b/arch/riscv/trap/trap_vector.S new file mode 100644 index 0000000..8e45cae --- /dev/null +++ b/arch/riscv/trap/trap_vector.S @@ -0,0 +1,283 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2026 Richard Qin */ + +/* + * Trap Vector Entry / Exit (RISC-V 64, S-mode) + * + * On trap entry (interrupt / exception / ecall): + * sscratch holds the TrapFrame pointer (set up by kernel before sret). + * We swap sscratch <-> tp, save all 31 GPRs + sstatus + sepc into TrapFrame, + * then load kernel metadata (sp, satp, tp, trap handler) and jump to C++. + * + * On trap return (userret): + * Restore all GPRs + sstatus + sepc from TrapFrame, swap sscratch <-> tp, + * then sret back to user mode. + * + * Layout must match arch::TrapFrame in arch/riscv/include/arch/trapframe.h + * Reference: docs/specs/arch_abstraction.md §7.2 + */ + +#include <arch/trapframe.h> + + .section .text + .globl trap_vector + .globl userret + .align 4 + +/* + * trap_vector — unified S-mode trap entry point (Direct mode) + * + * Precondition: sscratch = pointer to current task's TrapFrame + */ +trap_vector: + /* Step 1: swap sscratch <-> tp. Now tp = TrapFrame*, sscratch = old tp */ + csrrw tp, sscratch, tp + + /* Step 2: save all 31 general-purpose registers into TrapFrame */ + sd ra, TF_RA(tp) + sd sp, TF_SP(tp) + sd gp, TF_GP(tp) + /* tp (x4) is in sscratch — save it later */ + sd t0, TF_T0(tp) + sd t1, TF_T1(tp) + sd t2, TF_T2(tp) + sd s0, TF_S0(tp) + sd s1, TF_S1(tp) + sd a0, TF_A0(tp) + sd a1, TF_A1(tp) + sd a2, TF_A2(tp) + sd a3, TF_A3(tp) + sd a4, TF_A4(tp) + sd a5, TF_A5(tp) + sd a6, TF_A6(tp) + sd a7, TF_A7(tp) + sd s2, TF_S2(tp) + sd s3, TF_S3(tp) + sd s4, TF_S4(tp) + sd s5, TF_S5(tp) + sd s6, TF_S6(tp) + sd s7, TF_S7(tp) + sd s8, TF_S8(tp) + sd s9, TF_S9(tp) + sd s10, TF_S10(tp) + sd s11, TF_S11(tp) + sd t3, TF_T3(tp) + sd t4, TF_T4(tp) + sd t5, TF_T5(tp) + sd t6, TF_T6(tp) + + /* Save the real tp (user's tp) which is now in sscratch */ + csrr t0, sscratch + sd t0, TF_TP(tp) + + /* Step 3: save sstatus */ + csrr t0, sstatus + sd t0, TF_SSTATUS(tp) + + /* Step 4: save sepc (interrupted PC) */ + csrr t0, sepc + sd t0, TF_EPC(tp) + + /* Step 5: load kernel context from TrapFrame metadata */ + ld sp, TF_KERNEL_SP(tp) /* switch to kernel stack */ + ld t1, TF_KERNEL_SATP(tp) /* kernel page table */ + ld t2, TF_KERNEL_CPUID(tp) /* CPU ID for tp */ + ld t3, TF_KERNEL_TRAP(tp) /* C++ trap_handler address */ + + /* Switch to kernel page table */ + csrw satp, t1 + + /* Step 6: flush TLB after page table switch */ + sfence.vma zero, zero + + /* Step 6.5: restore kernel gp (global pointer). + * The compiler uses gp-relative addressing for global variable access + * (e.g. ld reg, offset(gp)). User mode may have set gp to anything, + * so we MUST reload it before entering any C++ code. */ +.option push +.option norelax + la gp, __global_pointer$ +.option pop + + /* Pass TrapFrame* to C++ handler (a0 = tf) */ + mv a0, tp + + /* Set tp = CPU ID (kernel convention) */ + mv tp, t2 + + /* Step 7: jump to C++ trap_handler */ + jalr t3 + + +/* ======================================================================== + * kernelvec — unified S-mode trap entry point for traps FROM S-MODE + * + * Traps from S-mode already have a valid kernel stack pointer in sp. + * We just allocate a TrapFrame on the stack and save registers. + * ======================================================================== */ + .globl kernelvec + .align 4 +kernelvec: + /* Allocate TrapFrame on stack */ + addi sp, sp, -296 + + /* Save all 31 general-purpose registers into TrapFrame */ + sd ra, TF_RA(sp) + /* Save the OLD sp (before we subtracted 296) */ + addi t0, sp, 296 + sd t0, TF_SP(sp) + sd gp, TF_GP(sp) + sd tp, TF_TP(sp) + sd t0, TF_T0(sp) + sd t1, TF_T1(sp) + sd t2, TF_T2(sp) + sd s0, TF_S0(sp) + sd s1, TF_S1(sp) + sd a0, TF_A0(sp) + sd a1, TF_A1(sp) + sd a2, TF_A2(sp) + sd a3, TF_A3(sp) + sd a4, TF_A4(sp) + sd a5, TF_A5(sp) + sd a6, TF_A6(sp) + sd a7, TF_A7(sp) + sd s2, TF_S2(sp) + sd s3, TF_S3(sp) + sd s4, TF_S4(sp) + sd s5, TF_S5(sp) + sd s6, TF_S6(sp) + sd s7, TF_S7(sp) + sd s8, TF_S8(sp) + sd s9, TF_S9(sp) + sd s10, TF_S10(sp) + sd s11, TF_S11(sp) + sd t3, TF_T3(sp) + sd t4, TF_T4(sp) + sd t5, TF_T5(sp) + sd t6, TF_T6(sp) + + /* Save sstatus and sepc */ + csrr t0, sstatus + sd t0, TF_SSTATUS(sp) + csrr t0, sepc + sd t0, TF_EPC(sp) + + /* Call C++ kernel_trap_handler(TrapFrame* tf) */ + mv a0, sp + call kernel_trap_handler + + /* Restore sstatus and sepc */ + ld t0, TF_SSTATUS(sp) + csrw sstatus, t0 + ld t0, TF_EPC(sp) + csrw sepc, t0 + + /* Restore all 31 general-purpose registers */ + ld ra, TF_RA(sp) + ld gp, TF_GP(sp) + ld tp, TF_TP(sp) + ld t0, TF_T0(sp) + ld t1, TF_T1(sp) + ld t2, TF_T2(sp) + ld s0, TF_S0(sp) + ld s1, TF_S1(sp) + ld a0, TF_A0(sp) + ld a1, TF_A1(sp) + ld a2, TF_A2(sp) + ld a3, TF_A3(sp) + ld a4, TF_A4(sp) + ld a5, TF_A5(sp) + ld a6, TF_A6(sp) + ld a7, TF_A7(sp) + ld s2, TF_S2(sp) + ld s3, TF_S3(sp) + ld s4, TF_S4(sp) + ld s5, TF_S5(sp) + ld s6, TF_S6(sp) + ld s7, TF_S7(sp) + ld s8, TF_S8(sp) + ld s9, TF_S9(sp) + ld s10, TF_S10(sp) + ld s11, TF_S11(sp) + ld t3, TF_T3(sp) + ld t4, TF_T4(sp) + ld t5, TF_T5(sp) + ld t6, TF_T6(sp) + + /* Restore sp last */ + ld sp, TF_SP(sp) + + sret + +/* + * userret — return to user mode + * + * Called from C++ as: userret(TrapFrame* tf) + * a0 = pointer to TrapFrame (with user satp already written by caller) + * + * Precondition: interrupts must be disabled by caller. + */ +userret: + /* a0 = TrapFrame pointer, use it as base */ + + /* Step 1: write back epc -> sepc */ + ld t0, TF_EPC(a0) + csrw sepc, t0 + + /* Step 2: write back sstatus */ + ld t0, TF_SSTATUS(a0) + csrw sstatus, t0 + + /* Step 3: caller is responsible for switching satp to user page table + * and flushing TLB before calling userret. */ + + /* Store TrapFrame address into sscratch for next trap entry */ + csrw sscratch, a0 + + /* Use a0 as base to restore all registers */ + mv tp, a0 + + /* Step 4: restore all 31 general-purpose registers */ + ld ra, TF_RA(tp) + ld sp, TF_SP(tp) + ld gp, TF_GP(tp) + /* defer tp restore — we're using it as base */ + ld t0, TF_T0(tp) + ld t1, TF_T1(tp) + ld t2, TF_T2(tp) + ld s0, TF_S0(tp) + ld s1, TF_S1(tp) + ld a0, TF_A0(tp) + ld a1, TF_A1(tp) + ld a2, TF_A2(tp) + ld a3, TF_A3(tp) + ld a4, TF_A4(tp) + ld a5, TF_A5(tp) + ld a6, TF_A6(tp) + ld a7, TF_A7(tp) + ld s2, TF_S2(tp) + ld s3, TF_S3(tp) + ld s4, TF_S4(tp) + ld s5, TF_S5(tp) + ld s6, TF_S6(tp) + ld s7, TF_S7(tp) + ld s8, TF_S8(tp) + ld s9, TF_S9(tp) + ld s10, TF_S10(tp) + ld s11, TF_S11(tp) + ld t3, TF_T3(tp) + ld t4, TF_T4(tp) + ld t5, TF_T5(tp) + ld t6, TF_T6(tp) + + /* Step 5: restore user tp (swap sscratch <-> tp) */ + /* sscratch currently = TrapFrame address, tp = TrapFrame address */ + /* We need: tp = user's tp, sscratch = TrapFrame address */ + /* Load user tp from TrapFrame first, but we can't use any temp reg now. + * Use the sscratch swap trick: */ + ld tp, TF_TP(tp) + + /* sscratch already holds TrapFrame address (set above with csrw) */ + + /* Step 6: sret — return to user mode */ + sret |
