diff options
| -rw-r--r-- | src/interrupts.rs | 96 | ||||
| -rw-r--r-- | src/ps2.rs | 64 |
2 files changed, 112 insertions, 48 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 |
| @@ -158,9 +158,64 @@ static mut KBD: KbdState = KbdState { | |||
| 158 | got_e0: false, | 158 | got_e0: false, |
| 159 | }; | 159 | }; |
| 160 | 160 | ||
| 161 | /// Initialise the PS/2 port for the keyboard — just drain stale bytes. | 161 | /// Initialise the PS/2 port: drain stale bytes + enable keyboard interrupt. |
| 162 | pub fn init() { | 162 | pub fn init() { |
| 163 | drain_input(); | 163 | drain_input(); |
| 164 | |||
| 165 | // Enable the keyboard interrupt on the PS/2 controller. | ||
| 166 | // Read Configuration Byte (command 0x20), set bit 0 (IRQ1 enable), | ||
| 167 | // write back (command 0x60). | ||
| 168 | unsafe { | ||
| 169 | // Tell the keyboard controller we want to read the config byte. | ||
| 170 | // Need to wait for the controller to be ready first. | ||
| 171 | wait_write(); | ||
| 172 | core::arch::asm!("out dx, al", in("dx") 0x64u16, in("al") 0x20u8, | ||
| 173 | options(nostack, preserves_flags)); | ||
| 174 | wait_read(); | ||
| 175 | let mut cfg: u8; | ||
| 176 | core::arch::asm!("in al, dx", out("al") cfg, in("dx") 0x60u16, | ||
| 177 | options(nostack, preserves_flags)); | ||
| 178 | |||
| 179 | cfg |= 0x01; // enable keyboard interrupt | ||
| 180 | |||
| 181 | // Write config byte back | ||
| 182 | wait_write(); | ||
| 183 | core::arch::asm!("out dx, al", in("dx") 0x64u16, in("al") 0x60u8, | ||
| 184 | options(nostack, preserves_flags)); | ||
| 185 | wait_write(); | ||
| 186 | core::arch::asm!("out dx, al", in("dx") 0x60u16, in("al") cfg, | ||
| 187 | options(nostack, preserves_flags)); | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | /// Wait until the PS/2 controller is ready to receive a command. | ||
| 192 | fn wait_write() { | ||
| 193 | unsafe { | ||
| 194 | loop { | ||
| 195 | let status: u8; | ||
| 196 | core::arch::asm!("in al, dx", out("al") status, in("dx") 0x64u16, | ||
| 197 | options(nostack, preserves_flags)); | ||
| 198 | if status & 0x02 == 0 { | ||
| 199 | // Input buffer empty — controller ready to receive | ||
| 200 | break; | ||
| 201 | } | ||
| 202 | } | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | /// Wait until the PS/2 controller has data available to read. | ||
| 207 | fn wait_read() { | ||
| 208 | unsafe { | ||
| 209 | loop { | ||
| 210 | let status: u8; | ||
| 211 | core::arch::asm!("in al, dx", out("al") status, in("dx") 0x64u16, | ||
| 212 | options(nostack, preserves_flags)); | ||
| 213 | if status & 0x01 != 0 { | ||
| 214 | // Output buffer full — data ready to read | ||
| 215 | break; | ||
| 216 | } | ||
| 217 | } | ||
| 218 | } | ||
| 164 | } | 219 | } |
| 165 | 220 | ||
| 166 | fn drain_input() { | 221 | fn drain_input() { |
| @@ -184,18 +239,11 @@ fn drain_input() { | |||
| 184 | /// this function pops and decodes them. | 239 | /// this function pops and decodes them. |
| 185 | pub fn poll_key() -> KeyEvent { | 240 | pub fn poll_key() -> KeyEvent { |
| 186 | loop { | 241 | loop { |
| 187 | // Disable interrupts so the buffer-empty → hlt window is atomic. | ||
| 188 | unsafe { core::arch::asm!("cli", options(nostack, preserves_flags)); } | ||
| 189 | |||
| 190 | if let Some(byte) = crate::interrupts::read_scancode() { | 242 | if let Some(byte) = crate::interrupts::read_scancode() { |
| 191 | unsafe { core::arch::asm!("sti", options(nostack, preserves_flags)); } | ||
| 192 | if let Some(ev) = process_byte(byte) { | 243 | if let Some(ev) = process_byte(byte) { |
| 193 | return ev; | 244 | return ev; |
| 194 | } | 245 | } |
| 195 | } else { | 246 | } else { |
| 196 | // Re-enable and halt. If a keyboard IRQ arrived between the | ||
| 197 | // buffer check and `sti`, the CPU sees a pending interrupt | ||
| 198 | // and `hlt` returns immediately. | ||
| 199 | unsafe { | 247 | unsafe { |
| 200 | core::arch::asm!("sti; hlt", options(nostack, preserves_flags)); | 248 | core::arch::asm!("sti; hlt", options(nostack, preserves_flags)); |
| 201 | } | 249 | } |
