diff options
| author | salmonik <salmonik@tuta.io> | 2026-07-31 02:40:18 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-07-31 02:40:18 +0200 |
| commit | 4b48b6938d91a91d7b1dd242991258fe47e284f0 (patch) | |
| tree | d0d32321e9384370b72b3bda4105daaea6853196 /src/interrupts.rs | |
| parent | interrupts: IDT, PIC remap, PIT timer, PS/2 keyboard IRQ (diff) | |
| download | rs-dos-4b48b6938d91a91d7b1dd242991258fe47e284f0.tar.gz rs-dos-4b48b6938d91a91d7b1dd242991258fe47e284f0.zip | |
fix: ISR dispatch calling convention + PS/2 interrupt enable
Root cause: x86_64-unknown-uefi uses the Microsoft x64 calling
convention where the first argument is passed in rcx, not rdi.
The ISR stub was setting rdi = rsp, but isr_dispatch() read
the frame pointer from rcx, getting garbage → triple fault.
Also:
- Enable PS/2 keyboard IRQ via controller config byte (bit 0).
- Expose read_scancode() and TICKS from interrupt module.
- ps2::poll_key() uses 'sti; hlt' race-free sleep pattern.
Diffstat (limited to '')
| -rw-r--r-- | src/interrupts.rs | 96 |
1 files changed, 56 insertions, 40 deletions
diff --git a/src/interrupts.rs b/src/interrupts.rs index 204d3d7..90d5bbb 100644 --- a/src/interrupts.rs +++ b/src/interrupts.rs | |||
| @@ -12,13 +12,13 @@ use core::sync::atomic::{AtomicU16, Ordering}; | |||
| 12 | #[derive(Copy, Clone)] | 12 | #[derive(Copy, Clone)] |
| 13 | #[repr(C, packed)] | 13 | #[repr(C, packed)] |
| 14 | struct IdtEntry { | 14 | struct IdtEntry { |
| 15 | offset_lo: u16, | 15 | offset_lo: u16, |
| 16 | selector: u16, | 16 | selector: u16, |
| 17 | ist: u8, | 17 | ist: u8, |
| 18 | flags: u8, | 18 | flags: u8, |
| 19 | offset_mid: u16, | 19 | offset_mid: u16, |
| 20 | offset_hi: u32, | 20 | offset_hi: u32, |
| 21 | _reserved: u32, | 21 | _reserved: u32, |
| 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). |
| @@ -27,13 +27,13 @@ unsafe fn idt_set_entry(idt: *mut u8, vec: u8, handler: u64, selector: u16, flag | |||
| 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: 0, |
| 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, |
| 36 | _reserved: 0, | 36 | _reserved: 0, |
| 37 | }); | 37 | }); |
| 38 | } | 38 | } |
| 39 | } | 39 | } |
| @@ -46,9 +46,9 @@ static mut IDTR: [u16; 5] = [0; 5]; // 2-byte limit + 4-byte base (48-bit pseudo | |||
| 46 | // PIC (8259A) | 46 | // PIC (8259A) |
| 47 | // --------------------------------------------------------------------------- | 47 | // --------------------------------------------------------------------------- |
| 48 | 48 | ||
| 49 | const PIC1_CMD: u16 = 0x20; | 49 | const PIC1_CMD: u16 = 0x20; |
| 50 | const PIC1_DATA: u16 = 0x21; | 50 | const PIC1_DATA: u16 = 0x21; |
| 51 | const PIC2_CMD: u16 = 0xA0; | 51 | const PIC2_CMD: u16 = 0xA0; |
| 52 | const PIC2_DATA: u16 = 0xA1; | 52 | const PIC2_DATA: u16 = 0xA1; |
| 53 | 53 | ||
| 54 | const PIC_EOI: u8 = 0x20; | 54 | const PIC_EOI: u8 = 0x20; |
| @@ -89,8 +89,8 @@ fn eoi(irq: u8) { | |||
| 89 | // PIT timer (channel 0, ~100 Hz) | 89 | // PIT timer (channel 0, ~100 Hz) |
| 90 | // --------------------------------------------------------------------------- | 90 | // --------------------------------------------------------------------------- |
| 91 | 91 | ||
| 92 | const PIT_CMD: u16 = 0x43; | 92 | const PIT_CMD: u16 = 0x43; |
| 93 | const PIT_CH0: u16 = 0x40; | 93 | const PIT_CH0: u16 = 0x40; |
| 94 | const PIT_FREQ: u64 = 1_193_182; | 94 | const PIT_FREQ: u64 = 1_193_182; |
| 95 | 95 | ||
| 96 | /// Set up PIT channel 0 in mode 2 (rate generator) at `hz` (1–1000ish). | 96 | /// Set up PIT channel 0 in mode 2 (rate generator) at `hz` (1–1000ish). |
| @@ -177,7 +177,9 @@ impl KeyBuf { | |||
| 177 | if next.wrapping_sub(h) as u16 > 255 { | 177 | if next.wrapping_sub(h) as u16 > 255 { |
| 178 | return; // drop — ring is full | 178 | return; // drop — ring is full |
| 179 | } | 179 | } |
| 180 | unsafe { *self.buf[t as usize & 255].get() = byte; } | 180 | unsafe { |
| 181 | *self.buf[t as usize & 255].get() = byte; | ||
| 182 | } | ||
| 181 | // Ensure byte is globally visible before incrementing tail. | 183 | // Ensure byte is globally visible before incrementing tail. |
| 182 | self.tail.store(next, Ordering::Release); | 184 | self.tail.store(next, Ordering::Release); |
| 183 | } | 185 | } |
| @@ -206,8 +208,8 @@ core::arch::global_asm!( | |||
| 206 | ".globl isr_stub_32", | 208 | ".globl isr_stub_32", |
| 207 | ".p2align 4", | 209 | ".p2align 4", |
| 208 | "isr_stub_32:", | 210 | "isr_stub_32:", |
| 209 | " push 0", // error code (non-exception) | 211 | " push 0", // error code (non-exception) |
| 210 | " push 32", // vector | 212 | " push 32", // vector |
| 211 | " jmp isr_common", | 213 | " jmp isr_common", |
| 212 | "", | 214 | "", |
| 213 | ".globl isr_stub_33", | 215 | ".globl isr_stub_33", |
| @@ -243,7 +245,7 @@ core::arch::global_asm!( | |||
| 243 | " push r14", | 245 | " push r14", |
| 244 | " push r15", | 246 | " push r15", |
| 245 | "", | 247 | "", |
| 246 | " mov rdi, rsp", // first arg: pointer to saved registers | 248 | " mov rcx, rsp", // first arg (rcx = MS x64 ABI) |
| 247 | " call isr_dispatch", | 249 | " call isr_dispatch", |
| 248 | "", | 250 | "", |
| 249 | " pop r15", | 251 | " pop r15", |
| @@ -261,36 +263,50 @@ core::arch::global_asm!( | |||
| 261 | " pop rdx", | 263 | " pop rdx", |
| 262 | " pop rcx", | 264 | " pop rcx", |
| 263 | " pop rax", | 265 | " pop rax", |
| 264 | " add rsp, 16", // pop vector + error code | 266 | " add rsp, 16", // pop vector + error code |
| 265 | " iretq", | 267 | " iretq", |
| 266 | ); | 268 | ); |
| 267 | 269 | ||
| 268 | /// Saved register frame (as pushed by `isr_common`). | 270 | /// Saved register frame (as pushed by `isr_common`). |
| 269 | #[repr(C)] | 271 | #[repr(C)] |
| 270 | struct IntFrame { | 272 | struct IntFrame { |
| 271 | r15: u64, r14: u64, r13: u64, r12: u64, | 273 | r15: u64, |
| 272 | r11: u64, r10: u64, r9: u64, r8: u64, | 274 | r14: u64, |
| 273 | rdi: u64, rsi: u64, rbp: u64, rbx: u64, | 275 | r13: u64, |
| 274 | rdx: u64, rcx: u64, rax: u64, | 276 | r12: u64, |
| 275 | vector: u64, | 277 | r11: u64, |
| 276 | error: u64, | 278 | r10: u64, |
| 277 | rip: u64, | 279 | r9: u64, |
| 278 | cs: u64, | 280 | r8: u64, |
| 279 | rflags: u64, | 281 | rdi: u64, |
| 282 | rsi: u64, | ||
| 283 | rbp: u64, | ||
| 284 | rbx: u64, | ||
| 285 | rdx: u64, | ||
| 286 | rcx: u64, | ||
| 287 | rax: u64, | ||
| 288 | vector: u64, | ||
| 289 | error: u64, | ||
| 290 | rip: u64, | ||
| 291 | cs: u64, | ||
| 292 | rflags: u64, | ||
| 280 | } | 293 | } |
| 281 | 294 | ||
| 282 | /// Called from the asm common handler — dispatches to the right Rust handler. | 295 | /// Called from the asm common handler — dispatches to the right Rust handler. |
| 283 | #[no_mangle] | 296 | #[no_mangle] |
| 284 | extern "C" fn isr_dispatch(frame: &IntFrame) { | 297 | unsafe extern "C" fn isr_dispatch(frame: &IntFrame) { |
| 285 | match frame.vector { | 298 | unsafe { |
| 286 | 32 => timer_irq(), | 299 | match frame.vector { |
| 287 | 33 => keyboard_irq(), | 300 | 32 => eoi(0), |
| 288 | 0xFF => { /* spurious — ignore */ } | 301 | 33 => { |
| 289 | other => { | 302 | let byte: u8; |
| 290 | // Log unexpected interrupt on serial (but don't halt — might be benign). | 303 | core::arch::asm!("in al, dx", out("al") byte, in("dx") 0x60u16, |
| 291 | crate::serial::write_str("intr: unhandled vector "); | 304 | options(nostack, preserves_flags)); |
| 292 | crate::serial::put_byte(other as u8); | 305 | // Push scancode to ring buffer (called with interrupts disabled). |
| 293 | crate::serial::put_byte(b'\n'); | 306 | KEYBUF.push(byte); |
| 307 | eoi(1); | ||
| 308 | } | ||
| 309 | _ => {} | ||
| 294 | } | 310 | } |
| 295 | } | 311 | } |
| 296 | } | 312 | } |
| @@ -346,7 +362,7 @@ pub fn init() { | |||
| 346 | 362 | ||
| 347 | // Build the IDTR pseudo-descriptor. | 363 | // Build the IDTR pseudo-descriptor. |
| 348 | IDTR = [ | 364 | IDTR = [ |
| 349 | (256u16 * 16 - 1) as u16, // limit = 256*16 - 1 | 365 | (256u16 * 16 - 1) as u16, // limit = 256*16 - 1 |
| 350 | idt_ptr as u16, // base lo | 366 | idt_ptr as u16, // base lo |
| 351 | (idt_ptr as u64 >> 16) as u16, // base mid | 367 | (idt_ptr as u64 >> 16) as u16, // base mid |
| 352 | (idt_ptr as u64 >> 32) as u16, // base hi | 368 | (idt_ptr as u64 >> 32) as u16, // base hi |
