diff --git a/crates/adapter-riscv/src/main.rs b/crates/adapter-riscv/src/main.rs index 04b9d62..199f928 100644 --- a/crates/adapter-riscv/src/main.rs +++ b/crates/adapter-riscv/src/main.rs @@ -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") }; } diff --git a/crates/adapter-riscv/src/timer.rs b/crates/adapter-riscv/src/timer.rs new file mode 100644 index 0000000..6acadb3 --- /dev/null +++ b/crates/adapter-riscv/src/timer.rs @@ -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); +} diff --git a/crates/adapter-riscv/src/trap.rs b/crates/adapter-riscv/src/trap.rs new file mode 100644 index 0000000..849e9f6 --- /dev/null +++ b/crates/adapter-riscv/src/trap.rs @@ -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") }; } + } + } + } +}