feat: rewrite the trap mechanism and add simple timer support

This commit is contained in:
Paul Pan 2024-02-16 13:38:24 +08:00
parent b9642c1ad2
commit 661e910ae7
15 changed files with 392 additions and 293 deletions

40
Cargo.lock generated
View File

@ -22,9 +22,21 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.4.1" version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
[[package]]
name = "critical-section"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
[[package]]
name = "embedded-hal"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
@ -60,6 +72,16 @@ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
] ]
[[package]]
name = "riscv"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f5c1b8bf41ea746266cdee443d1d1e9125c86ce1447e1a2615abd34330d33a9"
dependencies = [
"critical-section",
"embedded-hal",
]
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.14" version = "1.0.14"
@ -68,21 +90,18 @@ checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
[[package]] [[package]]
name = "sbi-rt" name = "sbi-rt"
version = "0.0.2" version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c113c53291db8ac141e01f43224ed488b8d6001ab66737b82e04695a43a42b7" checksum = "7fbaa69be1eedc61c426e6d489b2260482e928b465360576900d52d496a58bd0"
dependencies = [ dependencies = [
"sbi-spec", "sbi-spec",
] ]
[[package]] [[package]]
name = "sbi-spec" name = "sbi-spec"
version = "0.0.4" version = "0.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d4027cf9bb591a9fd0fc0e283be6165c5abe96cb73e9f0e24738c227f425377" checksum = "e6e36312fb5ddc10d08ecdc65187402baba4ac34585cb9d1b78522ae2358d890"
dependencies = [
"static_assertions",
]
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
@ -115,9 +134,10 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
name = "tiny_os" name = "tiny_os"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bitflags 2.4.1", "bitflags 2.4.2",
"lazy_static", "lazy_static",
"log", "log",
"riscv",
"sbi-rt", "sbi-rt",
"spin 0.9.8", "spin 0.9.8",
"static_assertions", "static_assertions",

View File

@ -10,8 +10,6 @@ default = ["arch_riscv64", "board_virt", "log_color", "frame_freelist"]
arch_riscv64 = [] arch_riscv64 = []
arch_riscv32 = [] arch_riscv32 = []
arch_arm = []
arch_x86 = []
board_default = [] board_default = []
board_virt = [] board_virt = []
@ -32,7 +30,8 @@ panic = "abort"
bitflags = "2.4.1" bitflags = "2.4.1"
lazy_static = { version = "1.4.0", features = ["spin_no_std"] } lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
log = "0.4" log = "0.4"
sbi-rt = { version = "0.0.2", features = ["legacy"] } riscv = { version = "0.11.1", features = ["s-mode"] }
sbi-rt = { version = "0.0.3", features = ["legacy"] }
spin = "0.9.8" spin = "0.9.8"
static_assertions = "1.1.0" static_assertions = "1.1.0"
uart_16550 = "0.3" uart_16550 = "0.3"

View File

