-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path\
52 lines (42 loc) · 1.64 KB
/
\
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
use crate::hal::arch::riscv::{asm, MIE_MTIE, MSTATUS_MIE};
use crate::NCPU;
use core::ptr::addr_of;
// Core Local Interrupter (CLINT), which contains the timer.
pub const CLINT: usize = 0x2000000;
const CLINT_MTIME: usize = CLINT + 0xbff8;
extern "C" {
pub fn timervec();
}
#[no_mangle]
pub static mut timer_scratch: [[u64; 5]; NCPU] = [[0u64; 5]; NCPU];
fn clint_mtimecmp(hartid: usize) -> *mut u64 {
(CLINT + 0x4000 + (8 * hartid)) as *mut u64
}
/// 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.
pub unsafe fn timerinit() {
// Each CPU has a separate source of timer interrupts.
let id = asm::r_mhartid() as usize;
// Ask the CLINT for a timer interrupt.
// cycles, about 1/10th second in qemu
let interval = 1_000_000u64;
*clint_mtimecmp(id) = *(CLINT_MTIME as *const u64) + 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 [u64; 5] = &mut timer_scratch[id];
scratch[3] = clint_mtimecmp(id) as usize as u64;
scratch[4] = interval;
asm::w_mscratch(addr_of!(scratch[0]) as usize as u64);
// Set the machine-mode trap handler.
asm::w_mtvec(timervec as usize as u64);
// Enable machine-mode interrupts.
asm::w_mstatus(asm::r_mstatus() | MSTATUS_MIE);
// Enable machine-mode timer interrupts.
asm::w_mie(asm::r_mie() | MIE_MTIE);
}