diff options
Diffstat (limited to 'na/arch/riscv/trap')
| -rw-r--r-- | na/arch/riscv/trap/extable.cc | 92 | ||||
| -rw-r--r-- | na/arch/riscv/trap/fpu.S | 103 | ||||
| -rw-r--r-- | na/arch/riscv/trap/swtch.S | 96 | ||||
| -rw-r--r-- | na/arch/riscv/trap/trap_md.cc | 241 | ||||
| -rw-r--r-- | na/arch/riscv/trap/trap_vector.S | 283 |
5 files changed, 0 insertions, 815 deletions
diff --git a/na/arch/riscv/trap/extable.cc b/na/arch/riscv/trap/extable.cc deleted file mode 100644 index 57a3dc1..0000000 --- a/na/arch/riscv/trap/extable.cc +++ /dev/null @@ -1,92 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* Copyright (C) 2026 Richard Qin */ - -#include <arch/extable.h> -#include <lume/types.h> - -extern struct ExceptionTableEntry __extable_start[]; -extern struct ExceptionTableEntry __extable_end[]; - -static void swap_extable_entries(struct ExceptionTableEntry *a, struct ExceptionTableEntry *b) { - uint64 a_insn = reinterpret_cast<uint64>(&a->insn) + a->insn; - uint64 a_fixup = reinterpret_cast<uint64>(&a->fixup) + a->fixup; - uint64 b_insn = reinterpret_cast<uint64>(&b->insn) + b->insn; - uint64 b_fixup = reinterpret_cast<uint64>(&b->fixup) + b->fixup; - - a->insn = static_cast<int32>(b_insn - reinterpret_cast<uint64>(&a->insn)); - a->fixup = static_cast<int32>(b_fixup - reinterpret_cast<uint64>(&a->fixup)); - b->insn = static_cast<int32>(a_insn - reinterpret_cast<uint64>(&b->insn)); - b->fixup = static_cast<int32>(a_fixup - reinterpret_cast<uint64>(&b->fixup)); -} - -static void sort_extable(size_t left, size_t right) { - if (left >= right) return; - - size_t mid_idx = left + (right - left) / 2; - struct ExceptionTableEntry *pivot = &__extable_start[mid_idx]; - uint64 pivot_val = reinterpret_cast<uint64>(&pivot->insn) + pivot->insn; - - size_t i = left; - size_t j = right; - - while (i <= j) { - while ((reinterpret_cast<uint64>(&__extable_start[i].insn) + __extable_start[i].insn) < pivot_val) { - i++; - } - while ((reinterpret_cast<uint64>(&__extable_start[j].insn) + __extable_start[j].insn) > pivot_val) { - if (j == 0) { - break; - } - j--; - } - if (i <= j) { - swap_extable_entries(&__extable_start[i], &__extable_start[j]); - i++; - if (j == 0) { - break; - } - j--; - } - } - - if (j > left) { - sort_extable(left, j); - } - if (i < right) { - sort_extable(i, right); - } -} - -void extable_init() { - size_t count = __extable_end - __extable_start; - if (count <= 1) return; - sort_extable(0, count - 1); -} - -uint64 search_extable(uint64 pc) { - size_t count = __extable_end - __extable_start; - if (count == 0) return 0; - - size_t left = 0; - size_t right = count - 1; - - /* Binary search */ - while (left <= right) { - size_t mid = left + (right - left) / 2; - const struct ExceptionTableEntry *entry = &__extable_start[mid]; - uint64 mid_pc = reinterpret_cast<uint64>(&entry->insn) + entry->insn; - - if (mid_pc == pc) { - return reinterpret_cast<uint64>(&entry->fixup) + entry->fixup; - } - - if (mid_pc < pc) { - left = mid + 1; - } else { - if (mid == 0) break; - right = mid - 1; - } - } - - return 0; -} diff --git a/na/arch/riscv/trap/fpu.S b/na/arch/riscv/trap/fpu.S deleted file mode 100644 index 90d96d9..0000000 --- a/na/arch/riscv/trap/fpu.S +++ /dev/null @@ -1,103 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* Copyright (C) 2026 Richard Qin */ - -/* - * FPU Save / Restore (RISC-V D extension) - * - * void fpu_save(FpState* state); - * Saves f0-f31 and fcsr to the FpState structure. - * - * void fpu_restore(FpState* state); - * Restores f0-f31 and fcsr from the FpState structure. - * - * FpState layout: f[0..31] at offset 0 (each 8 bytes), fcsr at offset 256. - * - * Called from schedule() when switching between tasks that have - * used the FPU (sstatus.FS == Dirty on save path). - */ - - .section .text - -/* ====================================================================== - * void fpu_save(FpState* state); // a0 = state pointer - * ====================================================================== */ - .globl fpu_save - .align 2 -fpu_save: - fsd f0, 0(a0) - fsd f1, 8(a0) - fsd f2, 16(a0) - fsd f3, 24(a0) - fsd f4, 32(a0) - fsd f5, 40(a0) - fsd f6, 48(a0) - fsd f7, 56(a0) - fsd f8, 64(a0) - fsd f9, 72(a0) - fsd f10, 80(a0) - fsd f11, 88(a0) - fsd f12, 96(a0) - fsd f13, 104(a0) - fsd f14, 112(a0) - fsd f15, 120(a0) - fsd f16, 128(a0) - fsd f17, 136(a0) - fsd f18, 144(a0) - fsd f19, 152(a0) - fsd f20, 160(a0) - fsd f21, 168(a0) - fsd f22, 176(a0) - fsd f23, 184(a0) - fsd f24, 192(a0) - fsd f25, 200(a0) - fsd f26, 208(a0) - fsd f27, 216(a0) - fsd f28, 224(a0) - fsd f29, 232(a0) - fsd f30, 240(a0) - fsd f31, 248(a0) - frcsr t0 - sw t0, 256(a0) - ret - -/* ====================================================================== - * void fpu_restore(FpState* state); // a0 = state pointer - * ====================================================================== */ - .globl fpu_restore - .align 2 -fpu_restore: - fld f0, 0(a0) - fld f1, 8(a0) - fld f2, 16(a0) - fld f3, 24(a0) - fld f4, 32(a0) - fld f5, 40(a0) - fld f6, 48(a0) - fld f7, 56(a0) - fld f8, 64(a0) - fld f9, 72(a0) - fld f10, 80(a0) - fld f11, 88(a0) - fld f12, 96(a0) - fld f13, 104(a0) - fld f14, 112(a0) - fld f15, 120(a0) - fld f16, 128(a0) - fld f17, 136(a0) - fld f18, 144(a0) - fld f19, 152(a0) - fld f20, 160(a0) - fld f21, 168(a0) - fld f22, 176(a0) - fld f23, 184(a0) - fld f24, 192(a0) - fld f25, 200(a0) - fld f26, 208(a0) - fld f27, 216(a0) - fld f28, 224(a0) - fld f29, 232(a0) - fld f30, 240(a0) - fld f31, 248(a0) - lw t0, 256(a0) - fscsr t0 - ret diff --git a/na/arch/riscv/trap/swtch.S b/na/arch/riscv/trap/swtch.S deleted file mode 100644 index dbae43d..0000000 --- a/na/arch/riscv/trap/swtch.S +++ /dev/null @@ -1,96 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* Copyright (C) 2026 Richard Qin */ - -/* - * Context Switch & Kernel Thread Trampoline (RISC-V 64) - * - * context_switch: saves/restores callee-saved registers. - * kthread_entry_trampoline: entry point for newly created kernel threads. - * After context_switch loads the new Context, execution lands here via - * Context.ra = kthread_entry_trampoline. The entry function and argument - * are in s0 and s1 respectively, set by sched_init/create_tcb. - * - * The trampoline calls the MI-layer kthread_entry(entry, arg). - * - * Reference: docs/specs/scheduler.md §6 - */ - - .section .text - -/* ======================================================================== - * void context_switch(Context* old_ctx, Context* next_ctx); - * a0 = old_ctx_ptr, a1 = next_ctx_ptr - * ======================================================================== */ - .globl context_switch - .align 2 -context_switch: - sd ra, 0(a0) - sd sp, 8(a0) - sd s0, 16(a0) - sd s1, 24(a0) - sd s2, 32(a0) - sd s3, 40(a0) - sd s4, 48(a0) - sd s5, 56(a0) - sd s6, 64(a0) - sd s7, 72(a0) - sd s8, 80(a0) - sd s9, 88(a0) - sd s10, 96(a0) - sd s11, 104(a0) - - ld ra, 0(a1) - ld sp, 8(a1) - ld s0, 16(a1) - ld s1, 24(a1) - ld s2, 32(a1) - ld s3, 40(a1) - ld s4, 48(a1) - ld s5, 56(a1) - ld s6, 64(a1) - ld s7, 72(a1) - ld s8, 80(a1) - ld s9, 88(a1) - ld s10, 96(a1) - ld s11, 104(a1) - - ret - -/* ======================================================================== - * kthread_entry_trampoline - * - * Reached via context_switch when a new kthread is first scheduled. - * Context was set up by create_tcb(): - * s0 = entry function pointer - * s1 = argument pointer - * - * Calls the MI-layer C++ function: - * void kthread_entry(void (*entry)(void*), void* arg); - * ======================================================================== */ - .globl kthread_entry_trampoline - .align 2 -kthread_entry_trampoline: - mv a0, s0 /* a0 = entry function */ - mv a1, s1 /* a1 = argument */ - call kthread_entry /* MI-layer C++ function */ - - /* kthread_entry should never return, but if it does: */ - wfi - j kthread_entry_trampoline + 12 /* loop on wfi */ - -/* ======================================================================== - * ret_from_fork - * - * Trampoline for newly forked processes. - * Context is set up by fork() so that s0 contains the TrapFrame pointer. - * - * Calls the MI-layer C++ function: - * void fork_child_entry(TrapFrame* tf); - * ======================================================================== */ - .globl ret_from_fork - .align 2 -ret_from_fork: - mv a0, s0 /* a0 = TrapFrame pointer */ - call fork_child_entry /* does not return, eventually calls userret */ - wfi - j ret_from_fork + 8
\ No newline at end of file diff --git a/na/arch/riscv/trap/trap_md.cc b/na/arch/riscv/trap/trap_md.cc deleted file mode 100644 index e30aea6..0000000 --- a/na/arch/riscv/trap/trap_md.cc +++ /dev/null @@ -1,241 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* Copyright (C) 2026 Richard Qin */ - -/* - * Trap MD Layer (RISC-V 64) - * - * All architecture-specific trap logic lives here: - * - scause/stval reading (CSR access) - * - scause → MI TrapCause translation - * - RISC-V specific fixups (ecall epc += 4, ebreak epc += 2) - * - stvec setup (trap_init / trap_init_ap) - * - kernel_trap_handler (traps from S-mode) - * - * MI layer (kernel/trap.cc) contains ZERO arch-specific code. - * - * Reference: docs/specs/trap.md §7 - */ - -#include <lume/trap.h> -#include <lume/irq.h> -#include <lume/klog.h> -#include <lume/console.h> -#include <lume/kprintf.h> -#include <lume/sched.h> -#include <lume/task.h> -#include <arch/trap.h> -#include <arch/trapframe.h> -#include <arch/cpu.h> -#include <arch/extable.h> - -/* PLIC interface (drivers/plic.cc) */ -uint32 plic_claim(); -void plic_complete(uint32 irq); - -/* trap_vector.S: assembly entry point */ -extern "C" void trap_vector(); - -/* ======================================================================== - * scause → TrapCause translation (RISC-V specific) - * ======================================================================== */ - -static TrapCause scause_to_trap_cause(uint64 scause) -{ - if (arch::trap::is_interrupt(scause)) { - switch (arch::trap::cause_code(scause)) { - case IRQ_S_TIMER: return TrapCause::IrqTimer; - case IRQ_S_SOFTWARE: return TrapCause::IrqSoftware; - case IRQ_S_EXTERNAL: return TrapCause::IrqExternal; - default: return TrapCause::Unknown; - } - } - - /* Exception (synchronous) */ - switch (scause) { - case EXC_INST_PAGE_FAULT: return TrapCause::PageFaultExec; - case EXC_LOAD_PAGE_FAULT: return TrapCause::PageFaultLoad; - case EXC_STORE_PAGE_FAULT: return TrapCause::PageFaultStore; - case EXC_ECALL_FROM_U: return TrapCause::Syscall; - case EXC_ILLEGAL_INST: return TrapCause::IllegalInst; - case EXC_BREAKPOINT: return TrapCause::Breakpoint; - - case EXC_INST_ACCESS_FAULT: - case EXC_LOAD_ACCESS_FAULT: - case EXC_STORE_ACCESS_FAULT: - return TrapCause::AccessFault; - - case EXC_INST_MISALIGNED: - case EXC_LOAD_MISALIGNED: - case EXC_STORE_MISALIGNED: - return TrapCause::AlignFault; - - default: - return TrapCause::Unknown; - } -} - -TrapCause trap_translate_scause(uint64 scause) -{ - return scause_to_trap_cause(scause); -} - -static TrapOrigin origin_from_sstatus(uint64 sstatus) -{ - constexpr uint64 kSstatusSPP = (1ULL << 8); - return (sstatus & kSstatusSPP) ? TrapOrigin::FromKernel - : TrapOrigin::FromUser; -} - -static TrapClass classify_trap(uint64 scause, TrapCause cause) -{ - if (arch::trap::is_interrupt(scause)) { - return TrapClass::Interrupt; - } - if (cause == TrapCause::Syscall) { - return TrapClass::Syscall; - } - if (cause == TrapCause::Unknown) { - return TrapClass::Unknown; - } - return TrapClass::Exception; -} - -static TrapInfo build_trap_info(arch::TrapFrame* tf, - uint64 scause, - uint64 stval, - TrapOrigin origin) -{ - TrapInfo info{}; - info.cause = scause_to_trap_cause(scause); - info.cls = classify_trap(scause, info.cause); - info.origin = origin; - info.val = stval; - info.epc = tf ? tf->epc : 0; - info.scause = scause; - return info; -} - -/* ======================================================================== - * trap_dispatch — user-mode trap entry (called from trap_vector.S) - * - * Reads RISC-V CSRs, translates to MI TrapCause, applies arch-specific - * fixups, then calls MI trap_handle(). - * - * After trap_handle returns, we must return to user mode via userret(). - * The assembly jalr t3 that called us has NO return-to-user code after it, - * so trap_dispatch must never return normally. - * ======================================================================== */ - -extern "C" void userret(arch::TrapFrame* tf); - -extern "C" [[noreturn]] void trap_dispatch(arch::TrapFrame* tf) -{ - /* Switch stvec to kernelvec immediately so that any traps during - * kernel execution (timer IRQs, page faults from copy_from_user) - * go through the kernel trap entry, not the user trap entry. - * This matches Linux which sets stvec to handle_exception (kernel) - * at the top of the user trap path. */ - extern void kernelvec(); - arch::trap::set_vector(reinterpret_cast<uint64>(&kernelvec)); - - /* Enable SUM so S-mode can access user pages (PTE_U). - * Without this, any dereference of user-space pointers in syscall - * handlers (sys_write buf, sys_read buf, etc.) would page fault. */ - arch::trap::enable_sum(); - - uint64 scause = arch::trap::get_cause(); - uint64 stval = arch::trap::get_value(); - - TrapOrigin origin = origin_from_sstatus(tf->sstatus); - TrapInfo info = build_trap_info(tf, scause, stval, origin); - - /* Per-trap trace from user mode. Stored at KLOG_DEBUG / KLOG_SUBSYS_TRAP - * so it lives in the ring buffer for postmortem but stays off the console - * by default. No more in-band 20-trap window — flip console_level or - * dump the ring on demand instead. */ - if (origin == TrapOrigin::FromUser) { - pr_debug_s(KLOG_SUBSYS_TRAP, - "trap: cause=%u epc=%llx val=%llx\n", - static_cast<unsigned>(info.cause), info.epc, info.val); - } - - /* Save trapframe in TCB for syscall handlers (like fork) */ - TaskControlBlock* curr = current_task(); - if (curr) { - curr->trapframe = tf; - } - - /* RISC-V ecall fix: ecall does not increment sepc. - * Advance epc so sret returns to the instruction AFTER ecall. */ - if (info.cause == TrapCause::Syscall) { - tf->epc += 4; - info.epc = tf->epc; - } - - /* RISC-V ebreak fix: advance past the C.EBREAK instruction (2 bytes). - * This is arch-specific because instruction size depends on the ISA. */ - if (info.cause == TrapCause::Breakpoint) { - tf->epc += 2; - info.epc = tf->epc; - } - - /* Call MI-layer dispatcher (zero inline assembly from here on) */ - trap_handle(tf, info); - - /* Return to user mode. - * Set stvec back to trap_vector for the next user-mode trap, - * then jump to userret which restores registers and does sret. */ - arch::trap::set_vector(reinterpret_cast<uint64>(&trap_vector)); - userret(tf); - __builtin_unreachable(); -} - -/* ======================================================================== - * kernel_trap_handler — traps taken while already in S-mode - * - * For Phase 2, only timer and external interrupts are expected. - * Exceptions in S-mode are always fatal. - * - * This belongs in MD because it reads scause directly. - * ======================================================================== */ - -extern "C" void kernel_trap_handler(arch::TrapFrame* tf) -{ - uint64 scause = arch::trap::get_cause(); - uint64 stval = arch::trap::get_value(); - - if (scause == EXC_LOAD_PAGE_FAULT || scause == EXC_STORE_PAGE_FAULT) { - uint64 fixup = search_extable(tf->epc); - if (fixup != 0) { - /* Exception table entry found: redirect execution to fixup code */ - tf->epc = fixup; - return; - } - } - - TrapInfo info = build_trap_info(tf, scause, stval, TrapOrigin::FromKernel); - trap_handle(tf, info); -} - -/* kernelvec assembly is in trap_vector.S */ -extern "C" void kernelvec(); - -/* ======================================================================== - * trap_init() — BSP: set stvec, init IrqManager - * ======================================================================== */ - -void trap_init() -{ - g_irq_manager.init(); - arch::trap::set_vector(reinterpret_cast<uint64>(&kernelvec)); -} - -/* ======================================================================== - * trap_init_ap() — AP: set this CPU's stvec - * ======================================================================== */ - -void trap_init_ap() -{ - arch::trap::set_vector(reinterpret_cast<uint64>(&kernelvec)); -} - diff --git a/na/arch/riscv/trap/trap_vector.S b/na/arch/riscv/trap/trap_vector.S deleted file mode 100644 index 8e45cae..0000000 --- a/na/arch/riscv/trap/trap_vector.S +++ /dev/null @@ -1,283 +0,0 @@ -/* 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 |