@ -1,123 +1,11 @@
.section .trampoline_section .equ XLENB, 4
.global trampoline .macro LD_SP r1, r2
trampoline: lw \r1, \r2*XLENB(sp)
.endm
.align 4 .macro SD_SP r1, r2
sw \r1, \r2*XLENB(sp)
.global __user2kernel .endm
__user2kernel:
# load trapframe
csrw sscratch, a0 # save a0, will be saved later
li a0, {TRAPFRAME} # TRAPFRAME will be mapped to the same virtual address
# save GPRs
sw ra, 0(a0)
sw sp, 4(a0)
sw gp, 8(a0)
sw tp, 12(a0)
sw t0, 16(a0)
sw t1, 20(a0)
sw t2, 24(a0)
sw s0, 28(a0)
sw s1, 32(a0)
#sw a0, 36(a0)
sw a1, 40(a0)
sw a2, 44(a0)
sw a3, 48(a0)
sw a4, 52(a0)
sw a5, 56(a0)
sw a6, 60(a0)
sw a7, 64(a0)
sw s2, 68(a0)
sw s3, 72(a0)
sw s4, 76(a0)
sw s5, 80(a0)
sw s6, 84(a0)
sw s7, 88(a0)
sw s8, 92(a0)
sw s9, 96(a0)
sw s10, 100(a0)
sw s11, 104(a0)
sw t3, 108(a0)
sw t4, 112(a0)
sw t5, 116(a0)
sw t6, 120(a0)
# save a0
csrr t0, sscratch
sw t0, 36(a0)
# save user epc / sstatus
csrr t0, sepc
csrr t1, sstatus
sw t0, 124(a0)
sw t1, 128(a0)
# restore kernel env
lw sp, 132(a0) # kernel sp
lw tp, 136(a0) # kernel tp
lw t0, 140(a0) # trap handler
lw t1, 144(a0) # page table
# load kernel page table
sfence.vma zero, zero
csrw satp, t1
sfence.vma zero, zero
# jump to trap handler
jr t0
.global __kernel2user
__kernel2user:
# restore user page table (a0)
sfence.vma zero, zero
csrw satp, a0
sfence.vma zero, zero
li a0, {TRAPFRAME}
# restore user epc / sstatus
lw t0, 124(a0)
lw t1, 128(a0)
csrw sepc, t0
csrw sstatus, t1
# restore GPRs
lw ra, 0(a0)
lw sp, 4(a0)
lw gp, 8(a0)
lw tp, 12(a0)
lw t0, 16(a0)
lw t1, 20(a0)
lw t2, 24(a0)
lw s0, 28(a0)
lw s1, 32(a0)
#lw a0, 36(a0)
lw a1, 40(a0)
lw a2, 44(a0)
lw a3, 48(a0)
lw a4, 52(a0)
lw a5, 56(a0)
lw a6, 60(a0)
lw a7, 64(a0)
lw s2, 68(a0)
lw s3, 72(a0)
lw s4, 76(a0)
lw s5, 80(a0)
lw s6, 84(a0)
lw s7, 88(a0)
lw s8, 92(a0)
lw s9, 96(a0)
lw s10, 100(a0)
lw s11, 104(a0)
lw t3, 108(a0)
lw t4, 112(a0)
lw t5, 116(a0)
lw t6, 120(a0)
lw a0, 36(a0)
# go back to usermode
sret
# vim:set ts=8 noexpandtab: # vim:set ts=8 noexpandtab:

View File

