1use crate::{serial_print, serial_println};
7use core::arch::asm;
8use spin::Mutex;
9
10#[derive(Debug, Clone, Copy, Default)]
12#[repr(C)]
13pub struct JumpBuffer {
14 rbx: u64,
15 rsp: u64,
16 rbp: u64,
17 r12: u64,
18 r13: u64,
19 r14: u64,
20 r15: u64,
21 rip: u64,
22}
23
24static TEST_JMP_BUF: Mutex<Option<JumpBuffer>> = Mutex::new(None);
25static FAIL_COUNT: Mutex<usize> = Mutex::new(0);
26
27#[unsafe(no_mangle)]
34pub unsafe extern "C" fn set_jmp() -> u64 {
35 let mut jmp_buf = JumpBuffer::default();
36 let res: u64;
37
38 asm!(
39 "mov [rcx + 0], rbx",
41 "mov [rcx + 8], rsp",
42 "mov [rcx + 16], rbp",
43 "mov [rcx + 24], r12",
44 "mov [rcx + 32], r13",
45 "mov [rcx + 40], r14",
46 "mov [rcx + 48], r15",
47 "lea rdx, [rip + 2f]",
49 "mov [rcx + 56], rdx",
50 "mov rax, 0",
52 "jmp 3f",
53 "2:",
55 "mov rax, 1",
57 "3:",
58 in("rcx") &mut jmp_buf,
59 out("rax") res,
60 out("rdx") _,
61 clobber_abi("sysv64")
62 );
63
64 if res == 0 {
65 *TEST_JMP_BUF.lock() = Some(jmp_buf);
66 }
67 res
68}
69
70pub fn long_jmp() -> ! {
72 let jmp_buf = TEST_JMP_BUF.lock().expect("No jump buffer set!");
73
74 let mut fail_count = FAIL_COUNT.lock();
77 *fail_count += 1;
78 drop(fail_count); unsafe {
81 asm!(
82 "mov rbx, [rcx + 0]",
84 "mov rsp, [rcx + 8]",
85 "mov rbp, [rcx + 16]",
86 "mov r12, [rcx + 24]",
87 "mov r13, [rcx + 32]",
88 "mov r14, [rcx + 40]",
89 "mov r15, [rcx + 48]",
90 "jmp [rcx + 56]",
92 in("rcx") &jmp_buf,
93 options(noreturn)
94 );
95 }
96}
97
98pub trait Testable {
100 fn run(&self) -> ();
102}
103
104impl<T> Testable for T
106where
107 T: Fn(),
108{
109 fn run(&self) {
110 serial_print!("Testing {}... ", core::any::type_name::<T>());
111
112 unsafe {
114 if set_jmp() == 0 {
115 self();
117 serial_println!("[OK]");
118 } else {
119 }
122 }
123 }
124}
125
126pub fn test_runner(tests: &[&dyn Testable]) {
128 serial_println!("Running {} tests", tests.len());
129
130 for test in tests {
131 test.run();
132 }
133
134 let final_fail_count = *FAIL_COUNT.lock();
135 serial_println!("Total failures: {}", final_fail_count);
136
137 if final_fail_count > 0 {
138 serial_println!("\n[DONE] FAILED: {} tests failed", final_fail_count);
139 exit_qemu(QemuExitCode::Failed);
140 } else {
141 serial_println!("\n[DONE] SUCCESS: All tests passed!");
142 exit_qemu(QemuExitCode::Success);
143 }
144}
145
146pub enum QemuExitCode {
148 Success = 0x10,
149 Failed = 0x11,
150}
151
152pub fn exit_qemu(exit_code: QemuExitCode) {
154 use x86_64::instructions::port::Port;
155
156 unsafe {
157 let mut port = Port::new(0xf4);
158 port.write(exit_code as u32);
159 }
160}
161
162#[cfg(test)]
164#[unsafe(no_mangle)]
165pub extern "C" fn kernel_main() -> ! {
166 crate::memory::init(); crate::drivers::init_devices(); crate::libs::time::init(); crate::libs::logger::init_logger(); crate::libs::initrd::load_initrd(); crate::interrupts::gdt::init(); crate::interrupts::idt::init_idt(); crate::interrupts::pic::init(); x86_64::instructions::interrupts::enable(); crate::test_main();
176 loop {
177 x86_64::instructions::hlt();
178 }
179}