summaryrefslogtreecommitdiffstats
path: root/rsnix/src/interrupts.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rsnix/src/interrupts.rs')
-rw-r--r--rsnix/src/interrupts.rs139
1 files changed, 107 insertions, 32 deletions
diff --git a/rsnix/src/interrupts.rs b/rsnix/src/interrupts.rs
index c176276..6abd54d 100644
--- a/rsnix/src/interrupts.rs
+++ b/rsnix/src/interrupts.rs
@@ -22,14 +22,14 @@ struct IdtEntry {
22} 22}
23 23
24/// Fill one IDT entry at the given offset (in bytes). 24/// Fill one IDT entry at the given offset (in bytes).
25unsafe fn idt_set_entry(idt: *mut u8, vec: u8, handler: u64, selector: u16, flags: u8) { 25unsafe fn idt_set_entry(idt: *mut u8, vec: u8, handler: u64, selector: u16, flags: u8, ist: u8) {
26 unsafe { 26 unsafe {
27 let off = (vec as usize) * 16; 27 let off = (vec as usize) * 16;
28 let p = idt.add(off) as *mut IdtEntry; 28 let p = idt.add(off) as *mut IdtEntry;
29 p.write(IdtEntry { 29 p.write(IdtEntry {
30 offset_lo: handler as u16, 30 offset_lo: handler as u16,
31 selector, 31 selector,
32 ist: 0, 32 ist,
33 flags: flags | 0x80, // bit 7 = present 33 flags: flags | 0x80, // bit 7 = present
34 offset_mid: (handler >> 16) as u16, 34 offset_mid: (handler >> 16) as u16,
35 offset_hi: (handler >> 32) as u32, 35 offset_hi: (handler >> 32) as u32,
@@ -213,6 +213,19 @@ static KEYBUF: KeyBuf = KeyBuf::new();
213 213
214core::arch::global_asm!( 214core::arch::global_asm!(
215 // ISR stubs — push vector number + error code, jump to common. 215 // ISR stubs — push vector number + error code, jump to common.
216 // Exceptions that push their own error code (8, 14) skip the push-0.
217 ".globl isr_stub_08",
218 ".p2align 4",
219 "isr_stub_08:",
220 " push 8", // vector (CPU already pushed the error code)
221 " jmp isr_common",
222 "",
223 ".globl isr_stub_14",
224 ".p2align 4",
225 "isr_stub_14:",
226 " push 14",
227 " jmp isr_common",
228 "",
216 ".globl isr_stub_32", 229 ".globl isr_stub_32",
217 ".p2align 4", 230 ".p2align 4",
218 "isr_stub_32:", 231 "isr_stub_32:",
@@ -227,6 +240,15 @@ core::arch::global_asm!(
227 " push 33", 240 " push 33",
228 " jmp isr_common", 241 " jmp isr_common",
229 "", 242 "",
243 // Software yield (vector 0x60 — outside the PIC's 32-47 range, which
244 // is why 0x40 was rejected: 0x40 = 64 decimal = slave IRQ 8 slot).
245 ".globl isr_stub_60",
246 ".p2align 4",
247 "isr_stub_60:",
248 " push 0",
249 " push 0x60",
250 " jmp isr_common",
251 "",
230 // Catch-all for unexpected interrupts. 252 // Catch-all for unexpected interrupts.
231 ".globl isr_stub_spurious", 253 ".globl isr_stub_spurious",
232 ".p2align 4", 254 ".p2align 4",
@@ -235,7 +257,8 @@ core::arch::global_asm!(
235 " push 0xFF", 257 " push 0xFF",
236 " jmp isr_common", 258 " jmp isr_common",
237 "", 259 "",
238 // Common handler — save all registers, call into Rust, restore, iretq. 260 // Common handler — save all registers, call into Rust (which returns
261 // the next task's frame pointer in rax), switch rsp, restore, iretq.
239 "isr_common:", 262 "isr_common:",
240 " push rax", 263 " push rax",
241 " push rcx", 264 " push rcx",
@@ -255,6 +278,7 @@ core::arch::global_asm!(
255 "", 278 "",
256 " mov rcx, rsp", // first arg (rcx = MS x64 ABI) 279 " mov rcx, rsp", // first arg (rcx = MS x64 ABI)
257 " call isr_dispatch", 280 " call isr_dispatch",
281 " mov rsp, rax", // context switch: rax = next task's frame ptr
258 "", 282 "",
259 " pop r15", 283 " pop r15",
260 " pop r14", 284 " pop r14",
@@ -277,37 +301,49 @@ core::arch::global_asm!(
277 301
278/// Saved register frame (as pushed by `isr_common`). 302/// Saved register frame (as pushed by `isr_common`).
279#[repr(C)] 303#[repr(C)]
280struct IntFrame { 304pub(crate) struct IntFrame {
281 r15: u64, 305 pub(crate) r15: u64,
282 r14: u64, 306 pub(crate) r14: u64,
283 r13: u64, 307 pub(crate) r13: u64,
284 r12: u64, 308 pub(crate) r12: u64,
285 r11: u64, 309 pub(crate) r11: u64,
286 r10: u64, 310 pub(crate) r10: u64,
287 r9: u64, 311 pub(crate) r9: u64,
288 r8: u64, 312 pub(crate) r8: u64,
289 rdi: u64, 313 pub(crate) rdi: u64,
290 rsi: u64, 314 pub(crate) rsi: u64,
291 rbp: u64, 315 pub(crate) rbp: u64,
292 rbx: u64, 316 pub(crate) rbx: u64,
293 rdx: u64, 317 pub(crate) rdx: u64,
294 rcx: u64, 318 pub(crate) rcx: u64,
295 rax: u64, 319 pub(crate) rax: u64,
296 vector: u64, 320 pub(crate) vector: u64,
297 error: u64, 321 pub(crate) error: u64,
298 rip: u64, 322 pub(crate) rip: u64,
299 cs: u64, 323 pub(crate) cs: u64,
300 rflags: u64, 324 pub(crate) rflags: u64,
325 // QEMU's 64-bit interrupt entry ALWAYS pushes SS/RSP/RFLAGS/CS/RIP
326 // (40 bytes) and iretq always pops all five, so the tail must be
327 // 40 bytes — these two fields are the CPU-pushed RSP/SS. Synthetic
328 // task frames set them to the task's initial stack top and the
329 // kernel SS so the first iretq lands with a valid stack.
330 pub(crate) rsp: u64,
331 pub(crate) ss: u64,
301} 332}
302 333
303/// Called from the asm common handler — dispatches to the right Rust handler. 334/// Called from the asm common handler — dispatches to the right Rust
335/// handler and returns the frame pointer the asm should switch to (the
336/// next task's, or the same one when no switch happens).
304#[unsafe(no_mangle)] 337#[unsafe(no_mangle)]
305unsafe extern "C" fn isr_dispatch(frame: &IntFrame) { 338unsafe extern "C" fn isr_dispatch(frame: &IntFrame) -> usize {
306 unsafe { 339 let fp = frame as *const IntFrame as usize;
340 crate::sched::set_in_isr(true);
341 let next = unsafe {
307 match frame.vector { 342 match frame.vector {
308 32 => { 343 32 => {
309 TICKS.fetch_add(1, Ordering::Relaxed); 344 TICKS.fetch_add(1, Ordering::Relaxed);
310 eoi(0); 345 eoi(0);
346 crate::sched::on_tick(fp)
311 } 347 }
312 33 => { 348 33 => {
313 let byte: u8; 349 let byte: u8;
@@ -316,10 +352,24 @@ unsafe extern "C" fn isr_dispatch(frame: &IntFrame) {
316 // Push scancode to ring buffer (called with interrupts disabled). 352 // Push scancode to ring buffer (called with interrupts disabled).
317 KEYBUF.push(byte); 353 KEYBUF.push(byte);
318 eoi(1); 354 eoi(1);
355 fp
356 }
357 8 => {
358 crate::serial_println!("double fault — halting");
359 halt()
319 } 360 }
320 _ => {} 361 14 => {
362 let cr2: u64;
363 core::arch::asm!("mov {}, cr2", out(reg) cr2,
364 options(nostack, preserves_flags));
365 crate::sched::page_fault(cr2, fp)
366 }
367 0x60 => crate::sched::on_yield(fp),
368 _ => fp,
321 } 369 }
322 } 370 };
371 crate::sched::set_in_isr(false);
372 next
323} 373}
324 374
325// --------------------------------------------------------------------------- 375// ---------------------------------------------------------------------------
@@ -350,12 +400,25 @@ pub fn init() {
350 400
351 // Fill all 256 entries with the spurious handler. 401 // Fill all 256 entries with the spurious handler.
352 for vec in 0..=255u8 { 402 for vec in 0..=255u8 {
353 idt_set_entry(idt_ptr, vec, spurious, cs, 0x0Eu8); 403 idt_set_entry(idt_ptr, vec, spurious, cs, 0x0Eu8, 0);
354 } 404 }
355 405
356 // Override vectors for interrupt we actually handle. 406 // Override vectors for interrupt we actually handle.
357 idt_set_entry(idt_ptr, 32, isr_stub_32 as *const () as u64, cs, 0x0Eu8); 407 idt_set_entry(idt_ptr, 32, isr_stub_32 as *const () as u64, cs, 0x0Eu8, 0);
358 idt_set_entry(idt_ptr, 33, isr_stub_33 as *const () as u64, cs, 0x0Eu8); 408 idt_set_entry(idt_ptr, 33, isr_stub_33 as *const () as u64, cs, 0x0Eu8, 0);
409 // Yield (software interrupt).
410 idt_set_entry(
411 idt_ptr,
412 0x60,
413 isr_stub_60 as *const () as u64,
414 cs,
415 0x0Eu8,
416 0,
417 );
418 // Double fault and page fault run on dedicated IST stacks (see
419 // gdt.rs) so a task fault can't corrupt the faulting task's stack.
420 idt_set_entry(idt_ptr, 8, isr_stub_08 as *const () as u64, cs, 0x0Eu8, 2);
421 idt_set_entry(idt_ptr, 14, isr_stub_14 as *const () as u64, cs, 0x0Eu8, 1);
359 422
360 // Build the IDTR pseudo-descriptor. 423 // Build the IDTR pseudo-descriptor.
361 IDTR = [ 424 IDTR = [
@@ -389,11 +452,23 @@ pub fn init() {
389 } 452 }
390} 453}
391 454
455/// Halt the CPU forever with interrupts disabled.
456pub fn halt() -> ! {
457 loop {
458 unsafe {
459 core::arch::asm!("cli; hlt", options(nostack, preserves_flags));
460 }
461 }
462}
463
392// --------------------------------------------------------------------------- 464// ---------------------------------------------------------------------------
393// Extern symbols from global_asm! 465// Extern symbols from global_asm!
394// --------------------------------------------------------------------------- 466// ---------------------------------------------------------------------------
395unsafe extern "C" { 467unsafe extern "C" {
468 fn isr_stub_08();
469 fn isr_stub_14();
396 fn isr_stub_32(); 470 fn isr_stub_32();
397 fn isr_stub_33(); 471 fn isr_stub_33();
472 fn isr_stub_60();
398 fn isr_stub_spurious(); 473 fn isr_stub_spurious();
399} 474}