@ -1,123 +1,11 @@
.section .trampoline_section .equ XLENB, 8
.global trampoline .macro LD_SP r1, r2
trampoline: ld \r1, \r2*XLENB(sp)
.endm
.align 4 .macro SD_SP r1, r2
sd \r1, \r2*XLENB(sp)
.global __user2kernel .endm
__user2kernel:
# load trapframe
csrw sscratch, a0 # save a0, will be saved later
li a0, {TRAPFRAME} # TRAPFRAME will be mapped to the same virtual address
# save GPRs
sd ra, 0(a0)
sd sp, 8(a0)
sd gp, 16(a0)
sd tp, 24(a0)
sd t0, 32(a0)
sd t1, 40(a0)
sd t2, 48(a0)
sd s0, 56(a0)
sd s1, 64(a0)
#sd a0, 72(a0)
sd a1, 80(a0)
sd a2, 88(a0)
sd a3, 96(a0)
sd a4, 104(a0)
sd a5, 112(a0)
sd a6, 120(a0)
sd a7, 128(a0)
sd s2, 136(a0)
sd s3, 144(a0)
sd s4, 152(a0)
sd s5, 160(a0)
sd s6, 168(a0)
sd s7, 176(a0)
sd s8, 184(a0)
sd s9, 192(a0)
sd s10, 200(a0)
sd s11, 208(a0)
sd t3, 216(a0)
sd t4, 224(a0)
sd t5, 232(a0)
sd t6, 240(a0)
# save a0
csrr t0, sscratch
sd t0, 72(a0)
# save user epc / sstatus
csrr t0, sepc
csrr t1, sstatus
sd t0, 248(a0)
sd t1, 256(a0)
# restore kernel env
ld sp, 264(a0) # kernel sp
ld tp, 272(a0) # kernel tp
ld t0, 280(a0) # trap handler
ld t1, 288(a0) # page table
# load kernel page table
sfence.vma zero, zero
csrw satp, t1
sfence.vma zero, zero
# jump to trap handler
jr t0
.global __kernel2user
__kernel2user:
# restore user page table (a0)
sfence.vma zero, zero
csrw satp, a0
sfence.vma zero, zero
li a0, {TRAPFRAME}
# restore user epc / sstatus
ld t0, 248(a0)
ld t1, 256(a0)
csrw sepc, t0
csrw sstatus, t1
# restore GPRs
ld ra, 0(a0)
ld sp, 8(a0)
ld gp, 16(a0)
ld tp, 24(a0)
ld t0, 32(a0)
ld t1, 40(a0)
ld t2, 48(a0)
ld s0, 56(a0)
ld s1, 64(a0)
#ld a0, 72(a0)
ld a1, 80(a0)
ld a2, 88(a0)
ld a3, 96(a0)
ld a4, 104(a0)
ld a5, 112(a0)
ld a6, 120(a0)
ld a7, 128(a0)
ld s2, 136(a0)
ld s3, 144(a0)
ld s4, 152(a0)
ld s5, 160(a0)
ld s6, 168(a0)
ld s7, 176(a0)
ld s8, 184(a0)
ld s9, 192(a0)
ld s10, 200(a0)
ld s11, 208(a0)
ld t3, 216(a0)
ld t4, 224(a0)
ld t5, 232(a0)
ld t6, 240(a0)
ld a0, 72(a0)
# go back to usermode
sret
# vim:set ts=8 noexpandtab: # vim:set ts=8 noexpandtab:

View File

