diff options
Diffstat (limited to '')
| -rw-r--r-- | rsnix/src/interrupts.rs | 139 |
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). |
| 25 | unsafe fn idt_set_entry(idt: *mut u8, vec: u8, handler: u64, selector: u16, flags: u8) { | 25 | unsafe 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 | ||
| 214 | core::arch::global_asm!( | 214 | core::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)] |
| 280 | struct IntFrame { | 304 | pub(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)] |
| 305 | unsafe extern "C" fn isr_dispatch(frame: &IntFrame) { | 338 | unsafe 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. | ||
| 456 | pub 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 | // --------------------------------------------------------------------------- |
| 395 | unsafe extern "C" { | 467 | unsafe 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 | } |
