feat(adapter-riscv): add trap handling, timer via SBI, and S/U-mode trap vector

This commit is contained in:
2026-10-18 01:53:37 +02:00
parent d9de828420
commit 941704bbbc
3 changed files with 237 additions and 0 deletions

View File

@@ -2,6 +2,8 @@
#![no_main]
mod page_table;
mod timer;
mod trap;
mod uart;
use core::arch::global_asm;
@@ -48,6 +50,10 @@ extern "C" fn kmain() -> ! {
page_table::activate(kernel_pt);
uart.write_str("k-os: MMU enabled (Sv39)\n");
trap::init_trap();
timer::init_timer();
uart.write_str("k-os: traps and timer initialized\n");
loop {
unsafe { core::arch::asm!("wfi") };
}

View File

@@ -0,0 +1,28 @@
const TICK_INTERVAL: u64 = 10_000_000; // 1 second at 10MHz
pub fn read_time() -> u64 {
let time: u64;
unsafe { core::arch::asm!("rdtime {0}", out(reg) time) };
time
}
fn sbi_set_timer(stime: u64) {
unsafe {
core::arch::asm!(
"ecall",
in("a0") stime,
in("a6") 0usize,
in("a7") 0x54494D45usize, // SBI Timer extension ID
);
}
}
pub fn init_timer() {
let now = read_time();
sbi_set_timer(now + TICK_INTERVAL);
}
pub fn handle_timer_interrupt() {
let now = read_time();
sbi_set_timer(now + TICK_INTERVAL);
}

View File

@@ -0,0 +1,203 @@
use core::arch::global_asm;
#[repr(C)]
pub struct TrapFrame {
pub regs: [usize; 32], // x0-x31
pub sepc: usize,
pub sstatus: usize,
}
impl TrapFrame {
pub fn new() -> Self {
Self {
regs: [0; 32],
sepc: 0,
sstatus: 0,
}
}
}
// sscratch = 0 when in S-mode, = kernel_stack_top when in U-mode
// On trap: swap sp/sscratch. If sp==0 after swap, we came from S-mode (undo swap).
global_asm!(r#"
.align 4
.globl trap_vector
trap_vector:
csrrw sp, sscratch, sp
bnez sp, _from_user
# came from S-mode: sscratch had 0, sp is now 0, old sp is in sscratch
csrrw sp, sscratch, sp # undo: sp = old kernel sp, sscratch = 0
_from_user:
# sp is now a valid kernel stack (either kept or swapped in)
addi sp, sp, -272
sd x1, 8(sp)
sd x3, 24(sp)
sd x4, 32(sp)
sd x5, 40(sp)
sd x6, 48(sp)
sd x7, 56(sp)
sd x8, 64(sp)
sd x9, 72(sp)
sd x10, 80(sp)
sd x11, 88(sp)
sd x12, 96(sp)
sd x13, 104(sp)
sd x14, 112(sp)
sd x15, 120(sp)
sd x16, 128(sp)
sd x17, 136(sp)
sd x18, 144(sp)
sd x19, 152(sp)
sd x20, 160(sp)
sd x21, 168(sp)
sd x22, 176(sp)
sd x23, 184(sp)
sd x24, 192(sp)
sd x25, 200(sp)
sd x26, 208(sp)
sd x27, 216(sp)
sd x28, 224(sp)
sd x29, 232(sp)
sd x30, 240(sp)
sd x31, 248(sp)
# save user sp from sscratch (0 if from S-mode, user sp if from U-mode)
csrr t0, sscratch
sd t0, 16(sp)
csrr t0, sepc
sd t0, 256(sp)
csrr t0, sstatus
sd t0, 264(sp)
mv a0, sp
call trap_handler_rust
ld t0, 256(sp)
csrw sepc, t0
ld t0, 264(sp)
csrw sstatus, t0
# check if returning to U-mode: sstatus.SPP (bit 8)
# if SPP=0 (user mode), restore sscratch with user sp
andi t1, t0, 0x100
bnez t1, _restore_smode
# returning to U-mode: put user sp in sscratch
ld t0, 16(sp)
csrw sscratch, t0
_restore_smode:
ld x1, 8(sp)
ld x3, 24(sp)
ld x4, 32(sp)
ld x5, 40(sp)
ld x6, 48(sp)
ld x7, 56(sp)
ld x8, 64(sp)
ld x9, 72(sp)
ld x10, 80(sp)
ld x11, 88(sp)
ld x12, 96(sp)
ld x13, 104(sp)
ld x14, 112(sp)
ld x15, 120(sp)
ld x16, 128(sp)
ld x17, 136(sp)
ld x18, 144(sp)
ld x19, 152(sp)
ld x20, 160(sp)
ld x21, 168(sp)
ld x22, 176(sp)
ld x23, 184(sp)
ld x24, 192(sp)
ld x25, 200(sp)
ld x26, 208(sp)
ld x27, 216(sp)
ld x28, 224(sp)
ld x29, 232(sp)
ld x30, 240(sp)
ld x31, 248(sp)
addi sp, sp, 272
# if returning to U-mode, swap sp/sscratch to restore user sp
csrr t0, sstatus
andi t0, t0, 0x100
bnez t0, _sret_smode
csrrw sp, sscratch, sp # sp = user sp, sscratch = kernel sp (for next trap)
_sret_smode:
sret
"#);
const CAUSE_INTERRUPT_BIT: usize = 1 << 63;
const CAUSE_S_TIMER: usize = 5;
const CAUSE_S_EXTERNAL: usize = 9;
const CAUSE_ECALL_U: usize = 8;
pub fn init_trap() {
unsafe extern "C" {
fn trap_vector();
}
unsafe {
let addr = trap_vector as usize;
core::arch::asm!("csrw stvec, {0}", in(reg) addr);
// sscratch = 0 means "currently in S-mode"
core::arch::asm!("csrw sscratch, zero");
// enable supervisor interrupts
core::arch::asm!("csrs sstatus, {0}", in(reg) 1usize << 1);
// enable timer + external + software interrupts in sie
let sie_bits = (1usize << 5) | (1usize << 9) | (1usize << 1);
core::arch::asm!("csrs sie, {0}", in(reg) sie_bits);
}
}
pub fn set_kernel_stack(kernel_sp: usize) {
unsafe {
core::arch::asm!("csrw sscratch, {0}", in(reg) kernel_sp);
}
}
#[unsafe(no_mangle)]
extern "C" fn trap_handler_rust(frame: &mut TrapFrame) {
let scause: usize;
unsafe { core::arch::asm!("csrr {0}, scause", out(reg) scause) };
let uart = crate::uart::Uart::new();
if scause & CAUSE_INTERRUPT_BIT != 0 {
let cause = scause & !CAUSE_INTERRUPT_BIT;
match cause {
CAUSE_S_TIMER => {
crate::timer::handle_timer_interrupt();
}
CAUSE_S_EXTERNAL => {
uart.write_str("[EXT]");
}
_ => {}
}
} else {
match scause {
CAUSE_ECALL_U => {
frame.sepc += 4;
uart.write_str("[ecall]\n");
}
_ => {
uart.write_str("FAULT: scause=");
uart.write_hex(scause);
let stval: usize;
unsafe { core::arch::asm!("csrr {0}, stval", out(reg) stval) };
uart.write_str(" stval=");
uart.write_hex(stval);
uart.write_str(" sepc=");
uart.write_hex(frame.sepc);
uart.put_char(b'\n');
loop { unsafe { core::arch::asm!("wfi") }; }
}
}
}
}