@ -0,0 +1,169 @@
.section .text.trap
.extern trap_handler
.align 4
.global trap_entry
trap_entry:
# from user: switch to saved kernel context
# from kernel: sscratch = 0
csrrw sp, sscratch, sp
bnez sp, __trap_from_user
__trap_from_kernel:
# Trap from kernelspace, no existing context, reserve space on kernel stack
csrr sp, sscratch
addi sp, sp, -34*XLENB
__trap_from_user:
# Trap from userspace, use user context
# save GPRs, except zero, sp
SD_SP ra, 0
#SD_SP sp, 1
SD_SP gp, 2
SD_SP tp, 3
SD_SP t0, 4
SD_SP t1, 5
SD_SP t2, 6
SD_SP s0, 7
SD_SP s1, 8
SD_SP a0, 9
SD_SP a1, 10
SD_SP a2, 11
SD_SP a3, 12
SD_SP a4, 13
SD_SP a5, 14
SD_SP a6, 15
SD_SP a7, 16
SD_SP s2, 17
SD_SP s3, 18
SD_SP s4, 19
SD_SP s5, 20
SD_SP s6, 21
SD_SP s7, 22
SD_SP s8, 23
SD_SP s9, 24
SD_SP s10, 25
SD_SP s11, 26
SD_SP t3, 27
SD_SP t4, 28
SD_SP t5, 29
SD_SP t6, 30
# save sp, sepc, sstatus
csrrw t0, sscratch, zero # make sure sscratch is zero in kernel
csrr t1, sepc
csrr t2, sstatus
SD_SP t0, 1
SD_SP t1, 31
SD_SP t2, 32
# find out whether we are from kernel (sstatus.SPP)
andi t2, t2, 1 << 8
beqz t2, __trap_from_user_next
__trap_from_kernel_next:
# goto trap_handler
mv a0, sp
la ra, __trap_return
j trap_handler
__trap_from_user_next:
# we come from switch_to_user, which saved callee-saved registers
# restore kernel sp
LD_SP sp, 33
# restore callee-saved registers
LD_SP s0, 0
LD_SP s1, 1
LD_SP s2, 2
LD_SP s3, 3
LD_SP s4, 4
LD_SP s5, 5
LD_SP s6, 6
LD_SP s7, 7
LD_SP s8, 8
LD_SP s9, 9
LD_SP s10, 10
LD_SP s11, 11
LD_SP ra, 12
LD_SP tp, 13 # not callee-saved
addi sp, sp, 14*XLENB
# return from switch_to_user
ret
.global switch_to_user
switch_to_user:
# save kernel callee-saved registers (into current kernel stack)
addi sp, sp, -14*XLENB
SD_SP s0, 0
SD_SP s1, 1
SD_SP s2, 2
SD_SP s3, 3
SD_SP s4, 4
SD_SP s5, 5
SD_SP s6, 6
SD_SP s7, 7
SD_SP s8, 8
SD_SP s9, 9
SD_SP s10, 10
SD_SP s11, 11
SD_SP ra, 12
SD_SP tp, 13 # not callee-saved
# switch to user context
mv t0, sp
mv sp, a0
# save kernel sp into current user context
SD_SP t0, 33
# save current user context
csrw sscratch, sp
__trap_return:
# restore sepc, sstatus
LD_SP t0, 31
LD_SP t1, 32
csrw sepc, t0
csrw sstatus, t1
# restore GPRs, except zero, sp
LD_SP ra, 0
#LD_SP sp, 1
LD_SP gp, 2
LD_SP tp, 3
LD_SP t0, 4
LD_SP t1, 5
LD_SP t2, 6
LD_SP s0, 7
LD_SP s1, 8
LD_SP a0, 9
LD_SP a1, 10
LD_SP a2, 11
LD_SP a3, 12
LD_SP a4, 13
LD_SP a5, 14
LD_SP a6, 15
LD_SP a7, 16
LD_SP s2, 17
LD_SP s3, 18
LD_SP s4, 19
LD_SP s5, 20
LD_SP s6, 21
LD_SP s7, 22
LD_SP s8, 23
LD_SP s9, 24
LD_SP s10, 25
LD_SP s11, 26
LD_SP t3, 27
LD_SP t4, 28
LD_SP t5, 29
LD_SP t6, 30
# restore user sp
LD_SP sp, 1
# go back from trap
sret
# vim:set ts=8 noexpandtab:

View File

@ -1,6 +1,8 @@
pub const UART0_BASE: usize = 0x1000_0000; pub const UART0_BASE: usize = 0x1000_0000;
pub const UART0_LSR: usize = 0x1000_0005; pub const UART0_LSR: usize = 0x1000_0005;
pub const TEST_DEVICE: *mut u32 = 0x10_0000 as *mut u32; pub const TEST_DEVICE: *mut u32 = 0x10_0000 as *mut u32;
pub const TIMER_TICKS: u64 = 100_000; // 100ms
pub mod printer; pub mod printer;
pub mod reader; pub mod reader;
pub mod timer;

View File

@ -0,0 +1,26 @@
use log::info;
use crate::plat::timer::{Timer, TimerOps};
impl TimerOps for Timer {
fn init() {
// make sure Timer extension is enabled
assert!(
sbi_rt::probe_extension(sbi_rt::Timer).is_available(),
"[SBI] Timer extension not found"
);
// enable timer interrupt and set next tick
unsafe { riscv::register::sie::set_stimer() }
Self::tick();
info!("[Timer] begin to tick");
}
fn read_cycle() -> u64 {
riscv::register::time::read64()
}
fn tick() {
sbi_rt::set_timer(Self::read_cycle() + super::TIMER_TICKS);
}
}

View File

