diff --git a/os/Makefile b/os/Makefile index e02bc81..a8f180f 100644 --- a/os/Makefile +++ b/os/Makefile @@ -24,7 +24,7 @@ ifeq ($(MODE), release) endif # KERNEL ENTRY -KERNEL_ENTRY_PA := 0x80200000 +KERNEL_ENTRY_PA := 0x80000000 # Binutils OBJDUMP := rust-objdump --arch-name=riscv64 @@ -71,7 +71,21 @@ disasm-vim: kernel @nvim $(DISASM_TMP) @rm $(DISASM_TMP) -run: run-inner +run: run-inner-none + +run-inner-none: build + @qemu-system-riscv64 \ + -M 128m \ + -machine virt \ + -bios none \ + $(GUI_OPTION) \ + -kernel $(KERNEL_ELF) \ + -drive file=$(FS_IMG),if=none,format=raw,id=x0 \ + -device virtio-blk-device,drive=x0 \ +# -device virtio-gpu-device \ + -device virtio-keyboard-device \ + -device virtio-mouse-device \ + -serial stdio run-inner: build @qemu-system-riscv64 \ diff --git a/os/src/boards/qemu.rs b/os/src/boards/qemu.rs index ffabff6..d68ee2f 100644 --- a/os/src/boards/qemu.rs +++ b/os/src/boards/qemu.rs @@ -2,7 +2,7 @@ pub const CLOCK_FREQ: usize = 12500000; pub const MMIO: &[(usize, usize)] = &[ (0x0010_0000, 0x00_2000), // VIRT_TEST/RTC in virt machine - (0x2000000, 0x10000), + (0x2000000, 0x10000), // core local interrupter (CLINT) (0xc000000, 0x210000), // VIRT_PLIC in virt machine (0x10000000, 0x9000), // VIRT_UART0 with GPU in virt machine ]; @@ -53,6 +53,57 @@ pub fn irq_handler() { plic.complete(0, IntrTargetPriority::Supervisor, intr_src_id); } + +// core local interrupter (CLINT), which contains the timer +pub const CLINT: usize = 0x2000000; +pub const fn clint_mtimecmp(hartid: usize) -> usize { + CLINT + 0x4000 + 8 * hartid +} +pub const CLINT_MTIME: usize = CLINT + 0xBFF8; // Cycles since boot. + +#[naked] +#[repr(align(16))] // if miss this alignment, a load access fault will occur. +#[no_mangle] +pub unsafe extern "C" fn timervec() -> ! { + // start.rs has set up the memory that mscratch points to: + // scratch[0,8,16] : register save area. + // scratch[24] : address of CLINT's MTIMECMP register. + // scratch[32] : desired interval between interrupts. + + // Now, mscrach has a pointer to an additional scratch space. + // to aboid overwriting the contents of the integer registers, + // the prologue of an interrupts handler usually begins by swapping + // an integer register(say a0) with mscratch CSR. + // The interrupt handler stores the integer registers + // used for processing in this scratch space. + // a0 saved in mscrach, a1 ~ a3 saved in scratch space. + //loop {} + asm!( + "csrrw a0, mscratch, a0", + "sd a1, 0(a0)", + "sd a2, 8(a0)", + "sd a3, 16(a0)", + // schedule the next timer interrupt + // by adding interval to mtimecmp. + "ld a1, 24(a0)", // CLINT_MTIMECMP(hartid) contents + "ld a2, 32(a0)", // interval + "ld a3, 0(a1)", + "add a3, a3, a2", + "sd a3, 0(a1)", + // raise a supervisor software interrupt. + "li a1, 2", + "csrw sip, a1", + // restore and return + "ld a3, 16(a0)", + "ld a2, 8(a0)", + "ld a1, 0(a0)", + "csrrw a0, mscratch, a0", + "mret", + options(noreturn) + ); +} + + //ref:: https://github.com/andre-richter/qemu-exit use core::arch::asm; diff --git a/os/src/entry.asm b/os/src/entry.asm index c32d68f..1d1a175 100644 --- a/os/src/entry.asm +++ b/os/src/entry.asm @@ -2,7 +2,7 @@ .globl _start _start: la sp, boot_stack_top - call rust_main + call rust_start .section .bss.stack .globl boot_stack_lower_bound diff --git a/os/src/linker-qemu.ld b/os/src/linker-qemu.ld index 5baafbd..92dd51c 100644 --- a/os/src/linker-qemu.ld +++ b/os/src/linker-qemu.ld @@ -1,6 +1,6 @@ OUTPUT_ARCH(riscv) ENTRY(_start) -BASE_ADDRESS = 0x80200000; +BASE_ADDRESS = 0x80000000; SECTIONS { diff --git a/os/src/main.rs b/os/src/main.rs index 299e4cf..7859560 100644 --- a/os/src/main.rs +++ b/os/src/main.rs @@ -2,6 +2,8 @@ #![no_main] #![feature(panic_info_message)] #![feature(alloc_error_handler)] +#![feature(naked_functions)] +#![feature(fn_align)] use crate::drivers::{GPU_DEVICE, KEYBOARD_DEVICE, MOUSE_DEVICE}; @@ -27,7 +29,10 @@ mod syscall; mod task; mod timer; mod trap; - +//mod start; +mod riscvregs; +use riscvregs::registers::*; +use riscvregs::registers::pmpcfg0::*; //use syscall::create_desktop; //for test core::arch::global_asm!(include_str!("entry.asm")); @@ -51,6 +56,85 @@ lazy_static! { unsafe { UPIntrFreeCell::new(false) }; } + +#[repr(C, align(16))] +struct Stack([u8; 4096 * 4 * 1]); + +#[no_mangle] +static mut STACK0: Stack = Stack([0; 4096 * 4 * 1]); + +#[no_mangle] +pub unsafe fn rust_start() -> ! { + // set MPP mode to Supervisor, for mret + mstatus::set_mpp(mstatus::MPP::Supervisor); + + // set MEPC to main, for mret + mepc::write(rust_main as usize); + + // disable paging for now. + satp::write(0); + + // delegate all interrupts and exceptions to supervisor mode. + medeleg::set_all(); + mideleg::set_all(); + sie::set_sext(); + sie::set_ssoft(); + sie::set_stimer(); + + // configure Physical Memory Protection to give supervisor mode + // access to all of physical memory. + pmpaddr0::write(0x3fffffffffffff); + pmpcfg0::set_pmp(0, Range::TOR, Permission::RWX, false); // 0 < addr < pmpaddr0 + + // ask for clock interrupts. + timerinit(); + + // keep each CPU's hartid in its tp register, for cpuid(). + let id = mhartid::read(); + core::arch::asm!("mv tp, {0}", in(reg) id); + + // switch to supervisor mode and jump to main(). + core::arch::asm!("mret"); + + extern "C" { + fn rust_main() -> !; + } + core::hint::unreachable_unchecked(); +} + +// a scratch area per CPU for machine-mode timer interrupts. +static mut TIMER_SCRATCH: [[u64; 5]; 1] = [[0; 5]; 1]; + +unsafe fn timerinit() { + // each CPU has a separate source of timer interrupts + let id = mhartid::read(); + + // ask the CLINT for a timer interrupts + let interval = 1000000u64; // cycles; about 1/10th second in qemu. + let mtimecmp = board::clint_mtimecmp(id) as *mut u64; + let mtime = board::CLINT_MTIME as *const u64; + mtimecmp.write_volatile(mtime.read_volatile() + interval); + + // prepare information in scratch[] for timervec. + // scratch[0..2] : space for timervec to save registers. + // scratch[3] : address of CLINT MTIMECMP register. + // scratch[4] : desired interval (in cycles) between timer interrupts. + let scratch = &mut TIMER_SCRATCH[id]; + scratch[3] = mtimecmp as u64; + scratch[4] = interval; + mscratch::write(scratch.as_mut_ptr() as usize); + + // set the machine-mode trap handler + mtvec::write(board::timervec as usize, mtvec::TrapMode::Direct); + + // enable machine-mode interrupts. + mstatus::set_mie(); + + // enable machime-mode timer interrupts. + mie::set_mtimer(); +} + + #[no_mangle] pub fn rust_main() -> ! { clear_bss(); @@ -63,11 +147,11 @@ pub fn rust_main() -> ! { let _mouse = MOUSE_DEVICE.clone(); println!("KERN: init trap"); trap::init(); - trap::enable_timer_interrupt(); - timer::set_next_trigger(); + //trap::enable_timer_interrupt(); + //timer::set_next_trigger(); board::device_init(); fs::list_apps(); - gui::init_paint(); + //gui::init_paint(); task::add_initproc(); *DEV_NON_BLOCKING_ACCESS.exclusive_access() = true; task::run_tasks(); diff --git a/os/src/riscvregs.rs b/os/src/riscvregs.rs new file mode 100644 index 0000000..1871242 --- /dev/null +++ b/os/src/riscvregs.rs @@ -0,0 +1,614 @@ +// RISC-V registers +pub mod registers { + // hart (core) id registers + pub mod mhartid { + use core::arch::asm; + + #[inline] + pub fn read() -> usize { + let id: usize; + unsafe { + asm!("csrr {}, mhartid", out(reg) id); + } + id + } + } + + // Machine Status Register, mstatus + pub mod mstatus { + use core::arch::asm; + + // Machine Status Register bit + const MPP_MASK: usize = 3 << 11; + const MIE: usize = 1 << 3; + + // Machine Previous Privilege mode + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub enum MPP { + Machine = 3, + Supervisor = 1, + User = 0, + } + + #[inline] + unsafe fn _read() -> usize { + let bits: usize; + asm!("csrr {}, mstatus", out(reg) bits); + bits + } + + #[inline] + unsafe fn _write(bits: usize) { + asm!("csrw mstatus, {}", in(reg) bits); + } + + // Machine Previous Privilege Mode + #[inline] + pub fn set_mpp(mpp: MPP) { + unsafe { + let mut value = _read(); + value &= !MPP_MASK; + value |= (mpp as usize) << 11; + _write(value); + } + } + + #[inline] + pub fn set_mie() { + unsafe { + asm!("csrs mstatus, {}", in(reg) MIE); + } + } + } + + // machine exception program counter, holds the + // instruction address to which a return from + // exception will go. + pub mod mepc { + use core::arch::asm; + + #[inline] + pub fn write(x: usize) { + unsafe { + asm!("csrw mepc, {}", in(reg) x); + } + } + } + + // Supervisor Status Register, sstatus + pub mod sstatus { + use core::arch::asm; + + // Supervisor Status Register bit + const SPP: usize = 1 << 8; // Previous mode, 1=Supervisor, 0=user + const SPIE: usize = 1 << 5; // Supervisor Previous Interrupt Enable + const SIE: usize = 1 << 1; // Supervisor Interrupt Enable + + #[derive(Clone, Copy, Debug)] + pub struct Sstatus { + bits: usize, + } + + impl Sstatus { + // Supervisor Interrupt Enable + #[inline] + pub(in crate::riscvregs) fn sie(&self) -> bool { + self.bits & SIE != 0 + } + + // Supervisor Previous Privilege mode + #[inline] + pub fn spp(&self) -> SPP { + match self.bits & SPP { + 0 => SPP::User, + _ => SPP::Supervisor, + } + } + + // restore status bits + #[inline] + pub fn restore(&self) { + unsafe { + _write(self.bits); + } + } + } + + // Supervisor Previous Privilege Mode + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub enum SPP { + Supervisor = 1, + User = 0, + } + + #[inline] + pub fn read() -> Sstatus { + let bits: usize; + unsafe { asm!("csrr {}, sstatus", out(reg) bits) } + Sstatus { bits } + } + + #[inline] + unsafe fn _write(bits: usize) { + asm!("csrw sstatus, {}", in(reg) bits); + } + + // bit set + #[inline] + unsafe fn _set(bits: usize) { + asm!("csrs sstatus, {}", in(reg) bits); + } + + // bit clear + #[inline] + unsafe fn _clear(bits: usize) { + asm!("csrc sstatus, {}", in(reg) bits); + } + + #[inline] + pub(in crate::riscvregs) unsafe fn set_sie() { + _set(SIE) + } + + #[inline] + pub(in crate::riscvregs) unsafe fn clear_sie() { + _clear(SIE) + } + + #[inline] + pub unsafe fn set_spie() { + _set(SPIE); + } + + #[inline] + pub unsafe fn set_spp(spp: SPP) { + match spp { + SPP::Supervisor => _set(SPP), + SPP::User => _clear(SPP), + } + } + } + + // Supervisor Interrupt Pending + pub mod sip { + use core::arch::asm; + + const SSIP: usize = 1 << 1; + + // Supervisor Software Interrupt Pending + #[inline] + pub unsafe fn clear_ssoft() { + asm!("csrc sip, {}", in(reg) SSIP); + } + } + + // Supervisor Interrupt Enable + pub mod sie { + use core::arch::asm; + + const SEIE: usize = 1 << 9; // external + const STIE: usize = 1 << 5; // timer + const SSIE: usize = 1 << 1; // software + + #[inline] + unsafe fn _set(bits: usize) { + asm!("csrs sie, {}", in(reg) bits); + } + + #[inline] + pub unsafe fn set_sext() { + _set(SEIE); + } + + #[inline] + pub unsafe fn set_stimer() { + _set(STIE); + } + + #[inline] + pub unsafe fn set_ssoft() { + _set(SSIE); + } + } + + // Machine-mode Interrupt Enable + pub mod mie { + use core::arch::asm; + + const MTIE: usize = 1 << 7; + + #[inline] + pub unsafe fn set_mtimer() { + asm!("csrs mie, {}", in(reg) MTIE); + } + } + + // supervisor exceptions program counter, holds the + // instruction address to which a return from + // exception will go. + pub mod sepc { + use core::arch::asm; + + #[inline] + pub fn read() -> usize { + let bits: usize; + unsafe { + asm!("csrr {}, sepc", out(reg) bits); + } + bits + } + + #[inline] + pub fn write(bits: usize) { + unsafe { + asm!("csrw sepc, {}", in(reg) bits); + } + } + } + + // Machine Exception Delegation + pub mod medeleg { + use core::arch::asm; + + pub unsafe fn set_all() { + asm!("csrw medeleg, {}", in(reg) 0xffff); + } + } + + // Machine Interrupt Delegation + pub mod mideleg { + use core::arch::asm; + + #[inline] + pub unsafe fn set_all() { + asm!("csrw mideleg, {}", in(reg) 0xffff); + } + } + + // Supervisor Trap-Vector Base Address + // low two bits are mode. + pub mod stvec { + pub use super::mtvec::TrapMode; + use core::arch::asm; + + #[inline] + pub unsafe fn write(addr: usize, mode: TrapMode) { + asm!("csrw stvec, {}", in(reg) addr + mode as usize); + } + } + + // Machine-mode interrupt vector + pub mod mtvec { + use core::arch::asm; + + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub enum TrapMode { + Direct = 0, + Vectored = 1, + } + + #[inline] + pub unsafe fn write(addr: usize, mode: TrapMode) { + asm!("csrw mtvec, {}", in(reg) addr + mode as usize); + } + } + + // Physical Memory Protection Configuration + pub mod pmpcfg0 { + use core::arch::asm; + + // Permission enum contains all possible permission modes for pmp registers + #[derive(Clone, Copy, Debug)] + pub enum Permission { + NONE = 0b000, + R = 0b001, + W = 0b010, + RW = 0b011, + X = 0b100, + RX = 0b101, + WX = 0b110, + RWX = 0b111, + } + + // Range enum contains all possible addressing modes for pmp registers + pub enum Range { + OFF = 0b00, + TOR = 0b01, + NA4 = 0b10, + NAPOT = 0b11, + } + + // Set the pmp configuration corresponging to the index + #[inline] + pub unsafe fn set_pmp(index: usize, range: Range, permission: Permission, locked: bool) { + assert!(index < 8); + let mut value = _read(); + let byte = (locked as usize) << 7 | (range as usize) << 3 | (permission as usize); + value |= byte << (8 * index); + _write(value); + } + + #[inline] + unsafe fn _read() -> usize { + let bits: usize; + asm!("csrr {}, pmpcfg0", out(reg) bits); + bits + } + + #[inline] + unsafe fn _write(bits: usize) { + asm!("csrw pmpcfg0, {}", in(reg) bits); + } + } + + // Physical memory protection address register + pub mod pmpaddr0 { + use core::arch::asm; + + pub fn write(bits: usize) { + unsafe { + asm!("csrw pmpaddr0, {}", in(reg) bits); + } + } + } + + // Supervisor address translation and protection; + // holds the address of the page table. + pub mod satp { + use core::arch::asm; + + // stap register + #[derive(Clone, Copy, Debug)] + pub struct Satp { + bits: usize, + } + + // 64-bit satp mode + pub enum Mode { + // No translation or protection + Bare = 0, + // Page-based 39-bit virtual addressing + Sv39 = 8, + // Page-based 48-bit virtual addressing + Sv48 = 9, + // Page-based 57-bit virtual addressing + Sv57 = 10, + // Page-based 64-bit virtual addressing + Sv64 = 11, + } + + impl Satp { + // Return the contents of the register as raw bits + #[inline] + pub fn bits(&self) -> usize { + self.bits + } + } + + #[inline] + pub unsafe fn read() -> Satp { + let bits: usize; + asm!("csrr {}, satp", out(reg) bits); + Satp { bits } + } + + #[inline] + pub unsafe fn write(bits: usize) { + asm!("csrw satp, {}", in(reg) bits); + } + + #[inline] + pub fn make(mode: Mode, asid: usize, ppn: usize) -> usize { + let mut bits: usize = 0; + bits |= (mode as usize) << 60; + bits |= asid << 44; + bits |= ppn >> 12; + bits + } + } + + // mscratch register + pub mod mscratch { + use core::arch::asm; + + #[inline] + pub fn write(bits: usize) { + unsafe { + asm!("csrw mscratch, {}", in(reg) bits); + } + } + } + + // Supervisor Trap Cause + pub mod scause { + use core::{arch::asm, mem::size_of}; + + // scause register + #[derive(Clone, Copy)] + pub struct Scause { + bits: usize, + } + + // Trap Cause + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub enum Trap { + Interrupt(Interrupt), + Exception(Exception), + } + + // Interrupt + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub enum Interrupt { + UserSoft, + SupervisorSoft, + UserTimer, + SupervisorTimer, + UserExternal, + SupervisorExternal, + Unknown, + } + + // Exception + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + pub enum Exception { + InstructionMisaligned, + InstructionFault, + IllegalInstruction, + Breakpoint, + LoadFault, + StoreMisaligned, + StoreFault, + UserEnvCall, + InstructionPageFault, + LoadPageFault, + StorePageFault, + Unknown, + } + + impl Interrupt { + #[inline] + pub fn from(nr: usize) -> Self { + match nr { + 0 => Interrupt::UserSoft, + 1 => Interrupt::SupervisorSoft, + 4 => Interrupt::UserTimer, + 5 => Interrupt::SupervisorTimer, + 8 => Interrupt::UserExternal, + 9 => Interrupt::SupervisorExternal, + _ => Interrupt::Unknown, + } + } + } + + impl Exception { + #[inline] + pub fn from(nr: usize) -> Self { + match nr { + 0 => Exception::InstructionMisaligned, + 1 => Exception::InstructionFault, + 2 => Exception::IllegalInstruction, + 3 => Exception::Breakpoint, + 5 => Exception::LoadFault, + 6 => Exception::StoreMisaligned, + 7 => Exception::StoreFault, + 8 => Exception::UserEnvCall, + 12 => Exception::InstructionPageFault, + 13 => Exception::LoadPageFault, + 15 => Exception::StorePageFault, + _ => Exception::Unknown, + } + } + } + + impl Scause { + // Returns the contents of the register as raw bits + #[inline] + pub fn bits(&self) -> usize { + self.bits + } + + // Returns the code field + #[inline] + pub fn code(&self) -> usize { + let bit = 1 << (size_of::() * 8 - 1); + self.bits & !bit + } + + // Trap cause + #[inline] + pub fn cause(&self) -> Trap { + if self.is_interrupt() { + Trap::Interrupt(Interrupt::from(self.code())) + } else { + Trap::Exception(Exception::from(self.code())) + } + } + + // Is trap cause an interrupt. + #[inline] + pub fn is_interrupt(&self) -> bool { + self.bits & (1 << (size_of::() * 8 - 1)) != 0 + } + + // Is trap cause an exception. + #[inline] + pub fn is_exception(&self) -> bool { + !self.is_interrupt() + } + } + + #[inline] + pub fn read() -> Scause { + let bits: usize; + unsafe { + asm!("csrr {}, scause", out(reg) bits); + } + Scause { bits } + } + } + + // Supervisor Trap Value + pub mod stval { + use core::arch::asm; + + #[inline] + pub fn read() -> usize { + let bits: usize; + unsafe { asm!("csrr {}, stval", out(reg) bits) } + bits + } + } +} + +use core::arch::asm; + +use registers::*; + +// enable device interrupts +#[inline] +pub fn intr_on() { + unsafe { + sstatus::set_sie(); + } +} + +// disable device interrupts +#[inline] +pub fn intr_off() { + unsafe { + sstatus::clear_sie(); + } +} + +// are device interrupts enabled? +#[inline] +pub fn intr_get() -> bool { + sstatus::read().sie() +} + +// flush the TLB. +#[inline] +pub unsafe fn sfence_vma() { + // the zero, zero means flush all TLB entries + asm!("sfence.vma zero, zero"); +} + +pub const PGSIZE: usize = 4096; // bytes per page +pub const PGSHIFT: usize = 12; // bits of offset within a page + +pub const fn pgroundup(sz: usize) -> usize { + (sz + PGSIZE - 1) & !(PGSIZE - 1) +} + +pub const fn pgrounddown(sz: usize) -> usize { + sz & !(PGSIZE - 1) +} + +// PTE flags +pub mod pteflags { + pub const PTE_V: usize = 1 << 0; // valid + pub const PTE_R: usize = 1 << 1; + pub const PTE_W: usize = 1 << 2; + pub const PTE_X: usize = 1 << 3; + pub const PTE_U: usize = 1 << 4; // user can access +} diff --git a/os/src/start.rs b/os/src/start.rs new file mode 100644 index 0000000..3a6d391 --- /dev/null +++ b/os/src/start.rs @@ -0,0 +1,86 @@ +//use crate::kernelvec::*; +//use crate::memlayout::*; +//use crate::param::NCPU; +//use super::main::*; +//use crate::riscv::registers::{pmpcfg0::*, *}; +use core::arch::asm; +use core::hint::unreachable_unchecked; + +mod riscv; + +#[repr(C, align(16))] +struct Stack([u8; 4096 * 4 * NCPU]); + +#[no_mangle] +static mut STACK0: Stack = Stack([0; 4096 * 4 * NCPU]); + +#[no_mangle] +pub unsafe fn rust_start() -> ! { + // set MPP mode to Supervisor, for mret + mstatus::set_mpp(mstatus::MPP::Supervisor); + + // set MEPC to main, for mret + mepc::write(rust_main as usize); + + // disable paging for now. + satp::write(0); + + // delegate all interrupts and exceptions to supervisor mode. + medeleg::set_all(); + mideleg::set_all(); + sie::set_sext(); + sie::set_ssoft(); + sie::set_stimer(); + + // configure Physical Memory Protection to give supervisor mode + // access to all of physical memory. + pmpaddr0::write(0x3fffffffffffff); + pmpcfg0::set_pmp(0, Range::TOR, Permission::RWX, false); // 0 < addr < pmpaddr0 + + // ask for clock interrupts. + timerinit(); + + // keep each CPU's hartid in its tp register, for cpuid(). + let id = mhartid::read(); + asm!("mv tp, {0}", in(reg) id); + + // switch to supervisor mode and jump to main(). + asm!("mret"); + + extern "C" { + fn rust_main() -> !; + } + unreachable_unchecked(); +} + +// a scratch area per CPU for machine-mode timer interrupts. +static mut TIMER_SCRATCH: [[u64; 5]; 1] = [[0; 5]; 1]; + +unsafe fn timerinit() { + // each CPU has a separate source of timer interrupts + let id = mhartid::read(); + + // ask the CLINT for a timer interrupts + let interval = 1000000u64; // cycles; about 1/10th second in qemu. + let mtimecmp = clint_mtimecmp(id) as *mut u64; + let mtime = CLINT_MTIME as *const u64; + mtimecmp.write_volatile(mtime.read_volatile() + interval); + + // prepare information in scratch[] for timervec. + // scratch[0..2] : space for timervec to save registers. + // scratch[3] : address of CLINT MTIMECMP register. + // scratch[4] : desired interval (in cycles) between timer interrupts. + let scratch = &mut TIMER_SCRATCH[id]; + scratch[3] = mtimecmp as u64; + scratch[4] = interval; + mscratch::write(scratch.as_mut_ptr() as usize); + + // set the machine-mode trap handler + mtvec::write(timervec as usize, mtvec::TrapMode::Direct); + + // enable machine-mode interrupts. + mstatus::set_mie(); + + // enable machime-mode timer interrupts. + mie::set_mtimer(); +} \ No newline at end of file