diff options
Diffstat (limited to 'na/arch/riscv/trap/trap_md.cc')
| -rw-r--r-- | na/arch/riscv/trap/trap_md.cc | 241 |
1 files changed, 0 insertions, 241 deletions
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)); -} - |
