|
1 | 1 | //! Module for interrupt support.
|
2 | 2 |
|
| 3 | +use core::arch::global_asm; |
3 | 4 | use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
|
4 | 5 | use doors_alloc::boxed::Box;
|
5 | 6 | use x86_64::instructions::interrupts;
|
6 | 7 | use x86_64::registers::control::Cr2;
|
7 | 8 | use x86_64::structures::idt::{self, InterruptDescriptorTable, InterruptStackFrame};
|
| 9 | +use x86_64::VirtAddr; |
8 | 10 |
|
9 | 11 | // We initialize the counter with 1 since the bootloader disables interrupts when
|
10 | 12 | // control is passed to the kernel.
|
@@ -73,130 +75,207 @@ pub fn end_interrupt_disable() {
|
73 | 75 | interrupts::enable();
|
74 | 76 | }
|
75 | 77 |
|
76 |
| -macro_rules! exception { |
77 |
| - ($id:ident, $name:literal) => { |
78 |
| - extern "x86-interrupt" fn $id(frame: InterruptStackFrame) { |
79 |
| - cpu_exception(frame, $name); |
80 |
| - } |
81 |
| - }; |
82 |
| -} |
83 |
| - |
84 |
| -macro_rules! exception_errcode { |
85 |
| - ($id:ident, $name:literal) => { |
86 |
| - extern "x86-interrupt" fn $id(frame: InterruptStackFrame, errcode: u64) { |
87 |
| - cpu_exception_errcode(frame, $name, errcode); |
88 |
| - } |
89 |
| - }; |
| 78 | +fn exception_id_to_name(id: u64) -> &'static str { |
| 79 | + match id { |
| 80 | + 0 => "Divide error", |
| 81 | + 1 => "Debug exception", |
| 82 | + 2 => "Non-maskable interrupt", |
| 83 | + 3 => "Breakpoint exception", |
| 84 | + 4 => "Overflow exception", |
| 85 | + 5 => "BOUND range exceeded", |
| 86 | + 6 => "Invalid opcode", |
| 87 | + 7 => "Device not available", |
| 88 | + 8 => "Double fault", |
| 89 | + 10 => "Invalid TSS", |
| 90 | + 11 => "Segment not present", |
| 91 | + 12 => "Stack segment exception", |
| 92 | + 13 => "General protection fault", |
| 93 | + 14 => "Page fault", |
| 94 | + 16 => "x87 floating-point exception", |
| 95 | + 17 => "Alignment check exception", |
| 96 | + 18 => "Machine check exception", |
| 97 | + 19 => "SIMD floating-point exception", |
| 98 | + 20 => "Virtualization exception", |
| 99 | + 29 => "VMM communication exception", |
| 100 | + 30 => "Security exception", |
| 101 | + _ => "Unknown exception", |
| 102 | + } |
90 | 103 | }
|
91 | 104 |
|
92 |
| -macro_rules! exception_diverging { |
93 |
| - ($id:ident, $name:literal) => { |
94 |
| - extern "x86-interrupt" fn $id(frame: InterruptStackFrame) -> ! { |
95 |
| - cpu_exception(frame, $name); |
| 105 | +macro_rules! exception { |
| 106 | + ($id:ident, $number:literal) => { |
| 107 | + global_asm!( |
| 108 | + concat!(stringify!($id), ":"), |
| 109 | + "push rax", |
| 110 | + "push rcx", |
| 111 | + "push rdx", |
| 112 | + "push rsi", |
| 113 | + "push rdi", |
| 114 | + "push r8", |
| 115 | + "push r9", |
| 116 | + "push r10", |
| 117 | + "push r11", |
| 118 | + "lea rdi, [rsp + 72]", // stack frame |
| 119 | + concat!("mov rsi, ", $number), // exception id |
| 120 | + "call {}", |
| 121 | + "pop r11", |
| 122 | + "pop r10", |
| 123 | + "pop r9", |
| 124 | + "pop r8", |
| 125 | + "pop rdi", |
| 126 | + "pop rsi", |
| 127 | + "pop rdx", |
| 128 | + "pop rcx", |
| 129 | + "pop rax", |
| 130 | + "iretq", |
| 131 | + sym cpu_exception |
| 132 | + ); |
| 133 | + extern "C" { |
| 134 | + fn $id(); |
96 | 135 | }
|
97 |
| - }; |
| 136 | + } |
98 | 137 | }
|
99 | 138 |
|
100 |
| -macro_rules! exception_diverging_errcode { |
101 |
| - ($id:ident, $name:literal) => { |
102 |
| - extern "x86-interrupt" fn $id(frame: InterruptStackFrame, errcode: u64) -> ! { |
103 |
| - cpu_exception_errcode(frame, $name, errcode); |
| 139 | +macro_rules! exception_errcode { |
| 140 | + ($id:ident, $number:literal) => { |
| 141 | + global_asm!( |
| 142 | + concat!(stringify!($id), ":"), |
| 143 | + "push rax", |
| 144 | + "push rcx", |
| 145 | + "push rdx", |
| 146 | + "push rsi", |
| 147 | + "push rdi", |
| 148 | + "push r8", |
| 149 | + "push r9", |
| 150 | + "push r10", |
| 151 | + "push r11", |
| 152 | + "lea rdi, [rsp + 80]", // stack frame |
| 153 | + concat!("mov rsi, ", $number), // exception id |
| 154 | + "mov rdx, [rsp + 72]", // error code |
| 155 | + "push 0", // 16 byte alignment |
| 156 | + "call {}", |
| 157 | + "pop rax", |
| 158 | + "pop r11", |
| 159 | + "pop r10", |
| 160 | + "pop r9", |
| 161 | + "pop r8", |
| 162 | + "pop rdi", |
| 163 | + "pop rsi", |
| 164 | + "pop rdx", |
| 165 | + "pop rcx", |
| 166 | + "pop rax", |
| 167 | + "add rsp, 8", // error code |
| 168 | + "iretq", |
| 169 | + sym cpu_exception_errcode |
| 170 | + ); |
| 171 | + extern "C" { |
| 172 | + fn $id(); |
104 | 173 | }
|
105 |
| - }; |
| 174 | + } |
106 | 175 | }
|
107 | 176 |
|
108 |
| -exception!(handler_divide_error, "Divide error"); |
109 |
| -exception!(handler_debug, "Debug exception"); |
110 |
| -exception!(handler_non_maskable_interrupt, "Non-maskable interrupt"); |
111 |
| -exception!(handler_breakpoint, "Breakpoint exception"); |
112 |
| -exception!(handler_overflow, "Overflow exception"); |
113 |
| -exception!(handler_bound_range_exceeded, "Bound range exceeded"); |
114 |
| -exception!(handler_invalid_opcode, "Invalid opcode"); |
115 |
| -exception!(handler_device_not_available, "Device not available"); |
116 |
| -exception_diverging_errcode!(handler_double_fault, "Double fault"); |
117 |
| -exception_errcode!(handler_invalid_tss, "Invalid TSS"); |
118 |
| -exception_errcode!(handler_segment_not_present, "Segment not present"); |
119 |
| -exception_errcode!(handler_stack_segment_fault, "Stack segment exception"); |
120 |
| -exception_errcode!(handler_general_protection_fault, "General protection fault"); |
121 |
| -// See below for page fault handler |
122 |
| -exception!(handler_x87_floating_point, "x87 floating-point exception"); |
123 |
| -exception_errcode!(handler_alignment_check, "Alignment check exception"); |
124 |
| -exception_diverging!(handler_machine_check, "Machine check exception"); |
125 |
| -exception!(handler_simd_floating_point, "SIMD floating-point exception"); |
126 |
| -exception!(handler_virtualization, "Virtualization exception"); |
127 |
| -exception_errcode!( |
128 |
| - handler_vmm_communication_exception, |
129 |
| - "VMM communication exception" |
130 |
| -); |
131 |
| -exception_errcode!(handler_security_exception, "Security exception"); |
| 177 | +exception!(handler_divide_error, 0); |
| 178 | +exception!(handler_debug, 1); |
| 179 | +exception!(handler_non_maskable_interrupt, 2); |
| 180 | +exception!(handler_breakpoint, 3); |
| 181 | +exception!(handler_overflow, 4); |
| 182 | +exception!(handler_bound_range_exceeded, 5); |
| 183 | +exception!(handler_invalid_opcode, 6); |
| 184 | +exception!(handler_device_not_available, 7); |
| 185 | +exception_errcode!(handler_double_fault, 8); |
| 186 | +exception_errcode!(handler_invalid_tss, 10); |
| 187 | +exception_errcode!(handler_segment_not_present, 11); |
| 188 | +exception_errcode!(handler_stack_segment_fault, 12); |
| 189 | +exception_errcode!(handler_general_protection_fault, 13); |
| 190 | +exception_errcode!(handler_page_fault, 14); |
| 191 | +exception!(handler_x87_floating_point, 16); |
| 192 | +exception_errcode!(handler_alignment_check, 17); |
| 193 | +exception!(handler_machine_check, 18); |
| 194 | +exception!(handler_simd_floating_point, 19); |
| 195 | +exception!(handler_virtualization, 20); |
| 196 | +exception_errcode!(handler_vmm_communication_exception, 29); |
| 197 | +exception_errcode!(handler_security_exception, 30); |
132 | 198 |
|
133 |
| -fn cpu_exception(frame: InterruptStackFrame, name: &'static str) -> ! { |
134 |
| - IN_KERNEL_INTERRUPT_HANDLER.store(true, Ordering::Relaxed); |
135 |
| - IN_KERNEL_EXCEPTION_HANDLER.store(true, Ordering::Relaxed); |
136 |
| - panic!("CPU exception: {}, stack frame: {:?}", name, frame); |
137 |
| -} |
138 |
| - |
139 |
| -fn cpu_exception_errcode(frame: InterruptStackFrame, name: &'static str, errcode: u64) -> ! { |
| 199 | +extern "C" fn cpu_exception(frame: &InterruptStackFrame, id: u64) { |
140 | 200 | IN_KERNEL_INTERRUPT_HANDLER.store(true, Ordering::Relaxed);
|
141 | 201 | IN_KERNEL_EXCEPTION_HANDLER.store(true, Ordering::Relaxed);
|
142 | 202 | panic!(
|
143 |
| - "CPU exception: {}, error code: {}, stack frame: {:?}", |
144 |
| - name, errcode, frame |
| 203 | + "CPU exception: {}, stack frame: {:?}", |
| 204 | + exception_id_to_name(id), |
| 205 | + frame |
145 | 206 | );
|
146 | 207 | }
|
147 | 208 |
|
148 |
| -extern "x86-interrupt" fn handler_page_fault( |
149 |
| - frame: InterruptStackFrame, |
150 |
| - errcode: idt::PageFaultErrorCode, |
151 |
| -) { |
| 209 | +extern "C" fn cpu_exception_errcode(frame: &InterruptStackFrame, id: u64, errcode: u64) { |
152 | 210 | IN_KERNEL_INTERRUPT_HANDLER.store(true, Ordering::Relaxed);
|
153 | 211 | IN_KERNEL_EXCEPTION_HANDLER.store(true, Ordering::Relaxed);
|
| 212 | + if id == 14 { |
| 213 | + panic!( |
| 214 | + "CPU exception: Page fault, stack frame: {:?}, error code: {:?}, address: {:?}", |
| 215 | + frame, |
| 216 | + idt::PageFaultErrorCode::from_bits_truncate(errcode), |
| 217 | + Cr2::read() |
| 218 | + ); |
| 219 | + } |
154 | 220 | panic!(
|
155 |
| - "CPU exception: Page fault, error code: {:?}, address: {:?}, stack frame: {:?}", |
156 |
| - errcode, |
157 |
| - Cr2::read(), |
158 |
| - frame |
| 221 | + "CPU exception: {}, stack frame: {:?}, error code: {}", |
| 222 | + exception_id_to_name(id), |
| 223 | + frame, |
| 224 | + errcode |
159 | 225 | );
|
160 | 226 | }
|
161 | 227 |
|
162 | 228 | /// Initializes interrupt handling.
|
163 | 229 | ///
|
164 | 230 | /// This requires heap allocation to be initialized first.
|
| 231 | +#[allow(clippy::fn_to_numeric_cast)] |
165 | 232 | pub fn init() {
|
166 | 233 | let idt = Box::leak(
|
167 | 234 | Box::try_new(InterruptDescriptorTable::new()).expect("Allocation should succeed"),
|
168 | 235 | );
|
169 |
| - idt.divide_error.set_handler_fn(handler_divide_error); |
170 |
| - idt.debug.set_handler_fn(handler_debug); |
171 |
| - idt.non_maskable_interrupt |
172 |
| - .set_handler_fn(handler_non_maskable_interrupt); |
173 |
| - idt.breakpoint.set_handler_fn(handler_breakpoint); |
174 |
| - idt.overflow.set_handler_fn(handler_overflow); |
175 |
| - idt.bound_range_exceeded |
176 |
| - .set_handler_fn(handler_bound_range_exceeded); |
177 |
| - idt.invalid_opcode.set_handler_fn(handler_invalid_opcode); |
178 |
| - idt.device_not_available |
179 |
| - .set_handler_fn(handler_device_not_available); |
180 |
| - idt.double_fault.set_handler_fn(handler_double_fault); |
181 |
| - idt.invalid_tss.set_handler_fn(handler_invalid_tss); |
182 |
| - idt.segment_not_present |
183 |
| - .set_handler_fn(handler_segment_not_present); |
184 |
| - idt.stack_segment_fault |
185 |
| - .set_handler_fn(handler_stack_segment_fault); |
186 |
| - idt.general_protection_fault |
187 |
| - .set_handler_fn(handler_general_protection_fault); |
188 |
| - idt.page_fault.set_handler_fn(handler_page_fault); |
189 |
| - idt.x87_floating_point |
190 |
| - .set_handler_fn(handler_x87_floating_point); |
191 |
| - idt.alignment_check.set_handler_fn(handler_alignment_check); |
192 |
| - idt.machine_check.set_handler_fn(handler_machine_check); |
193 |
| - idt.simd_floating_point |
194 |
| - .set_handler_fn(handler_simd_floating_point); |
195 |
| - idt.virtualization.set_handler_fn(handler_virtualization); |
196 |
| - idt.vmm_communication_exception |
197 |
| - .set_handler_fn(handler_vmm_communication_exception); |
198 |
| - idt.security_exception |
199 |
| - .set_handler_fn(handler_security_exception); |
200 |
| - |
| 236 | + unsafe { |
| 237 | + idt.divide_error |
| 238 | + .set_handler_addr(VirtAddr::new(handler_divide_error as u64)); |
| 239 | + idt.debug |
| 240 | + .set_handler_addr(VirtAddr::new(handler_debug as u64)); |
| 241 | + idt.non_maskable_interrupt |
| 242 | + .set_handler_addr(VirtAddr::new(handler_non_maskable_interrupt as u64)); |
| 243 | + idt.breakpoint |
| 244 | + .set_handler_addr(VirtAddr::new(handler_breakpoint as u64)); |
| 245 | + idt.overflow |
| 246 | + .set_handler_addr(VirtAddr::new(handler_overflow as u64)); |
| 247 | + idt.bound_range_exceeded |
| 248 | + .set_handler_addr(VirtAddr::new(handler_bound_range_exceeded as u64)); |
| 249 | + idt.invalid_opcode |
| 250 | + .set_handler_addr(VirtAddr::new(handler_invalid_opcode as u64)); |
| 251 | + idt.device_not_available |
| 252 | + .set_handler_addr(VirtAddr::new(handler_device_not_available as u64)); |
| 253 | + idt.double_fault |
| 254 | + .set_handler_addr(VirtAddr::new(handler_double_fault as u64)); |
| 255 | + idt.invalid_tss |
| 256 | + .set_handler_addr(VirtAddr::new(handler_invalid_tss as u64)); |
| 257 | + idt.segment_not_present |
| 258 | + .set_handler_addr(VirtAddr::new(handler_segment_not_present as u64)); |
| 259 | + idt.stack_segment_fault |
| 260 | + .set_handler_addr(VirtAddr::new(handler_stack_segment_fault as u64)); |
| 261 | + idt.general_protection_fault |
| 262 | + .set_handler_addr(VirtAddr::new(handler_general_protection_fault as u64)); |
| 263 | + idt.page_fault |
| 264 | + .set_handler_addr(VirtAddr::new(handler_page_fault as u64)); |
| 265 | + idt.x87_floating_point |
| 266 | + .set_handler_addr(VirtAddr::new(handler_x87_floating_point as u64)); |
| 267 | + idt.alignment_check |
| 268 | + .set_handler_addr(VirtAddr::new(handler_alignment_check as u64)); |
| 269 | + idt.machine_check |
| 270 | + .set_handler_addr(VirtAddr::new(handler_machine_check as u64)); |
| 271 | + idt.simd_floating_point |
| 272 | + .set_handler_addr(VirtAddr::new(handler_simd_floating_point as u64)); |
| 273 | + idt.virtualization |
| 274 | + .set_handler_addr(VirtAddr::new(handler_virtualization as u64)); |
| 275 | + idt.vmm_communication_exception |
| 276 | + .set_handler_addr(VirtAddr::new(handler_vmm_communication_exception as u64)); |
| 277 | + idt.security_exception |
| 278 | + .set_handler_addr(VirtAddr::new(handler_security_exception as u64)); |
| 279 | + } |
201 | 280 | idt.load();
|
202 | 281 | }
|
0 commit comments