1use super::{pic, tss};
7use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
8use x86_64::{
9 structures::{
10 gdt::SegmentSelector,
11 idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode},
12 },
13 VirtAddr,
14};
15
16const KERNEL_CODE_SELECTOR: SegmentSelector = SegmentSelector(0x08);
17
18#[repr(C, packed)]
19struct Idtr {
20 limit: u16,
21 base: u64,
22}
23
24#[derive(Clone, Copy, Debug)]
25pub struct LiveIdtGateInfo {
26 pub vector: u8,
27 pub selector: u16,
28 pub options: u16,
29 pub offset: u64,
30}
31
32#[allow(dead_code)]
34pub mod irq {
35 pub const TIMER: u8 = super::pic::PIC1_OFFSET; pub const KEYBOARD: u8 = super::pic::PIC1_OFFSET + 1; pub const CASCADE: u8 = super::pic::PIC1_OFFSET + 2; pub const MOUSE: u8 = super::pic::PIC1_OFFSET + 12; pub const COM2: u8 = super::pic::PIC1_OFFSET + 3; pub const COM1: u8 = super::pic::PIC1_OFFSET + 4; pub const FLOPPY: u8 = super::pic::PIC1_OFFSET + 6; pub const ATA_PRIMARY: u8 = super::pic::PIC1_OFFSET + 14; pub const ATA_SECONDARY: u8 = super::pic::PIC1_OFFSET + 15; }
45
46struct SwapGsGuard {
62 from_ring3: bool,
63}
64
65impl SwapGsGuard {
66 #[inline(always)]
69 fn new(from_ring3: bool) -> Self {
70 if from_ring3 {
71 unsafe { core::arch::asm!("swapgs", options(nostack, preserves_flags)) };
75 }
76 Self { from_ring3 }
77 }
78}
79
80impl Drop for SwapGsGuard {
81 #[inline(always)]
82 fn drop(&mut self) {
83 if self.from_ring3 {
84 unsafe { core::arch::asm!("swapgs", options(nostack, preserves_flags)) };
87 }
88 }
89}
90
91#[inline(always)]
104fn needs_swapgs(cs: u16) -> bool {
105 if (cs & 3) == 3 {
107 return true;
108 }
109 let gs_base: u64 = unsafe {
112 let lo: u32;
113 let hi: u32;
114 core::arch::asm!(
115 "rdmsr",
116 in("ecx") 0xC000_0101u32, out("eax") lo,
118 out("edx") hi,
119 options(nostack, preserves_flags),
120 );
121 (lo as u64) | ((hi as u64) << 32)
122 };
123 gs_base < 0xFFFF_8000_0000_0000
124}
125
126static mut IDT_STORAGE: InterruptDescriptorTable = InterruptDescriptorTable::new();
128static IDT_STORAGE_LOCK: AtomicBool = AtomicBool::new(false);
129static USER_PF_TRACE_BUDGET: AtomicU32 = AtomicU32::new(64);
130static RESCHED_IPI_TRACE_BUDGET: AtomicU32 = AtomicU32::new(32);
131
132pub fn live_gate_info(vector: u8) -> Option<LiveIdtGateInfo> {
133 let mut idtr = Idtr { limit: 0, base: 0 };
134 unsafe {
136 core::arch::asm!(
137 "sidt [{}]",
138 in(reg) &mut idtr,
139 options(nostack, preserves_flags),
140 );
141 }
142
143 let entry_offset = vector as usize * 16;
144 if entry_offset + 16 > idtr.limit as usize + 1 {
145 return None;
146 }
147
148 let (low, high) = unsafe {
150 let entry_ptr = (idtr.base + entry_offset as u64) as *const u64;
151 (
152 core::ptr::read_unaligned(entry_ptr),
153 core::ptr::read_unaligned(entry_ptr.add(1)),
154 )
155 };
156
157 let offset = (low & 0xFFFF) | (((low >> 48) & 0xFFFF) << 16) | ((high & 0xFFFF_FFFF) << 32);
158 let selector = ((low >> 16) & 0xFFFF) as u16;
159 let options = ((low >> 32) & 0xFFFF) as u16;
160
161 Some(LiveIdtGateInfo {
162 vector,
163 selector,
164 options,
165 offset,
166 })
167}
168
169#[repr(C)]
176#[derive(Clone, Copy, Debug, Default)]
177pub struct InterruptReturnDecision {
178 pub next_rsp: u64,
179 pub old_fpu: *mut u8,
180 pub new_fpu: *const u8,
181}
182
183#[unsafe(naked)]
190unsafe extern "C" fn lapic_timer_entry() -> ! {
191 core::arch::naked_asm!(
192 "cld",
193 "test qword ptr [rsp + 8], 0x3",
201 "jz 2f",
202 "swapgs",
203 "2:",
204 "push rax",
206 "push rcx",
207 "push rdx",
208 "push rdi",
209 "push rsi",
210 "push r8",
211 "push r9",
212 "push r10",
213 "push r11",
214 "push rbx",
215 "push rbp",
216 "push r12",
217 "push r13",
218 "push r14",
219 "push r15",
220 "sub rsp, 32",
223 "mov rdi, rsp",
224 "lea rsi, [rsp + 32]",
225 "call {inner}",
226 "mov rax, [rsp + 0]",
228 "mov rdx, [rsp + 8]",
229 "mov rcx, [rsp + 16]",
230 "add rsp, 32",
231 "test rax, rax",
232 "jz 3f",
233 "fxsave [rdx]",
235 "mov rsp, rax",
236 "fxrstor [rcx]",
237 "call {switch_finish}",
238 "3:",
239 "pop r15",
243 "pop r14",
244 "pop r13",
245 "pop r12",
246 "pop rbp",
247 "pop rbx",
248 "pop r11",
249 "pop r10",
250 "pop r9",
251 "pop r8",
252 "pop rsi",
253 "pop rdi",
254 "pop rdx",
255 "pop rcx",
256 "pop rax",
257 "test qword ptr [rsp + 8], 0x3",
259 "jz 4f",
260 "swapgs",
261 "4:",
262 "iretq",
263 inner = sym lapic_timer_inner,
264 switch_finish = sym crate::process::scheduler::finish_interrupt_switch,
265 );
266}
267
268#[unsafe(naked)]
273unsafe extern "C" fn resched_ipi_entry() -> ! {
274 core::arch::naked_asm!(
275 "cld",
276 "test qword ptr [rsp + 8], 0x3",
277 "jz 2f",
278 "swapgs",
279 "2:",
280 "push rax",
281 "mov al, 0x65",
282 "out 0xe9, al",
283 "push rcx",
284 "push rdx",
285 "push rdi",
286 "push rsi",
287 "push r8",
288 "push r9",
289 "push r10",
290 "push r11",
291 "push rbx",
292 "push rbp",
293 "push r12",
294 "push r13",
295 "push r14",
296 "push r15",
297 "sub rsp, 32",
298 "mov al, 0x45",
299 "out 0xe9, al",
300 "mov rdi, rsp",
301 "lea rsi, [rsp + 32]",
302 "call {inner}",
303 "mov rax, [rsp + 0]",
304 "mov rdx, [rsp + 8]",
305 "mov rcx, [rsp + 16]",
306 "add rsp, 32",
307 "test rax, rax",
308 "jz 3f",
309 "ud2",
310 "3:",
311 "pop r15",
312 "pop r14",
313 "pop r13",
314 "pop r12",
315 "pop rbp",
316 "pop rbx",
317 "pop r11",
318 "pop r10",
319 "pop r9",
320 "pop r8",
321 "pop rsi",
322 "pop rdi",
323 "pop rdx",
324 "pop rcx",
325 "pop rax",
326 "test qword ptr [rsp + 8], 0x3",
327 "jz 4f",
328 "swapgs",
329 "4:",
330 "iretq",
331 inner = sym resched_ipi_inner,
332 );
333}
334
335extern "C" fn lapic_timer_inner(
336 frame: &mut crate::syscall::SyscallFrame,
337) -> InterruptReturnDecision {
338 let cpu = crate::arch::x86_64::percpu::current_cpu_index();
339 let ticks = crate::process::scheduler::ticks();
340 let from_ring3 = (frame.iret_cs & 3) == 3;
342 if from_ring3 && (ticks < 5 || ticks % 100 == 0) {
343 unsafe { core::arch::asm!("mov al, 0x48; out 0xe9, al", out("al") _) } }
345 crate::process::scheduler::timer_tick();
346 crate::arch::x86_64::speaker::speaker_tick();
347 super::apic::eoi();
348
349 if from_ring3 {
355 crate::process::signal::deliver_pending_signal_on_interrupt_return(frame);
356 }
357
358 crate::process::scheduler::request_force_resched_hint(cpu);
368 InterruptReturnDecision::default()
369}
370
371extern "C" fn resched_ipi_inner(
372 frame: &mut crate::syscall::SyscallFrame,
373) -> InterruptReturnDecision {
374 let cpu = crate::arch::x86_64::percpu::current_cpu_index();
375 let should_trace = RESCHED_IPI_TRACE_BUDGET
376 .fetch_update(Ordering::AcqRel, Ordering::Relaxed, |budget| {
377 budget.checked_sub(1)
378 })
379 .is_ok();
380 if should_trace {
381 let rsp0 = crate::arch::x86_64::tss::kernel_stack_for(cpu)
382 .map(|addr| addr.as_u64())
383 .unwrap_or(0);
384 let (slot_rip, slot_cs, slot_rsp, slot_ss) = if rsp0 >= 40 {
385 unsafe {
389 let frame_base = (rsp0 - 40) as *const u64;
390 (
391 *frame_base.add(0),
392 *frame_base.add(1),
393 *frame_base.add(3),
394 *frame_base.add(4),
395 )
396 }
397 } else {
398 (0, 0, 0, 0)
399 };
400 crate::e9_println!(
401 "[ipi-rsp0] cpu={} rsp0={:#x} slot_rip={:#x} slot_cs={:#x} slot_rsp={:#x} slot_ss={:#x} frame_rip={:#x} frame_cs={:#x} frame_rsp={:#x} frame_ss={:#x}",
402 cpu,
403 rsp0,
404 slot_rip,
405 slot_cs,
406 slot_rsp,
407 slot_ss,
408 frame.iret_rip,
409 frame.iret_cs,
410 frame.iret_rsp,
411 frame.iret_ss,
412 );
413 }
414 super::apic::eoi();
415 crate::process::scheduler::request_force_resched_hint(cpu);
416 InterruptReturnDecision::default()
417}
418
419#[inline]
420fn lock_idt_storage() {
421 while IDT_STORAGE_LOCK
422 .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
423 .is_err()
424 {
425 core::hint::spin_loop();
426 }
427}
428
429#[inline]
430fn unlock_idt_storage() {
431 IDT_STORAGE_LOCK.store(false, Ordering::Release);
432}
433
434pub fn init() {
435 lock_idt_storage();
436 unsafe {
437 let idt = &raw mut IDT_STORAGE;
438
439 (*idt)
441 .breakpoint
442 .set_handler_fn(breakpoint_handler)
443 .set_code_selector(KERNEL_CODE_SELECTOR);
444 (*idt)
445 .page_fault
446 .set_handler_fn(page_fault_handler)
447 .set_code_selector(KERNEL_CODE_SELECTOR);
448 (*idt)
449 .general_protection_fault
450 .set_handler_fn(general_protection_fault_handler)
451 .set_code_selector(KERNEL_CODE_SELECTOR);
452 (*idt)
453 .stack_segment_fault
454 .set_handler_fn(stack_segment_fault_handler)
455 .set_code_selector(KERNEL_CODE_SELECTOR);
456 (*idt)
457 .non_maskable_interrupt
458 .set_handler_fn(non_maskable_interrupt_handler)
459 .set_code_selector(KERNEL_CODE_SELECTOR);
460 (*idt)
461 .invalid_opcode
462 .set_handler_fn(invalid_opcode_handler)
463 .set_code_selector(KERNEL_CODE_SELECTOR);
464 (*idt)
465 .double_fault
466 .set_handler_fn(double_fault_handler)
467 .set_code_selector(KERNEL_CODE_SELECTOR)
468 .set_stack_index(tss::DOUBLE_FAULT_IST_INDEX);
469
470 let idt_ref = &mut *idt;
472 idt_ref[irq::TIMER as u8]
473 .set_handler_fn(legacy_timer_handler)
474 .set_code_selector(KERNEL_CODE_SELECTOR);
475 idt_ref[irq::KEYBOARD as u8]
476 .set_handler_fn(keyboard_handler)
477 .set_code_selector(KERNEL_CODE_SELECTOR);
478 idt_ref[irq::MOUSE as u8]
479 .set_handler_fn(mouse_handler)
480 .set_code_selector(KERNEL_CODE_SELECTOR);
481
482 idt_ref[0xFF_u8]
484 .set_handler_fn(spurious_handler)
485 .set_code_selector(KERNEL_CODE_SELECTOR);
486
487 idt_ref[super::apic::IPI_RESCHED_VECTOR as u8]
490 .set_handler_addr(VirtAddr::from_ptr(resched_ipi_entry as *const ()))
491 .set_code_selector(KERNEL_CODE_SELECTOR);
492
493 idt_ref[super::apic::IPI_TLB_SHOOTDOWN_VECTOR as u8]
495 .set_handler_fn(tlb_shootdown_handler)
496 .set_code_selector(KERNEL_CODE_SELECTOR);
497
498 (*idt).load_unsafe();
499 }
500 unlock_idt_storage();
501
502 log::debug!("IDT initialized with {} entries", 256);
503}
504
505pub fn load() {
506 lock_idt_storage();
507 unsafe {
508 let idt = &raw const IDT_STORAGE;
509 (*idt).load_unsafe();
510 }
511 unlock_idt_storage();
512}
513
514pub fn register_lapic_timer_vector(vector: u8) {
516 lock_idt_storage();
517 unsafe {
518 let idt = &raw mut IDT_STORAGE;
519 (&mut *idt)[vector]
520 .set_handler_addr(VirtAddr::from_ptr(lapic_timer_entry as *const ()))
521 .set_code_selector(KERNEL_CODE_SELECTOR);
522 (*idt).load_unsafe();
523 }
524 unlock_idt_storage();
525}
526
527pub fn register_ahci_irq(irq: u8) {
531 let vector = if irq < 16 {
532 super::pic::PIC1_OFFSET + irq
533 } else {
534 irq
535 };
536
537 lock_idt_storage();
538 unsafe {
539 let idt = &raw mut IDT_STORAGE;
540 (&mut *idt)[vector]
541 .set_handler_fn(ahci_handler)
542 .set_code_selector(KERNEL_CODE_SELECTOR);
543 (*idt).load_unsafe();
544 }
545 unlock_idt_storage();
546 log::info!("AHCI IRQ {} registered on vector {:#x}", irq, vector);
547}
548
549pub fn register_nvme_irq(irq: u8) {
553 let vector = if irq < 16 {
554 super::pic::PIC1_OFFSET + irq
555 } else {
556 irq
557 };
558
559 lock_idt_storage();
560 unsafe {
561 let idt = &raw mut IDT_STORAGE;
562 (&mut *idt)[vector]
563 .set_handler_fn(nvme_handler)
564 .set_code_selector(KERNEL_CODE_SELECTOR);
565 (*idt).load_unsafe();
566 }
567 unlock_idt_storage();
568 log::info!("NVMe IRQ {} registered on vector {:#x}", irq, vector);
569}
570
571pub fn register_virtio_block_irq(irq: u8) {
576 let vector = if irq < 16 {
579 super::pic::PIC1_OFFSET + irq
580 } else {
581 irq
582 };
583
584 lock_idt_storage();
585 unsafe {
586 let idt = &raw mut IDT_STORAGE;
587 (&mut *idt)[vector]
588 .set_handler_fn(virtio_block_handler)
589 .set_code_selector(KERNEL_CODE_SELECTOR);
590 (*idt).load_unsafe();
591 }
592 unlock_idt_storage();
593 log::info!("VirtIO-blk IRQ {} registered on vector {:#x}", irq, vector);
594}
595
596pub fn register_xhci_irq(irq: u8) {
600 let vector = if irq < 16 {
601 super::pic::PIC1_OFFSET + irq
602 } else {
603 irq
604 };
605
606 lock_idt_storage();
607 unsafe {
608 let idt = &raw mut IDT_STORAGE;
609 (&mut *idt)[vector]
610 .set_handler_fn(xhci_handler)
611 .set_code_selector(KERNEL_CODE_SELECTOR);
612 (*idt).load_unsafe();
613 }
614 unlock_idt_storage();
615 log::info!("xHCI IRQ {} registered on vector {:#x}", irq, vector);
616}
617
618pub fn register_nic_irq(irq: u8) {
626 let vector = if irq < 16 {
627 super::pic::PIC1_OFFSET + irq
628 } else {
629 irq
630 };
631
632 lock_idt_storage();
633 unsafe {
634 let idt = &raw mut IDT_STORAGE;
635 (&mut *idt)[vector]
636 .set_handler_fn(nic_handler)
637 .set_code_selector(KERNEL_CODE_SELECTOR);
638 (*idt).load_unsafe();
639 }
640 unlock_idt_storage();
641 log::info!("NIC IRQ {} registered on vector {:#x}", irq, vector);
642}
643
644extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {
650 let _gs = SwapGsGuard::new(needs_swapgs(stack_frame.code_segment.0));
651 log::warn!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
652}
653
654extern "x86-interrupt" fn invalid_opcode_handler(stack_frame: InterruptStackFrame) {
656 let cs = stack_frame.code_segment.0;
657 let is_user = (cs & 3) == 3;
658 let _gs = SwapGsGuard::new(needs_swapgs(cs));
659 if is_user {
660 if let Some(tid) = crate::process::current_task_id() {
661 crate::silo::handle_user_fault(
662 tid,
663 crate::silo::SiloFaultReason::InvalidOpcode,
664 stack_frame.instruction_pointer.as_u64(),
665 0,
666 stack_frame.instruction_pointer.as_u64(),
667 );
668 return;
669 }
670 }
671 log::error!("EXCEPTION: INVALID OPCODE\n{:#?}", stack_frame);
672 panic!("Invalid opcode");
673}
674
675extern "x86-interrupt" fn non_maskable_interrupt_handler(stack_frame: InterruptStackFrame) {
676 let _gs = SwapGsGuard::new(needs_swapgs(stack_frame.code_segment.0));
679 if crate::boot::panic::panic_in_progress() {
680 crate::arch::x86_64::cli();
681 loop {
682 crate::arch::x86_64::hlt();
683 }
684 }
685 crate::serial_force_println!(
686 "[NMI] rip={:#x} cs={:#x}",
687 stack_frame.instruction_pointer.as_u64(),
688 stack_frame.code_segment.0
689 );
690 crate::arch::x86_64::cli();
691 loop {
692 crate::arch::x86_64::hlt();
693 }
694}
695
696extern "x86-interrupt" fn page_fault_handler(
698 stack_frame: InterruptStackFrame,
699 error_code: PageFaultErrorCode,
700) {
701 use x86_64::registers::control::Cr2;
702 let cs = stack_frame.code_segment.0;
703 let is_user = (cs & 3) == 3;
704 let swapgs_needed = needs_swapgs(cs);
709 let _gs = SwapGsGuard::new(swapgs_needed);
710
711 if swapgs_needed && !is_user {
713 let fault_addr = x86_64::registers::control::Cr2::read()
714 .as_ref()
715 .map(|v| v.as_u64())
716 .unwrap_or(0);
717 crate::serial_force_println!(
718 "\x1b[31;1m[pagefault]\x1b[0m SWAPGS-WINDOW: CS={:#x} (Ring0) but GS was user! rip={:#x} addr={:#x} err={:#x}",
719 cs,
720 stack_frame.instruction_pointer.as_u64(),
721 fault_addr,
722 error_code.bits()
723 );
724
725 if let Some(task) = crate::process::current_task_clone() {
732 crate::process::kill_task(task.id);
733 }
734 drop(_gs);
735 crate::process::scheduler::exit_current_task(-11); }
737
738 let fault_addr = Cr2::read();
740 let fault_vaddr = fault_addr.as_ref().map(|v| v.as_u64()).unwrap_or(0);
741 let rip = stack_frame.instruction_pointer.as_u64();
742 let user_rsp = stack_frame.stack_pointer.as_u64();
743
744 let mut trace_ctx = crate::trace::TraceTaskCtx::empty();
745 if is_user {
746 if let Some(task) = crate::process::current_task_clone() {
747 let as_ref = task.process.address_space_arc();
748 trace_ctx = crate::trace::TraceTaskCtx {
749 task_id: task.id.as_u64(),
750 pid: task.pid,
751 tid: task.tid,
752 cr3: as_ref.cr3().as_u64(),
753 };
754 }
755 }
756
757 let do_pf_trace = if is_user {
758 USER_PF_TRACE_BUDGET
759 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |v| {
760 if v > 0 {
761 Some(v - 1)
762 } else {
763 None
764 }
765 })
766 .is_ok()
767 } else {
768 true
769 };
770 if do_pf_trace {
771 crate::trace_mem!(
772 crate::trace::category::MEM_PF,
773 crate::trace::TraceKind::MemPageFault,
774 error_code.bits() as u64,
775 trace_ctx,
776 rip,
777 fault_vaddr,
778 user_rsp,
779 0
780 );
781 }
782
783 if error_code.contains(PageFaultErrorCode::PROTECTION_VIOLATION)
786 && error_code.contains(PageFaultErrorCode::CAUSED_BY_WRITE)
787 && is_user
788 {
789 if let Some(task) = crate::process::current_task_clone() {
790 let address_space = task.process.address_space_arc();
791 if let Ok(vaddr) = fault_addr {
792 match crate::syscall::fork::handle_cow_fault(vaddr.as_u64(), &address_space) {
793 Ok(()) => {
794 crate::trace_mem!(
795 crate::trace::category::MEM_COW,
796 crate::trace::TraceKind::MemCow,
797 1,
798 trace_ctx,
799 rip,
800 vaddr.as_u64(),
801 0,
802 0
803 );
804 return;
805 }
806 Err(reason) => {
807 crate::trace_mem!(
808 crate::trace::category::MEM_COW,
809 crate::trace::TraceKind::MemCow,
810 0,
811 trace_ctx,
812 rip,
813 vaddr.as_u64(),
814 0,
815 0
816 );
817 crate::serial_println!(
818 "\x1b[31m[pagefault] COW resolve failed\x1b[0m: task={} \x1b[36mpid={}\x1b[0m tid={} \x1b[35maddr={:#x}\x1b[0m \x1b[35mrip={:#x}\x1b[0m err={}",
819 task.id.as_u64(),
820 task.pid,
821 task.tid,
822 vaddr.as_u64(),
823 stack_frame.instruction_pointer.as_u64(),
824 reason
825 );
826 }
827 }
828 }
829 }
830 }
831
832 if is_user {
833 if let Some(task) = crate::process::current_task_clone() {
834 let address_space = task.process.address_space_arc();
835 if let Ok(vaddr) = fault_addr {
836 if do_pf_trace {
837 crate::serial_force_println!(
840 "\x1b[33m[pagefault] USER fault\x1b[0m: tid={} rip={:#x} addr={:#x} err={:#x}",
841 task.tid,
842 rip,
843 vaddr.as_u64(),
844 error_code.bits()
845 );
846 crate::e9_println!(
848 "[PF] tid={} rip={:#x} addr={:#x} err={:#x}",
849 task.tid,
850 rip,
851 vaddr.as_u64(),
852 error_code.bits()
853 );
854 }
855
856 match address_space.handle_fault(vaddr.as_u64()) {
857 Ok(()) => {
858 if do_pf_trace {
859 crate::serial_force_println!(
860 "\x1b[32m[pagefault] USER fault resolved\x1b[0m: tid={} addr={:#x}",
861 task.tid,
862 vaddr.as_u64()
863 );
864 }
865 return;
866 }
867 Err(e) => {
868 crate::serial_force_println!(
869 "\x1b[31m[pagefault] USER fault resolution FAILED\x1b[0m: tid={} addr={:#x} err={:?}",
870 task.tid,
871 vaddr.as_u64(),
872 e
873 );
874 crate::e9_println!(
875 "[PF-FAIL] tid={} rip={:#x} addr={:#x}",
876 task.tid,
877 rip,
878 vaddr.as_u64()
879 );
880 dump_user_pf_context(&address_space, rip, user_rsp);
881 }
882 }
883 }
884 }
885 }
886
887 if is_user {
888 if let Some(tid) = crate::process::current_task_id() {
889 crate::silo::handle_user_fault(
890 tid,
891 crate::silo::SiloFaultReason::PageFault,
892 fault_addr.as_ref().map(|v| v.as_u64()).unwrap_or(0),
893 error_code.bits() as u64,
894 stack_frame.instruction_pointer.as_u64(),
895 );
896 return;
897 }
898 } else {
899 crate::serial_force_println!(
901 "\x1b[31;1m[pagefault] KERNEL fault\x1b[0m: rip={:#x} addr={:#x} err={:#x}",
902 rip,
903 fault_addr.as_ref().map(|v| v.as_u64()).unwrap_or(0),
904 error_code.bits()
905 );
906 }
907
908 let task_snap = crate::process::scheduler::current_task_clone_try();
910 dump_page_fault_full(&stack_frame, error_code, fault_addr, &task_snap);
911}
912
913fn decode_error_code(ec: PageFaultErrorCode) -> &'static str {
929 let p = ec.contains(PageFaultErrorCode::PROTECTION_VIOLATION);
930 let w = ec.contains(PageFaultErrorCode::CAUSED_BY_WRITE);
931 let u = ec.contains(PageFaultErrorCode::USER_MODE);
932 match (p, w, u) {
933 (false, false, false) => "kernel read of non-present page",
934 (false, true, false) => "kernel write to non-present page",
935 (false, false, true) => "user read of non-present page",
936 (false, true, true) => "user write to non-present page",
937 (true, false, false) => "kernel read protection violation",
938 (true, true, false) => "kernel write protection violation (COW / RO page)",
939 (true, false, true) => "user read protection violation (NX / supervisor-only)",
940 (true, true, true) => "user write protection violation (COW / RO page)",
941 }
942}
943
944fn format_pte_flags(entry: u64) -> [u8; 32] {
946 let mut buf = [b' '; 32];
947 let mut pos = 0usize;
948 let flags: &[(&str, u64)] = &[
949 ("P", 1 << 0),
950 ("RW", 1 << 1),
951 ("US", 1 << 2),
952 ("PWT", 1 << 3),
953 ("PCD", 1 << 4),
954 ("A", 1 << 5),
955 ("D", 1 << 6),
956 ("PS", 1 << 7),
957 ("G", 1 << 8),
958 ("NX", 1 << 63),
959 ];
960 for &(name, bit) in flags {
961 if entry & bit != 0 {
962 for &b in name.as_bytes() {
963 if pos < buf.len() {
964 buf[pos] = b;
965 pos += 1;
966 }
967 }
968 if pos < buf.len() {
969 buf[pos] = b'|';
970 pos += 1;
971 }
972 }
973 }
974 if pos > 0 && buf[pos - 1] == b'|' {
975 buf[pos - 1] = b' ';
976 }
977 buf
978}
979
980fn translate_via_raw_pt(vaddr: u64, cr3_phys: u64, hhdm: u64) -> Option<u64> {
988 unsafe {
989 let l4_ptr = (cr3_phys + hhdm) as *const u64;
990 let l4e = *l4_ptr.add(((vaddr >> 39) & 0x1FF) as usize);
991 if l4e & 1 == 0 {
992 return None;
993 }
994
995 let l3_ptr = ((l4e & 0x000F_FFFF_FFFF_F000) + hhdm) as *const u64;
996 let l3e = *l3_ptr.add(((vaddr >> 30) & 0x1FF) as usize);
997 if l3e & 1 == 0 {
998 return None;
999 }
1000 if l3e & 0x80 != 0 {
1001 return Some((l3e & 0x000F_FFFF_C000_0000) + (vaddr & 0x3FFF_FFFF));
1002 }
1003
1004 let l2_ptr = ((l3e & 0x000F_FFFF_FFFF_F000) + hhdm) as *const u64;
1005 let l2e = *l2_ptr.add(((vaddr >> 21) & 0x1FF) as usize);
1006 if l2e & 1 == 0 {
1007 return None;
1008 }
1009 if l2e & 0x80 != 0 {
1010 return Some((l2e & 0x000F_FFFF_FFE0_0000) + (vaddr & 0x1F_FFFF));
1011 }
1012
1013 let l1_ptr = ((l2e & 0x000F_FFFF_FFFF_F000) + hhdm) as *const u64;
1014 let l1e = *l1_ptr.add(((vaddr >> 12) & 0x1FF) as usize);
1015 if l1e & 1 == 0 {
1016 return None;
1017 }
1018 Some((l1e & 0x000F_FFFF_FFFF_F000) + (vaddr & 0xFFF))
1019 }
1020}
1021
1022fn dump_memory_bytes(vaddr: u64, cr3_phys: u64, count: usize, prefix: &str) {
1025 let hhdm = crate::memory::hhdm_offset();
1026 let mut offset = 0usize;
1027 while offset < count {
1028 let cur_va = vaddr.wrapping_add(offset as u64);
1029 let page_off = (cur_va & 0xFFF) as usize;
1030 let chunk = core::cmp::min(count - offset, 0x1000 - page_off);
1031 let Some(phys) = translate_via_raw_pt(cur_va, cr3_phys, hhdm) else {
1032 crate::serial_println!("{}(page {:#x} not mapped)", prefix, cur_va);
1033 offset += chunk;
1034 continue;
1035 };
1036 let src = (phys - (cur_va & 0xFFF) + hhdm) as *const u8;
1038 let mut line_off = 0usize;
1039 while line_off < chunk {
1040 let ll = core::cmp::min(16, chunk - line_off);
1041 let line_va = cur_va.wrapping_add(line_off as u64);
1042 let mut hex = [0u8; 48];
1043 let mut asc = [b'.'; 16];
1044 for i in 0..ll {
1045 let byte = unsafe { *src.add(page_off + line_off + i) };
1046 let hi = byte >> 4;
1047 let lo = byte & 0xF;
1048 hex[i * 3] = if hi < 10 { b'0' + hi } else { b'a' + hi - 10 };
1049 hex[i * 3 + 1] = if lo < 10 { b'0' + lo } else { b'a' + lo - 10 };
1050 hex[i * 3 + 2] = b' ';
1051 if byte >= 0x20 && byte < 0x7F {
1052 asc[i] = byte;
1053 }
1054 }
1055 for i in ll..16 {
1056 hex[i * 3] = b' ';
1057 hex[i * 3 + 1] = b' ';
1058 hex[i * 3 + 2] = b' ';
1059 }
1060 crate::serial_println!(
1061 "{}{:#018x}: {} |{}|",
1062 prefix,
1063 line_va,
1064 core::str::from_utf8(&hex[..48]).unwrap_or("???"),
1065 core::str::from_utf8(&asc[..ll]).unwrap_or("???")
1066 );
1067 line_off += ll;
1068 }
1069 offset += chunk;
1070 }
1071}
1072
1073fn dump_page_table_walk(vaddr: u64, cr3_phys: u64) {
1075 let hhdm = crate::memory::hhdm_offset();
1076 let l4_idx = ((vaddr >> 39) & 0x1FF) as usize;
1077 let l3_idx = ((vaddr >> 30) & 0x1FF) as usize;
1078 let l2_idx = ((vaddr >> 21) & 0x1FF) as usize;
1079 let l1_idx = ((vaddr >> 12) & 0x1FF) as usize;
1080
1081 unsafe {
1083 let l4_ptr = (cr3_phys + hhdm) as *const u64;
1084 let l4e = *l4_ptr.add(l4_idx);
1085 let f = format_pte_flags(l4e);
1086 crate::serial_println!(
1087 " PML4[{:>3}] = {:#018x} phys={:#014x} [{}]",
1088 l4_idx,
1089 l4e,
1090 l4e & 0x000F_FFFF_FFFF_F000,
1091 core::str::from_utf8(&f).unwrap_or("?").trim()
1092 );
1093 if l4e & 1 == 0 {
1094 crate::serial_println!(" \x1b[1;31m ==> STOP: PML4 not present !\x1b[0m");
1095 return;
1096 }
1097
1098 let l3_ptr = ((l4e & 0x000F_FFFF_FFFF_F000) + hhdm) as *const u64;
1099 let l3e = *l3_ptr.add(l3_idx);
1100 let f = format_pte_flags(l3e);
1101 crate::serial_println!(
1102 " PDPT[{:>3}] = {:#018x} phys={:#014x} [{}]",
1103 l3_idx,
1104 l3e,
1105 l3e & 0x000F_FFFF_FFFF_F000,
1106 core::str::from_utf8(&f).unwrap_or("?").trim()
1107 );
1108 if l3e & 1 == 0 {
1109 crate::serial_println!(" \x1b[1;31m ==> STOP: PDPT not present !\x1b[0m");
1110 return;
1111 }
1112 if l3e & 0x80 != 0 {
1113 crate::serial_println!(
1114 " ==> 1 GiB huge page => phys {:#x}",
1115 l3e & 0x000F_FFFF_C000_0000
1116 );
1117 return;
1118 } let l2_ptr = ((l3e & 0x000F_FFFF_FFFF_F000) + hhdm) as *const u64;
1121 let l2e = *l2_ptr.add(l2_idx);
1122 let f = format_pte_flags(l2e);
1123 crate::serial_println!(
1124 " PD [{:>3}] = {:#018x} phys={:#014x} [{}]",
1125 l2_idx,
1126 l2e,
1127 l2e & 0x000F_FFFF_FFFF_F000,
1128 core::str::from_utf8(&f).unwrap_or("?").trim()
1129 );
1130 if l2e & 1 == 0 {
1131 crate::serial_println!(" \x1b[1;31m ==> STOP: PD not present !\x1b[0m");
1132 return;
1133 }
1134 if l2e & 0x80 != 0 {
1135 crate::serial_println!(
1136 " ==> 2 MiB huge page => phys {:#x}",
1137 l2e & 0x000F_FFFF_FFE0_0000
1138 );
1139 return;
1140 } let l1_ptr = ((l2e & 0x000F_FFFF_FFFF_F000) + hhdm) as *const u64;
1143 let l1e = *l1_ptr.add(l1_idx);
1144 let f = format_pte_flags(l1e);
1145 crate::serial_println!(
1146 " PT [{:>3}] = {:#018x} phys={:#014x} [{}]",
1147 l1_idx,
1148 l1e,
1149 l1e & 0x000F_FFFF_FFFF_F000,
1150 core::str::from_utf8(&f).unwrap_or("?").trim()
1151 );
1152 if l1e & 1 == 0 {
1153 crate::serial_println!(" \x1b[1;31m ==> STOP: PT not present !\x1b[0m");
1154 } else {
1155 crate::serial_println!(
1156 " \x1b[1;32m ==> PAGE PRESENT\x1b[0m => phys {:#x} (check RW/US/NX flags)",
1157 l1e & 0x000F_FFFF_FFFF_F000
1158 );
1159 }
1160 crate::serial_println!(" --- Neighbouring PT entries ---");
1162 let start = if l1_idx >= 2 { l1_idx - 2 } else { 0 };
1163 for i in start..core::cmp::min(l1_idx + 3, 512) {
1164 let e = *l1_ptr.add(i);
1165 if e != 0 {
1166 let f = format_pte_flags(e);
1167 crate::serial_println!(
1168 " PT[{:>3}] = {:#018x} [{}]{}",
1169 i,
1170 e,
1171 core::str::from_utf8(&f).unwrap_or("?").trim(),
1172 if i == l1_idx { " <<<" } else { "" }
1173 );
1174 }
1175 }
1176 }
1177}
1178
1179fn dump_nearby_vma_regions(as_ref: &crate::memory::AddressSpace, fault_vaddr: u64) {
1181 let page_start = fault_vaddr & !0xFFF;
1182 let probes = [
1183 page_start,
1184 fault_vaddr & !0x1F_FFFF,
1185 fault_vaddr & !0x3FFF_FFFF,
1186 0x0000_0001_0000_0000,
1187 0x0000_0000_0040_0000,
1188 0x0000_7FFF_F000_0000,
1189 ];
1190 let mut found_any = false;
1191 for &p in &probes {
1192 if let Some(vma) = as_ref.region_by_start(p) {
1193 let end = vma.start + (vma.page_count as u64) * vma.page_size.bytes();
1194 let hit = fault_vaddr >= vma.start && fault_vaddr < end;
1195 crate::serial_println!(
1196 " VMA {:#014x}..{:#014x} pages={:<5} type={:?} flags={:?} pgsz={:?}{}",
1197 vma.start,
1198 end,
1199 vma.page_count,
1200 vma.vma_type,
1201 vma.flags,
1202 vma.page_size,
1203 if hit {
1204 " \x1b[1;32m<<< FAULT\x1b[0m"
1205 } else {
1206 ""
1207 }
1208 );
1209 found_any = true;
1210 }
1211 }
1212 if as_ref.has_mapping_in_range(page_start, 0x1000) {
1213 crate::serial_println!(
1214 " Note: fault page {:#x} IS within a tracked mapping range",
1215 page_start
1216 );
1217 } else {
1218 crate::serial_println!(
1219 " Note: fault page {:#x} is NOT within any tracked mapping range",
1220 page_start
1221 );
1222 }
1223 if !found_any {
1224 crate::serial_println!(" (no VMA regions found at probed addresses)");
1225 }
1226}
1227
1228fn dump_page_fault_full(
1233 stack_frame: &InterruptStackFrame,
1234 error_code: PageFaultErrorCode,
1235 fault_addr: Result<x86_64::VirtAddr, x86_64::addr::VirtAddrNotValid>,
1236 task: &Option<alloc::sync::Arc<crate::process::task::Task>>,
1237) -> ! {
1238 use x86_64::registers::control::{Cr0, Cr3, Cr4};
1239
1240 let rip = stack_frame.instruction_pointer.as_u64();
1241 let rsp = stack_frame.stack_pointer.as_u64();
1242 let cs = stack_frame.code_segment.0;
1243 let ss = stack_frame.stack_segment.0;
1244 let rflags = stack_frame.cpu_flags.bits();
1245 let fault_vaddr = fault_addr.as_ref().map(|v| v.as_u64()).unwrap_or(0);
1246 let is_user = (cs & 3) == 3;
1247
1248 crate::serial_println!("\x1b[1;31m");
1249 crate::serial_println!(
1250 "****************************************************************************"
1251 );
1252 crate::serial_println!("* KERNEL PAGE FAULT EXCEPTiON *");
1253 crate::serial_println!(
1254 "********************************************************************\x1b[0m"
1255 );
1256
1257 crate::serial_println!("\x1b[1;33m--- Error code ---\x1b[0m");
1259 crate::serial_println!(" Raw : {:#06x}", error_code.bits());
1260 crate::serial_println!(
1261 " Diagnostic : \x1b[1;31m{}\x1b[0m",
1262 decode_error_code(error_code)
1263 );
1264 crate::serial_println!(
1265 " PRESENT : {} | WRITE : {} | USER : {} | RSVD : {} | FETCH : {}",
1266 error_code.contains(PageFaultErrorCode::PROTECTION_VIOLATION) as u8,
1267 error_code.contains(PageFaultErrorCode::CAUSED_BY_WRITE) as u8,
1268 error_code.contains(PageFaultErrorCode::USER_MODE) as u8,
1269 (error_code.bits() >> 3) & 1,
1270 (error_code.bits() >> 4) & 1
1271 );
1272
1273 crate::serial_println!("\x1b[1;33m--- Faulting context ---\x1b[0m");
1275 crate::serial_println!(" CR2 (addr) : \x1b[1;35m{:#018x}\x1b[0m", fault_vaddr);
1276 crate::serial_println!(" RIP : \x1b[1;36m{:#018x}\x1b[0m", rip);
1277 crate::serial_println!(" RSP : {:#018x}", rsp);
1278 crate::serial_println!(
1279 " CS : {:#06x} (ring={}{}) | SS : {:#06x}",
1280 cs,
1281 cs & 3,
1282 if is_user { " USER" } else { " KERNEL" },
1283 ss
1284 );
1285
1286 let mut rf_str = [0u8; 64];
1288 let mut rfp = 0usize;
1289 for &(name, bit) in &[
1290 ("CF", 1u64),
1291 ("PF", 4),
1292 ("AF", 16),
1293 ("ZF", 64),
1294 ("SF", 128),
1295 ("TF", 256),
1296 ("IF", 512),
1297 ("DF", 1024),
1298 ("OF", 2048),
1299 ] {
1300 if rflags & bit != 0 {
1301 for &b in name.as_bytes() {
1302 if rfp < rf_str.len() {
1303 rf_str[rfp] = b;
1304 rfp += 1;
1305 }
1306 }
1307 if rfp < rf_str.len() {
1308 rf_str[rfp] = b' ';
1309 rfp += 1;
1310 }
1311 }
1312 }
1313 crate::serial_println!(
1314 " RFLAGS : {:#018x} [{}]",
1315 rflags,
1316 core::str::from_utf8(&rf_str[..rfp]).unwrap_or("?")
1317 );
1318
1319 crate::serial_println!("\x1b[1;33m--- Control registers ---\x1b[0m");
1321 let cr0 = Cr0::read_raw();
1322 let (cr3_frame, cr3_flags) = Cr3::read();
1323 let cr3_phys = cr3_frame.start_address().as_u64();
1324 let cr4 = Cr4::read_raw();
1325 let efer: u64 = x86_64::registers::model_specific::Efer::read_raw();
1326 crate::serial_println!(" CR0 : {:#018x}", cr0);
1327 crate::serial_println!(
1328 " CR3 : {:#018x} (flags={:#x})",
1329 cr3_phys,
1330 cr3_flags.bits()
1331 );
1332 crate::serial_println!(" CR4 : {:#018x}", cr4);
1333 crate::serial_println!(
1334 " EFER : {:#018x} [{}{}{}]",
1335 efer,
1336 if efer & 1 != 0 { "SCE " } else { "" },
1337 if efer & (1 << 8) != 0 { "LME " } else { "" },
1338 if efer & (1 << 11) != 0 { "NXE" } else { "" }
1339 );
1340
1341 crate::serial_println!("\x1b[1;33m--- CPU context ---\x1b[0m");
1343 crate::serial_println!(" LAPIC ID : {}", super::apic::lapic_id());
1344 crate::serial_println!(" Ticks sched : {}", crate::process::scheduler::ticks());
1345 crate::serial_println!(" HHDM offset : {:#x}", crate::memory::hhdm_offset());
1346
1347 crate::serial_println!("\x1b[1;33m--- Task context ---\x1b[0m");
1349 if let Some(ref t) = *task {
1350 crate::serial_println!(
1351 " ID={} PID={} TID={} TGID={} name=\"{}\" prio={:?} ticks={}",
1352 t.id.as_u64(),
1353 t.pid,
1354 t.tid,
1355 t.tgid,
1356 t.name,
1357 t.priority,
1358 t.ticks.load(core::sync::atomic::Ordering::Relaxed)
1359 );
1360 let task_cr3: u64 = {
1373 let hhdm = crate::memory::hhdm_offset();
1374 let _proc_ptr: u64 = alloc::sync::Arc::as_ptr(&t.process) as u64;
1376 let as_cell_addr: u64 =
1381 unsafe { (*alloc::sync::Arc::as_ptr(&t.process)).address_space.get() as u64 };
1382 let as_inner_u64: u64 = match translate_via_raw_pt(as_cell_addr, cr3_phys, hhdm) {
1385 Some(phys) => unsafe { *((phys + hhdm) as *const u64) },
1386 None => 0,
1387 };
1388 if as_inner_u64 == 0 {
1389 0u64
1390 } else {
1391 let as_data_ptr: u64 = as_inner_u64 + 2 * core::mem::size_of::<usize>() as u64;
1396 match translate_via_raw_pt(as_data_ptr, cr3_phys, hhdm) {
1398 Some(phys) => unsafe { *((phys + hhdm) as *const u64) },
1399 None => 0,
1400 }
1401 }
1402 };
1403 if task_cr3 == 0 {
1404 crate::serial_println!(
1405 " Task CR3 : <unreadable : null/unmapped Arc<AddressSpace>>"
1406 );
1407 } else {
1408 crate::serial_println!(
1409 " Task CR3 : {:#018x}{}",
1410 task_cr3,
1411 if task_cr3 != cr3_phys {
1412 " *** DIFFERS from hardware CR3! ***"
1413 } else {
1414 " (matches hardware CR3)"
1415 }
1416 );
1417 }
1418 } else {
1419 crate::serial_println!(" (no current task : scheduler idle or unavailable)");
1420 }
1421
1422 crate::serial_println!("\x1b[1;33m--- Memory stats ---\x1b[0m");
1424 if let Some(guard) = crate::memory::get_allocator().try_lock() {
1425 if let Some(ref alloc) = *guard {
1426 let (total, allocated) = alloc.page_totals();
1427 let free = total.saturating_sub(allocated);
1428 crate::serial_println!(
1429 " Total={} pages ({} MiB) Alloc={} ({} MiB) Free={} ({} MiB)",
1430 total,
1431 total * 4 / 1024,
1432 allocated,
1433 allocated * 4 / 1024,
1434 free,
1435 free * 4 / 1024
1436 );
1437 let mut zones =
1438 [crate::memory::buddy::ZoneStats::empty(); crate::memory::zone::ZoneType::COUNT];
1439 let n = alloc.zone_snapshot(&mut zones);
1440 for i in 0..n {
1441 let zone = zones[i];
1442 let zone_ref = alloc.get_zone(i);
1443 crate::serial_println!(
1444 " Zone {} ({}): base={:#x} managed={} present={} spanned={} reserved={} alloc={} free={} state={:?} seg={}/{} largest={:?}",
1445 i,
1446 match zone.zone_type {
1447 crate::memory::zone::ZoneType::DMA => "DMA",
1448 crate::memory::zone::ZoneType::Normal => "Normal",
1449 crate::memory::zone::ZoneType::HighMem => "High",
1450 },
1451 zone.base,
1452 zone.managed_pages,
1453 zone.present_pages,
1454 zone.spanned_pages,
1455 zone.reserved_pages,
1456 zone.allocated_pages,
1457 zone.free_pages,
1458 zone.pressure(),
1459 zone.segment_count,
1460 zone.segment_capacity,
1461 zone.largest_free_order
1462 );
1463 crate::serial_println!(
1464 " reserve={} avail={} holes={} cached[u/m]={}/{} free[u/m]={}/{} pageblocks[u/m]={}/{} total={} order={}",
1465 zone.reserve_floor_pages(),
1466 zone.available_after_reserve_pages(),
1467 zone.hole_pages(),
1468 zone.cached_unmovable_pages,
1469 zone.cached_movable_pages,
1470 zone.unmovable_free_pages,
1471 zone.movable_free_pages,
1472 zone.unmovable_pageblocks,
1473 zone.movable_pageblocks,
1474 zone.pageblock_count,
1475 crate::memory::zone::PAGEBLOCK_ORDER
1476 );
1477 crate::serial_println!(
1478 " frag/order: o1={}%% o4={}%% o{}={}%%",
1479 zone_ref.fragmentation_score(1, zone.cached_pages),
1480 zone_ref.fragmentation_score(4, zone.cached_pages),
1481 crate::memory::zone::PAGEBLOCK_ORDER,
1482 zone_ref.fragmentation_score(
1483 crate::memory::zone::PAGEBLOCK_ORDER as u8,
1484 zone.cached_pages,
1485 )
1486 );
1487 }
1488 } else {
1489 crate::serial_println!(" (allocator not initialized)");
1490 }
1491 } else {
1492 crate::serial_println!(" (allocator lock contended : skipping)");
1493 }
1494
1495 let quarantine = crate::memory::buddy::poison_quarantine_pages_snapshot();
1496 let fail_counts = crate::memory::buddy::buddy_alloc_fail_counts_snapshot();
1497 let compaction = crate::memory::buddy::compaction_stats_snapshot();
1498 crate::serial_println!(" Poison quarantine : {} pages", quarantine);
1499
1500 let mut printed_fail = false;
1501 for (order, count) in fail_counts.iter().enumerate() {
1502 if *count == 0 {
1503 continue;
1504 }
1505 printed_fail = true;
1506 crate::serial_println!(" Buddy alloc fail : order={} count={}", order, count);
1507 }
1508 if !printed_fail {
1509 crate::serial_println!(" Buddy alloc fail : none");
1510 }
1511
1512 if compaction.attempts == 0 {
1513 crate::serial_println!(" Compaction assist : none");
1514 } else {
1515 crate::serial_println!(
1516 " Compaction assist : attempts={} success={} last_order={:?} migratetype={:?} zone={:?} pressure={:?}",
1517 compaction.attempts,
1518 compaction.successes,
1519 compaction.last_order,
1520 compaction.last_migratetype,
1521 compaction.last_zone,
1522 compaction.last_pressure
1523 );
1524 crate::serial_println!(
1525 " frag={}%% req={} avail={} usable={} cached={} drained={} pageblocks={}/{}",
1526 compaction.last_fragmentation_score,
1527 compaction.last_requested_pages,
1528 compaction.last_available_pages,
1529 compaction.last_usable_pages,
1530 compaction.last_cached_pages,
1531 compaction.last_drained_pages,
1532 compaction.last_matching_pageblocks,
1533 compaction.last_pageblock_count
1534 );
1535 }
1536
1537 crate::serial_println!("\x1b[1;33m--- Code at RIP ({:#x}) ---\x1b[0m", rip);
1539 dump_memory_bytes(rip, cr3_phys, 32, " ");
1540
1541 crate::serial_println!("\x1b[1;33m--- Stack dump (RSP={:#x}) ---\x1b[0m", rsp);
1543 dump_memory_bytes(rsp, cr3_phys, 128, " ");
1544
1545 crate::serial_println!(
1547 "\x1b[1;33m--- Page table walk (CR2={:#x}, CR3={:#x}) ---\x1b[0m",
1548 fault_vaddr,
1549 cr3_phys
1550 );
1551 if fault_addr.is_ok() {
1552 dump_page_table_walk(fault_vaddr, cr3_phys);
1553 } else {
1554 crate::serial_println!(" (CR2 is a non-canonical address: {:#x})", fault_vaddr);
1555 }
1556
1557 if let Some(ref t) = *task {
1559 crate::serial_println!("\x1b[1;33m--- VMA regions near fault ---\x1b[0m");
1560 let hhdm_vma = crate::memory::hhdm_offset();
1565 let safe_as: Option<*const crate::memory::AddressSpace> = unsafe {
1566 let as_cell_addr: u64 =
1567 (*alloc::sync::Arc::as_ptr(&t.process)).address_space.get() as u64;
1568 match translate_via_raw_pt(as_cell_addr, cr3_phys, hhdm_vma) {
1569 Some(phys) => {
1570 let as_inner_u64 = *((phys + hhdm_vma) as *const u64);
1572 if as_inner_u64 == 0 {
1573 None
1574 } else {
1575 let as_data_ptr = (as_inner_u64 + 2 * core::mem::size_of::<usize>() as u64)
1577 as *const crate::memory::AddressSpace;
1578 if translate_via_raw_pt(as_data_ptr as u64, cr3_phys, hhdm_vma).is_some() {
1580 Some(as_data_ptr)
1581 } else {
1582 None
1583 }
1584 }
1585 }
1586 None => None,
1587 }
1588 };
1589 if let Some(as_ptr) = safe_as {
1590 let as_ref = unsafe { &*as_ptr };
1592 dump_nearby_vma_regions(as_ref, fault_vaddr);
1593 } else {
1594 crate::serial_println!(" (AddressSpace unreadable : skipping VMA dump)");
1595 }
1596 }
1597
1598 crate::serial_println!("\x1b[1;31m***********************************************************");
1599 crate::serial_println!("* END OF PAGE FAULT DuMP *");
1600 crate::serial_println!(
1601 "*******************************************************************\x1b[0m"
1602 );
1603
1604 panic!(
1605 "PAGE FAULT: {} at {:#x}, RIP={:#x}, CR3={:#x}, err={:#x}",
1606 decode_error_code(error_code),
1607 fault_vaddr,
1608 rip,
1609 cr3_phys,
1610 error_code.bits()
1611 );
1612}
1613
1614fn dump_user_pf_context(as_ref: &crate::memory::AddressSpace, rip: u64, rsp: u64) {
1616 use x86_64::VirtAddr;
1617
1618 let hhdm = crate::memory::hhdm_offset();
1619
1620 if let Some(phys) = as_ref.translate(VirtAddr::new(rip)) {
1621 let off = (rip & 0xfff) as usize;
1622 let mut bytes = [0u8; 8];
1623 unsafe {
1625 let src = (phys.as_u64() - (rip & 0xfff) + hhdm + off as u64) as *const u8;
1626 core::ptr::copy_nonoverlapping(src, bytes.as_mut_ptr(), bytes.len());
1627 }
1628 crate::serial_println!(
1629 "[pagefault] ctx: rsp={:#x} rip-bytes={:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",
1630 rsp,
1631 bytes[0],
1632 bytes[1],
1633 bytes[2],
1634 bytes[3],
1635 bytes[4],
1636 bytes[5],
1637 bytes[6],
1638 bytes[7],
1639 );
1640 } else {
1641 crate::serial_println!("[pagefault] ctx: rsp={:#x} rip page unmapped", rsp);
1642 }
1643
1644 if let Some(phys) = as_ref.translate(VirtAddr::new(rsp)) {
1645 crate::serial_println!(
1646 "[pagefault] stack-top: rsp mapped (phys={:#x})",
1647 phys.as_u64()
1648 );
1649 } else {
1650 crate::serial_println!("[pagefault] stack-top: rsp unmapped");
1651 }
1652}
1653
1654extern "x86-interrupt" fn general_protection_fault_handler(
1656 stack_frame: InterruptStackFrame,
1657 error_code: u64,
1658) {
1659 let cs = stack_frame.code_segment.0;
1660 let is_user = (cs & 3) == 3;
1661 let swapgs_needed = needs_swapgs(cs);
1665 let _gs = SwapGsGuard::new(swapgs_needed);
1666 if swapgs_needed && !is_user {
1668 crate::serial_force_println!(
1669 "\x1b[31;1m[GPF]\x1b[0m SWAPGS-WINDOW: CS={:#x} (Ring0) but GS was user! rip={:#x} err={:#x} rsp={:#x}",
1670 cs,
1671 stack_frame.instruction_pointer.as_u64(),
1672 error_code,
1673 stack_frame.stack_pointer.as_u64()
1674 );
1675 if let Some(task) = crate::process::current_task_clone() {
1677 crate::process::kill_task(task.id);
1678 }
1679 drop(_gs);
1680 crate::process::scheduler::exit_current_task(-11); }
1682 if is_user {
1683 if let Some(tid) = crate::process::current_task_id() {
1684 crate::serial_force_println!(
1685 "\x1b[31;1m[GPF]\x1b[0m USER tid={} rip={:#x} err={:#x}",
1686 tid,
1687 stack_frame.instruction_pointer.as_u64(),
1688 error_code
1689 );
1690 crate::silo::handle_user_fault(
1691 tid,
1692 crate::silo::SiloFaultReason::GeneralProtection,
1693 stack_frame.instruction_pointer.as_u64(),
1694 error_code,
1695 stack_frame.instruction_pointer.as_u64(),
1696 );
1697 return;
1698 }
1699 }
1700 crate::serial_force_println!(
1701 "\x1b[31;1m[GPF]\x1b[0m KERNEL rip={:#x} err={:#x} cs={:#x} rsp={:#x}",
1702 stack_frame.instruction_pointer.as_u64(),
1703 error_code,
1704 stack_frame.code_segment.0,
1705 stack_frame.stack_pointer.as_u64()
1706 );
1707 panic!("General protection fault");
1708}
1709
1710extern "x86-interrupt" fn stack_segment_fault_handler(
1712 stack_frame: InterruptStackFrame,
1713 error_code: u64,
1714) {
1715 let _gs = SwapGsGuard::new(needs_swapgs(stack_frame.code_segment.0));
1718 crate::serial_force_println!(
1719 "\x1b[31;1m[STACK_FAULT]\x1b[0m rip={:#x} err={:#x} cs={:#x} rsp={:#x}",
1720 stack_frame.instruction_pointer.as_u64(),
1721 error_code,
1722 stack_frame.code_segment.0,
1723 stack_frame.stack_pointer.as_u64()
1724 );
1725 panic!("Stack segment fault");
1726}
1727
1728extern "x86-interrupt" fn double_fault_handler(
1739 stack_frame: InterruptStackFrame,
1740 error_code: u64,
1741) -> ! {
1742 unsafe {
1752 let lo: u32;
1753 let hi: u32;
1754 core::arch::asm!(
1755 "rdmsr",
1756 in("ecx") 0xC000_0101u32, out("eax") lo,
1758 out("edx") hi,
1759 options(nostack, preserves_flags),
1760 );
1761 let gs_base = (lo as u64) | ((hi as u64) << 32);
1762 if gs_base < 0xFFFF_8000_0000_0000 {
1764 core::arch::asm!("swapgs", options(nostack, preserves_flags));
1765 }
1766 }
1767 crate::serial_force_println!(
1768 "\x1b[31;1m[DOUBLE_FAULT]\x1b[0m rip={:#x} err={:#x} cs={:#x} rsp={:#x}",
1769 stack_frame.instruction_pointer.as_u64(),
1770 error_code,
1771 stack_frame.code_segment.0,
1772 stack_frame.stack_pointer.as_u64()
1773 );
1774 panic!(
1775 "EXCEPTION: DOUBLE FAULT (error code: {:#x})\n{:#?}",
1776 error_code, stack_frame
1777 );
1778}
1779
1780extern "x86-interrupt" fn legacy_timer_handler(stack_frame: InterruptStackFrame) {
1788 let _gs = SwapGsGuard::new((stack_frame.code_segment.0 & 3) == 3);
1790 if crate::arch::x86_64::timer::is_apic_timer_active() {
1791 if super::apic::is_initialized() {
1793 super::apic::eoi();
1794 } else {
1795 pic::end_of_interrupt(0);
1796 }
1797 return;
1798 }
1799
1800 let ticks = crate::process::scheduler::ticks();
1803 if ticks < 10 || ticks % 100 == 0 {
1804 crate::serial_force_println!("[heartbeat] PIC timer tick={}", ticks);
1805 }
1806
1807 crate::process::scheduler::timer_tick();
1809 if super::apic::is_initialized() {
1814 super::apic::eoi();
1815 } else {
1816 pic::end_of_interrupt(0);
1817 }
1818
1819 let cpl = stack_frame.code_segment.0 & 3;
1824 if cpl == 3 {
1825 let cpu = crate::arch::x86_64::percpu::current_cpu_index();
1826 crate::process::scheduler::request_force_resched_hint(cpu);
1827 } else {
1828 crate::process::scheduler::maybe_preempt();
1829 }
1830 if ticks < 10 {
1831 crate::serial_force_println!("[heartbeat] PIC timer tick={} preempt_done", ticks);
1832 }
1833}
1834
1835extern "x86-interrupt" fn lapic_timer_handler(stack_frame: InterruptStackFrame) {
1837 let cs = stack_frame.code_segment.0;
1839 let _gs = SwapGsGuard::new((cs & 3) == 3);
1840 let cpu = crate::arch::x86_64::percpu::current_cpu_index();
1841 let ticks = crate::process::scheduler::ticks();
1842 if ticks < 10 || ticks % 100 == 0 {
1845 crate::serial_force_println!(
1846 "[heartbeat] APIC timer tick={} cpu={} cs={:#x} rip={:#x}",
1847 ticks,
1848 cpu,
1849 cs,
1850 stack_frame.instruction_pointer.as_u64()
1851 );
1852 }
1853
1854 unsafe { core::arch::asm!("mov al, '0'; out 0xe9, al", out("al") _) };
1859 crate::process::scheduler::timer_tick();
1860 unsafe { core::arch::asm!("mov al, '1'; out 0xe9, al", out("al") _) };
1861 super::apic::eoi();
1862 if (cs & 3) == 3 {
1874 crate::process::scheduler::request_force_resched_hint(cpu);
1875 unsafe { core::arch::asm!("mov al, 'P'; out 0xe9, al", out("al") _) };
1876 } else {
1877 crate::process::scheduler::maybe_preempt();
1878 }
1879}
1880
1881extern "x86-interrupt" fn mouse_handler(_stack_frame: InterruptStackFrame) {
1883 crate::arch::x86_64::mouse::handle_irq();
1884 pic::end_of_interrupt(12);
1888}
1889
1890extern "x86-interrupt" fn keyboard_handler(_stack_frame: InterruptStackFrame) {
1892 let raw = unsafe { super::io::inb(0x60) };
1893
1894 crate::entropy::add_entropy(1, (raw as u64) ^ super::rdtsc());
1896
1897 if let Some(ch) = super::keyboard_layout::handle_scancode_raw(raw) {
1898 crate::arch::x86_64::keyboard::add_to_buffer(ch);
1899 }
1900
1901 pic::end_of_interrupt(1);
1905}
1906
1907extern "x86-interrupt" fn spurious_handler(_stack_frame: InterruptStackFrame) {
1910 }
1912
1913extern "x86-interrupt" fn ahci_handler(_stack_frame: InterruptStackFrame) {
1918 crate::entropy::add_entropy(3, super::rdtsc());
1920
1921 crate::hardware::storage::ahci::handle_interrupt();
1922
1923 if super::apic::is_initialized() {
1924 super::apic::eoi();
1925 } else {
1926 let irq = crate::hardware::storage::ahci::AHCI_IRQ_LINE
1927 .load(core::sync::atomic::Ordering::Relaxed);
1928 pic::end_of_interrupt(irq);
1929 }
1930}
1931
1932extern "x86-interrupt" fn nvme_handler(_stack_frame: InterruptStackFrame) {
1936 crate::entropy::add_entropy(3, super::rdtsc());
1937
1938 crate::hardware::storage::nvme::handle_interrupt();
1939
1940 if super::apic::is_initialized() {
1941 super::apic::eoi();
1942 } else {
1943 let irq = crate::hardware::storage::nvme::NVME_IRQ_LINE
1944 .load(core::sync::atomic::Ordering::Relaxed);
1945 pic::end_of_interrupt(irq);
1946 }
1947}
1948
1949extern "x86-interrupt" fn virtio_block_handler(_stack_frame: InterruptStackFrame) {
1954 crate::hardware::storage::virtio_block::handle_interrupt();
1956
1957 if super::apic::is_initialized() {
1959 super::apic::eoi();
1960 } else {
1961 let irq = crate::hardware::storage::virtio_block::get_irq();
1963 pic::end_of_interrupt(irq);
1964 }
1965}
1966
1967extern "x86-interrupt" fn xhci_handler(_stack_frame: InterruptStackFrame) {
1972 crate::hardware::usb::xhci::handle_interrupt();
1973
1974 if super::apic::is_initialized() {
1975 super::apic::eoi();
1976 } else {
1977 let irq =
1978 crate::hardware::usb::xhci::XHCI_IRQ_LINE.load(core::sync::atomic::Ordering::Relaxed);
1979 pic::end_of_interrupt(irq);
1980 }
1981}
1982
1983extern "x86-interrupt" fn nic_handler(stack_frame: InterruptStackFrame) {
1990 let _gs = SwapGsGuard::new((stack_frame.code_segment.0 & 3) == 3);
1994
1995 crate::hardware::nic::handle_interrupt();
1996
1997 if super::apic::is_initialized() {
1998 super::apic::eoi();
1999 } else {
2000 let irq = crate::hardware::nic::NIC_IRQ_LINE.load(core::sync::atomic::Ordering::Relaxed);
2001 pic::end_of_interrupt(irq);
2002 }
2003}
2004
2005extern "x86-interrupt" fn resched_ipi_handler(stack_frame: InterruptStackFrame) {
2015 let _gs = SwapGsGuard::new((stack_frame.code_segment.0 & 3) == 3);
2017 super::apic::eoi();
2018 crate::process::scheduler::maybe_preempt();
2019}
2020
2021extern "x86-interrupt" fn tlb_shootdown_handler(stack_frame: InterruptStackFrame) {
2023 let _gs = SwapGsGuard::new((stack_frame.code_segment.0 & 3) == 3);
2025 super::tlb::tlb_shootdown_ipi_handler();
2027}