use core::arch::asm; use core::usize; use crate::kmain::kmain; use crate::memlayout::{CLINT_MTIME, CLINT_MTIMECMP}; use crate::param::NCPU; use crate::riscv as rv; extern "C" { // assembly code in kernelvec.S for machine-mode timer interrupt. fn timervec(); } #[repr(align(16))] pub struct EntryStack([u8; 4096 * NCPU]); // entry.S needs one stack per CPU. #[no_mangle] pub static mut stack0: EntryStack = EntryStack([0; 4096 * NCPU]); // a scratch area per CPU for machine-mode timer interrupts. static mut timer_scratch: [[u64; 5]; NCPU] = [[0; 5]; NCPU]; // entry.S jumps here in machine mode on stack0. #[no_mangle] pub unsafe extern "C" fn start() { // set M Previous Privilege mode to Supervisor, for mret. let mut x = rv::r_mstatus(); x &= !rv::MSTATUS_MPP_MASK; x |= rv::MSTATUS_MPP_S; rv::w_mstatus(x); // set M Exception Program Counter to main, for mret. // requires gcc -mcmodel=medany rv::w_mepc(kmain as *const () as u64); // disable paging for now. rv::w_satp(0); // delegate all interrupts and exceptions to supervisor mode. rv::w_medeleg(0xffff); rv::w_mideleg(0xffff); rv::w_sie(rv::r_sie() | rv::SIE_SEIE | rv::SIE_STIE | rv::SIE_SSIE); // configure Physical Memory Protection to give supervisor mode // access to all of physical memory. rv::w_pmpaddr0(0x3fffffffffffff); rv::w_pmpcfg0(0xf); // ask for clock interrupts. timerinit(); // keep each CPU's hartid in its tp register, for cpuid(). let id = rv::r_mhartid(); rv::w_tp(id); // switch to supervisor mode and jump to main(). asm!("mret"); } // arrange to receive timer interrupts. // they will arrive in machine mode at // at timervec in kernelvec.S, // which turns them into software interrupts for // devintr() in trap.c. unsafe fn timerinit() { // each CPU has a separate source of timer interrupts. let id = rv::r_mhartid(); // ask the CLINT for a timer interrupt. let interval: u64 = 1000000; // cycles; about 1/10th second in qemu. *CLINT_MTIMECMP(id) = *CLINT_MTIME + 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. timer_scratch[id as usize][3] = CLINT_MTIMECMP(id) as u64; timer_scratch[id as usize][4] = interval; // w_mscratch((uint64)scratch); rv::w_mscratch(timer_scratch[id as usize].as_ptr() as u64); // set the machine-mode trap handler. rv::w_mtvec(timervec as *const () as u64); // enable machine-mode interrupts. rv::w_mstatus(rv::r_mstatus() | rv::MSTATUS_MIE); // enable machine-mode timer interrupts. rv::w_mie(rv::r_mie() | rv::MIE_MTIE); }