diff options
Diffstat (limited to 'na/arch/riscv/include')
| -rw-r--r-- | na/arch/riscv/include/arch/context.h | 33 | ||||
| -rw-r--r-- | na/arch/riscv/include/arch/cpu.h | 64 | ||||
| -rw-r--r-- | na/arch/riscv/include/arch/mmu.h | 63 | ||||
| -rw-r--r-- | na/arch/riscv/include/arch/trap.h | 82 | ||||
| -rw-r--r-- | na/arch/riscv/include/arch/trapframe.h | 92 |
5 files changed, 334 insertions, 0 deletions
diff --git a/na/arch/riscv/include/arch/context.h b/na/arch/riscv/include/arch/context.h new file mode 100644 index 0000000..3d935fa --- /dev/null +++ b/na/arch/riscv/include/arch/context.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2026 Richard Qin */ + +#pragma once + +/* + * Context Switch Structure (RISC-V 64) + * + * Holds only callee-saved registers for voluntary (non-preemptive) + * context switches between kernel threads / tasks. + * + * Used by: arch::context_switch() in swtch.S + * Reference: docs/specs/arch_abstraction.md §3.1 + */ + +#include <lume/types.h> + +namespace arch { + +struct Context { + uint64 ra; // 0: Return address + uint64 sp; // 8: Stack pointer + uint64 s[12]; // 16: s0-s11 (callee-saved registers) +}; + +static_assert(sizeof(Context) == 112); + +// Assembly-implemented context switch. +// Saves current CPU registers to old_ctx, loads next_ctx into CPU. +// Defined in arch/riscv/trap/swtch.S +extern "C" void context_switch(Context* old_ctx, Context* next_ctx); + +} // namespace arch diff --git a/na/arch/riscv/include/arch/cpu.h b/na/arch/riscv/include/arch/cpu.h new file mode 100644 index 0000000..644ae37 --- /dev/null +++ b/na/arch/riscv/include/arch/cpu.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2026 Richard Qin */ + +#pragma once + +/* + * CPU Control & Status Primitives (RISC-V 64) + * + * Provides interrupt enable/disable, CPU ID, timer, and halt. + * All functions are static inline — zero call overhead. + * + * Precondition: tp register must be initialized with CPU ID + * by entry.S before any of these functions are called. + * + * Reference: docs/specs/arch_abstraction.md §4.1 + */ + +#include <lume/types.h> + +/* RISC-V sstatus register bit definitions */ +#define SSTATUS_SIE (1UL << 1) /* Supervisor Interrupt Enable */ +#define SSTATUS_SPIE (1UL << 5) /* Supervisor Previous Interrupt Enable */ +#define SSTATUS_SPP (1UL << 8) /* Supervisor Previous Privilege (0=U, 1=S) */ + +namespace arch::cpu { + +// Get current CPU ID (from tp register, set by entry.S) +static inline uint64 id() { + uint64 val; + __asm__ volatile("mv %0, tp" : "=r"(val)); + return val; +} + +// Enable interrupts (set sstatus.SIE = 1) +static inline void intr_on() { + __asm__ volatile("csrs sstatus, %0" :: "r"(SSTATUS_SIE)); +} + +// Disable interrupts (clear sstatus.SIE = 0) +static inline void intr_off() { + __asm__ volatile("csrc sstatus, %0" :: "r"(SSTATUS_SIE)); +} + +// Check if interrupts are currently enabled +static inline bool intr_enabled() { + uint64 sstatus; + __asm__ volatile("csrr %0, sstatus" : "=r"(sstatus)); + return (sstatus & SSTATUS_SIE) != 0; +} + +// Halt CPU until next interrupt (wfi instruction) +static inline void halt_until_interrupt() { + __asm__ volatile("wfi"); +} + +// Read hardware monotonic timer (rdtime instruction) +// Returns ticks since boot; frequency from FDT timebase-frequency. +static inline uint64 read_time() { + uint64 val; + __asm__ volatile("rdtime %0" : "=r"(val)); + return val; +} + +} // namespace arch::cpu diff --git a/na/arch/riscv/include/arch/mmu.h b/na/arch/riscv/include/arch/mmu.h new file mode 100644 index 0000000..a0ee1ed --- /dev/null +++ b/na/arch/riscv/include/arch/mmu.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2026 Richard Qin */ + +#pragma once + +/* + * MMU Hardware Primitives (RISC-V SV39) + * + * Page table switching, TLB flushing, and fault address reading. + * All functions are static inline — zero call overhead. + * + * Note on SMP: flush_tlb_page() only flushes the LOCAL CPU's TLB. + * Cross-CPU TLB shootdown via IPI is not yet implemented. + * + * Reference: docs/specs/arch_abstraction.md §4.2 + */ + +#include <lume/types.h> + +/* SATP register: mode field for SV39 */ +#define SATP_SV39 (8UL << 60) + +/* SATP PPN mask (bits 43:0) */ +#define SATP_PPN_MASK ((1UL << 44) - 1) + +namespace arch::mmu { + +// Read the faulting virtual address from stval (set by HW on page fault) +static inline uint64 get_fault_address() { + uint64 val; + __asm__ volatile("csrr %0, stval" : "=r"(val)); + return val; +} + +// Switch the top-level page table. +// Takes a PHYSICAL ADDRESS (not PPN); internally shifts right by 12 +// and combines with SV39 mode bits before writing to satp. +static inline void set_page_table(uint64 physical_pgdir_addr) { + uint64 ppn = physical_pgdir_addr >> 12; + uint64 satp_val = SATP_SV39 | ppn; + __asm__ volatile("csrw satp, %0" :: "r"(satp_val)); + // Note: caller should call flush_tlb_all() after this +} + +// Read the current page table physical address. +// Extracts PPN from satp and converts back to physical address. +static inline uint64 get_page_table() { + uint64 satp_val; + __asm__ volatile("csrr %0, satp" : "=r"(satp_val)); + return (satp_val & SATP_PPN_MASK) << 12; +} + +// Flush the entire TLB (all ASIDs, all addresses) +static inline void flush_tlb_all() { + __asm__ volatile("sfence.vma zero, zero" ::: "memory"); +} + +// Flush a single TLB entry for the given virtual address +static inline void flush_tlb_page(uint64 vaddr) { + __asm__ volatile("sfence.vma %0, zero" :: "r"(vaddr) : "memory"); +} + +} // namespace arch::mmu diff --git a/na/arch/riscv/include/arch/trap.h b/na/arch/riscv/include/arch/trap.h new file mode 100644 index 0000000..c99d8cb --- /dev/null +++ b/na/arch/riscv/include/arch/trap.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2026 Richard Qin */ + +#pragma once + +/* + * Trap Vector Control Primitives (RISC-V 64) + * + * Set/read the S-mode trap vector (stvec), trap cause (scause), + * and trap value (stval) registers. + * + * Reference: docs/specs/arch_abstraction.md §4.3 + */ + +#include <lume/types.h> + +/* scause interrupt bit (bit 63): 1 = interrupt, 0 = exception */ +#define SCAUSE_INTERRUPT (1UL << 63) + +/* Exception cause codes (scause with bit 63 = 0) */ +#define EXC_INST_MISALIGNED 0 +#define EXC_INST_ACCESS_FAULT 1 +#define EXC_ILLEGAL_INST 2 +#define EXC_BREAKPOINT 3 +#define EXC_LOAD_MISALIGNED 4 +#define EXC_LOAD_ACCESS_FAULT 5 +#define EXC_STORE_MISALIGNED 6 +#define EXC_STORE_ACCESS_FAULT 7 +#define EXC_ECALL_FROM_U 8 +#define EXC_ECALL_FROM_S 9 +#define EXC_INST_PAGE_FAULT 12 +#define EXC_LOAD_PAGE_FAULT 13 +#define EXC_STORE_PAGE_FAULT 15 + +/* Interrupt cause codes (scause with bit 63 = 1, lower bits) */ +#define IRQ_S_SOFTWARE 1 +#define IRQ_S_TIMER 5 +#define IRQ_S_EXTERNAL 9 + +namespace arch::trap { + +// Set S-mode trap vector base address (writes stvec). +// Mode: Direct (all traps go to handler_addr). +static inline void set_vector(uint64 handler_addr) { + // Lowest 2 bits of stvec = mode. 0 = Direct. + __asm__ volatile("csrw stvec, %0" :: "r"(handler_addr)); +} + +// Read current trap vector base address +static inline uint64 get_vector() { + uint64 val; + __asm__ volatile("csrr %0, stvec" : "=r"(val)); + return val; +} + +// Read trap cause register (scause) +// Bit 63: 1=interrupt, 0=exception. Lower bits: cause code. +static inline uint64 get_cause() { + uint64 val; + __asm__ volatile("csrr %0, scause" : "=r"(val)); + return val; +} + +// Read trap value register (stval) +// Contains faulting address (page faults) or faulting instruction. +static inline uint64 get_value() { + uint64 val; + __asm__ volatile("csrr %0, stval" : "=r"(val)); + return val; +} + +// Check if a scause value represents an interrupt (vs exception) +static inline bool is_interrupt(uint64 cause) { + return (cause & SCAUSE_INTERRUPT) != 0; +} + +// Extract the cause code (strip the interrupt bit) +static inline uint64 cause_code(uint64 cause) { + return cause & ~SCAUSE_INTERRUPT; +} + +} // namespace arch::trap diff --git a/na/arch/riscv/include/arch/trapframe.h b/na/arch/riscv/include/arch/trapframe.h new file mode 100644 index 0000000..de49e9c --- /dev/null +++ b/na/arch/riscv/include/arch/trapframe.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2026 Richard Qin */ + +#pragma once + +/* + * Trap Frame Structure (RISC-V 64) + * + * Saved/restored by the assembly trap_vector on every interrupt, + * exception, or system call. Contains the full CPU snapshot needed + * to resume execution after trap handling. + * + * Layout must match the assembly offsets in trap_vector.S exactly. + * Reference: docs/specs/arch_abstraction.md §3.2 + */ + +/* + * TrapFrame field offsets for use in assembly. + * These must be kept in sync with the struct layout below. + * Placed before any C++ code so assembly can #include this file. + */ +#define TF_KERNEL_SATP 0 +#define TF_KERNEL_SP 8 +#define TF_KERNEL_TRAP 16 +#define TF_KERNEL_CPUID 24 +#define TF_EPC 32 +#define TF_SSTATUS 40 +#define TF_RA 48 +#define TF_SP 56 +#define TF_GP 64 +#define TF_TP 72 +#define TF_T0 80 +#define TF_T1 88 +#define TF_T2 96 +#define TF_T3 104 +#define TF_T4 112 +#define TF_T5 120 +#define TF_T6 128 +#define TF_S0 136 +#define TF_S1 144 +#define TF_S2 152 +#define TF_S3 160 +#define TF_S4 168 +#define TF_S5 176 +#define TF_S6 184 +#define TF_S7 192 +#define TF_S8 200 +#define TF_S9 208 +#define TF_S10 216 +#define TF_S11 224 +#define TF_A0 232 +#define TF_A1 240 +#define TF_A2 248 +#define TF_A3 256 +#define TF_A4 264 +#define TF_A5 272 +#define TF_A6 280 +#define TF_A7 288 +#define TF_SIZE 296 + +#ifndef __ASSEMBLER__ + +#include <lume/types.h> + +namespace arch { + +struct TrapFrame { + /* ---- Kernel metadata (offset 0-39) ---- */ + uint64 kernel_satp; // 0: Kernel page table (written to satp on trap entry) + uint64 kernel_sp; // 8: Top of kernel stack for this task + uint64 kernel_trap; // 16: Address of C++ trap_handler function + uint64 kernel_cpu_id; // 24: CPU ID (loaded into tp on trap entry) + uint64 epc; // 32: Interrupted instruction address (sepc) + + /* ---- CSR backup (offset 40-47) ---- */ + uint64 sstatus; // 40: Privilege status (SPP, SPIE bits) + + /* ---- General-purpose registers (offset 48-295) ---- */ + uint64 ra; // 48: x1 - Return address + uint64 sp; // 56: x2 - User stack pointer + uint64 gp; // 64: x3 - Global pointer + uint64 tp; // 72: x4 - Thread pointer + uint64 t[7]; // 80: x5-x7, x28-x31 (t0-t6, caller-saved temporaries) + uint64 s[12]; // 136: x8-x9, x18-x27 (s0-s11, callee-saved) + uint64 a[8]; // 232: x10-x17 (a0-a7, arguments / syscall params) +}; + +static_assert(sizeof(TrapFrame) == 296, "TrapFrame size mismatch with assembly offsets"); + +} // namespace arch + +#endif /* __ASSEMBLER__ */ |