@ -15,15 +15,6 @@ SECTIONS {
__text_start = .; __text_start = .;
*(.text.entry) *(.text.entry)
*(.text .text.*) *(.text .text.*)
. = ALIGN(PAGE_SIZE);
__trampoline_start = .;
*(.trampoline_section)
. = ALIGN(PAGE_SIZE);
__trampoline_end = .;
ASSERT(__trampoline_end - __trampoline_start == PAGE_SIZE, "trampoline does not fit into one page");
__text_end = .; __text_end = .;
} > DRAM } > DRAM

View File

@ -56,4 +56,14 @@ impl LowLevel for Hardware {
error!("[riscv/lowlevel] system_reset failed, shutdown instead"); error!("[riscv/lowlevel] system_reset failed, shutdown instead");
Self::shutdown(true); Self::shutdown(true);
} }
#[inline]
fn enable_interrupt() {
unsafe { riscv::register::sstatus::set_sie() }
}
#[inline]
fn disable_interrupt() {
unsafe { riscv::register::sstatus::clear_sie() }
}
} }

View File

@ -1,44 +1,103 @@
use lazy_static::lazy_static; use log::trace;
use riscv::register::scause::{Interrupt as I, Trap as T};
use crate::plat::timer::{Timer, TimerOps};
use crate::plat::trap::{Trap, TrapContextOps, TrapOps};
#[cfg(feature = "arch_riscv64")] #[cfg(feature = "arch_riscv64")]
core::arch::global_asm!( core::arch::global_asm!(include_str!("./asm/trap64.S"));
include_str!("./asm/trap64.S"),
TRAPFRAME = const super::layout::TRAPFRAME
);
#[cfg(feature = "arch_riscv32")] #[cfg(feature = "arch_riscv32")]
core::arch::global_asm!( core::arch::global_asm!(include_str!("./asm/trap32.S"));
include_str!("./asm/trap32.S"),
TRAPFRAME = const super::layout::TRAPFRAME core::arch::global_asm!(include_str!("./asm/trap_common.S"));
);
extern "C" { extern "C" {
pub fn __user2kernel(); fn trap_entry();
pub fn __kernel2user(ctx: *mut TrapContext); fn switch_to_user(regs: &mut TrapContext);
} }
// TODO: remove this when we have a real context switch #[no_mangle]
lazy_static! { pub extern "C" fn trap_handler(tf: &mut TrapContext) {
pub static ref __DUMMY: usize = unsafe { let scause = riscv::register::scause::read();
__user2kernel(); trace!("[Interrupt] scause: {:?}", scause.cause());
__kernel2user(core::ptr::null_mut()); match scause.cause() {
42 T::Interrupt(I::SupervisorTimer) => {
}; // TODO: refactor this
trace!("[Interrupt] Tick: {}", riscv::register::time::read64());
Timer::tick();
},
_ => {
panic!(
"[Interrupt] Unhandled Trap, scause: {:?}, bits: {:#x}",
scause.cause(),
scause.bits()
);
},
}
trace!("[Interrupt] exiting...");
} }
impl TrapOps for Trap {
fn init() {
// anyway, clear sscratch
unsafe { core::arch::asm!("csrw sscratch, zero") }
// set trap_entry
unsafe { core::arch::asm!("csrw stvec, {}", in(reg) trap_entry as usize) }
}
}
impl TrapContextOps for TrapContext {
fn restore(&mut self) {
unsafe { switch_to_user(self) }
}
}
#[derive(Debug)]
#[repr(C)]
pub struct GeneralRegs {
pub ra: usize,
pub sp: usize,
pub gp: usize,
pub tp: usize,
pub t0: usize,
pub t1: usize,
pub t2: usize,
pub s0: usize,
pub s1: usize,
pub a0: usize,
pub a1: usize,
pub a2: usize,
pub a3: usize,
pub a4: usize,
pub a5: usize,
pub a6: usize,
pub a7: usize,
pub s2: usize,
pub s3: usize,
pub s4: usize,
pub s5: usize,
pub s6: usize,
pub s7: usize,
pub s8: usize,
pub s9: usize,
pub s10: usize,
pub s11: usize,
pub t3: usize,
pub t4: usize,
pub t5: usize,
pub t6: usize,
}
#[derive(Debug)]
#[repr(C)] #[repr(C)]
pub struct TrapContext { pub struct TrapContext {
pub gprs: [usize; 31], // [0:30] GPRs exclude zero
// GPRs exclude zero pub gprs: GeneralRegs,
pub user_sepc: usize, // [31] program counter
// 248/124 user program counter pub sepc: usize,
pub user_sstatus: usize, // [32] status register
// 256/128 user status register pub sstatus: usize,
// [33] kernel sp
pub kernel_sp: usize, pub kernel_sp: usize,
// 264/132 kernel stack pointer
pub kernel_tp: usize,
// 272/136 kernel tp
pub kernel_trap: usize,
// 280/140 kernel trap handler
pub kernel_satp: usize, // 288/144 kernel page table
} }

