1use crate::thread::Thread;
4use riscv::{
5 interrupt::supervisor::{Exception, Interrupt},
6 register::{
7 scause::{self, Trap},
8 sie, time,
9 },
10};
11
12#[derive(Clone, Debug)]
14pub enum Case {
15 NotExist,
17 Begin,
19 Interval {
21 begin: u64,
23 end: u64,
25 },
26 ReadFailed,
28 TimeDecreased {
30 a: u64,
32 b: u64,
34 },
35 SetTimer,
37 UnexpectedTrap(Trap<usize, usize>),
39 Pass,
41}
42
43pub fn test(delay: u64, mut f: impl FnMut(Case)) {
45 if sbi::probe_extension(sbi::Timer).is_unavailable() {
46 f(Case::NotExist);
47 return;
48 }
49 f(Case::Begin);
50 let begin: usize;
51 let end: usize;
52 let mut ok = 0xffusize;
53 unsafe {
54 core::arch::asm!(
55 " la {stvec}, 2f
56 csrw stvec, {stvec}
57 csrr {begin}, time
58 csrr {end}, time
59 mv {ok}, zero
60 .align 2
61 2:
62 ",
63 stvec = out(reg) _,
64 begin = out(reg) begin,
65 end = out(reg) end,
66 ok = inlateout(reg) ok,
67 );
68 }
69 let begin: u64 = begin as u64;
71 let end: u64 = end as u64;
72 if ok != 0 {
73 f(Case::ReadFailed);
74 return;
75 }
76 if begin >= end {
77 f(Case::TimeDecreased { a: begin, b: end });
78 return;
79 }
80 f(Case::Interval { begin, end });
81
82 let mut stack = [0usize; 32];
83 let mut thread = Thread::new(riscv::asm::wfi as *const () as _);
84 *thread.sp_mut() = stack.as_mut_ptr_range().end as _;
85
86 sbi::set_timer(time::read64() + delay);
87 unsafe {
88 sie::set_stimer();
89 thread.execute();
90 }
91 let trap = scause::read().cause();
92 match trap.try_into::<Interrupt, Exception>() {
93 Ok(Trap::Interrupt(Interrupt::SupervisorTimer)) => {
94 sbi::set_timer(u64::MAX);
95 f(Case::SetTimer);
96 f(Case::Pass);
97 }
98 _ => {
99 f(Case::UnexpectedTrap(trap));
100 }
101 }
102}