View File

@ -1,6 +1,9 @@
use log::{debug, error, info}; use log::{debug, error, info};
use crate::plat::io::{RawConsole, Reader};
use crate::plat::lowlevel::{Hardware, LowLevel}; use crate::plat::lowlevel::{Hardware, LowLevel};
use crate::plat::timer::{Timer, TimerOps};
use crate::plat::trap::{Trap, TrapOps};
#[no_mangle] #[no_mangle]
pub extern "C" fn rust_main(hart_id: usize, device_tree_addr: usize) -> ! { pub extern "C" fn rust_main(hart_id: usize, device_tree_addr: usize) -> ! {
@ -15,8 +18,29 @@ pub extern "C" fn rust_main(hart_id: usize, device_tree_addr: usize) -> ! {
info!("hart_id = {}", hart_id); info!("hart_id = {}", hart_id);
debug!("device_tree_addr = {:#x}", device_tree_addr); debug!("device_tree_addr = {:#x}", device_tree_addr);
if hart_id != 0 {
error!("No SMP support now, halting...");
Hardware::halt();
}
// TODO: setup and start scheduler // TODO: setup and start scheduler
// --- some dummy tests ---
Trap::init();
Timer::init();
Hardware::enable_interrupt();
loop {
let ch = RawConsole::get_char();
debug!("Key: {}", ch);
if ch == 'q' {
break;
}
}
// --- end ---
error!("[rust_main] Should not reach here! Maybe scheduler is not working?"); error!("[rust_main] Should not reach here! Maybe scheduler is not working?");
Hardware::shutdown(true); Hardware::shutdown(true);
} }

View File

@ -1,7 +1,10 @@
pub struct Hardware; pub struct Hardware;
pub trait LowLevel { pub trait LowLevel {
fn halt() {} fn halt() {
#[allow(clippy::empty_loop)]
loop {}
}
fn shutdown(_failure: bool) -> ! { fn shutdown(_failure: bool) -> ! {
Self::halt(); Self::halt();
@ -14,5 +17,7 @@ pub trait LowLevel {
panic!("Reset is not implemented for this architecture"); panic!("Reset is not implemented for this architecture");
} }
fn disable_interrupt() {} fn enable_interrupt();
fn disable_interrupt();
} }

View File

@ -1,2 +1,4 @@
pub mod io; pub mod io;
pub mod lowlevel; pub mod lowlevel;
pub mod timer;
pub mod trap;

7
src/plat/timer.rs Normal file
View File

@ -0,0 +1,7 @@
pub struct Timer;
pub trait TimerOps {
fn init();
fn read_cycle() -> u64;
fn tick();
}

9
src/plat/trap.rs Normal file
View File

@ -0,0 +1,9 @@
pub struct Trap;
pub trait TrapOps {
fn init();
}
pub trait TrapContextOps {
fn restore(&mut self);
}