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);
27
28#[repr(C, packed)]
29struct Idtr {
30 limit: u16,
31 base: u64,
32}
33
34#[derive(Clone, Copy, Debug)]
35pub struct LiveIdtGateInfo {
36 pub vector: u8,
37 pub selector: u16,
38 pub options: u16,
39 pub offset: u64,
40}
41
42#[allow(dead_code)]
44pub mod irq {
45 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; }
55
56struct SwapGsGuard {
72 from_ring3: bool,
73}
74
75impl SwapGsGuard {
76 #[inline(always)]
79 fn new(from_ring3: bool) -> Self {
80 if from_ring3 {
81 unsafe { core::arch::asm!("swapgs", options(nostack, preserves_flags)) };
85 }
86 Self { from_ring3 }
87 }
88}
89
90impl Drop for SwapGsGuard {
91 #[inline(always)]
92 fn drop(&mut self) {
93 if self.from_ring3 {
94 unsafe { core::arch::asm!("swapgs", options(nostack, preserves_flags)) };
97 }
98 }
99}
100
101#[inline(always)]
114fn needs_swapgs(cs: u16) -> bool {
115 if (cs & 3) == 3 {
117 return true;
118 }
119 let gs_base: u64 = unsafe {
122 let lo: u32;
123 let hi: u32;
124 core::arch::asm!(
125 "rdmsr",
126 in("ecx") 0xC000_0101u32, out("eax") lo,
128 out("edx") hi,
129 options(nostack, preserves_flags),
130 );
131 (lo as u64) | ((hi as u64) << 32)
132 };
133 gs_base < 0xFFFF_8000_0000_0000
134}
135
136#[repr(C)]
140struct IdtStorage {
141 lock: AtomicBool,
142 idt: InterruptDescriptorTable,
143}
144
145static mut IDT_STORAGEWrapper: IdtStorage = IdtStorage {
146 lock: AtomicBool::new(false),
147 idt: InterruptDescriptorTable::new(),
148};
149static USER_PF_TRACE_BUDGET: AtomicU32 = AtomicU32::new(64);
150static RESCHED_IPI_TRACE_BUDGET: AtomicU32 = AtomicU32::new(32);
151
152pub fn live_gate_info(vector: u8) -> Option<LiveIdtGateInfo> {
153 let mut idtr = Idtr { limit: 0, base: 0 };
154 unsafe {
156 core::arch::asm!(
157 "sidt [{}]",
158 in(reg) &mut idtr,
159 options(nostack, preserves_flags),
160 );
161 }
162
163 let entry_offset = vector as usize * 16;
164 if entry_offset + 16 > idtr.limit as usize + 1 {
165 return None;
166 }
167
168 let (low, high) = unsafe {
170 let entry_ptr = (idtr.base + entry_offset as u64) as *const u64;
171 (
172 core::ptr::read_unaligned(entry_ptr),
173 core::ptr::read_unaligned(entry_ptr.add(1)),
174 )
175 };
176
177 let offset = (low & 0xFFFF) | (((low >> 48) & 0xFFFF) << 16) | ((high & 0xFFFF_FFFF) << 32);
178 let selector = ((low >> 16) & 0xFFFF) as u16;
179 let options = ((low >> 32) & 0xFFFF) as u16;
180
181 Some(LiveIdtGateInfo {
182 vector,
183 selector,
184 options,
185 offset,
186 })
187}
188
189#[repr(C)]
196#[derive(Clone, Copy, Debug, Default)]
197pub struct InterruptReturnDecision {
198 pub next_rsp: u64,
199 pub old_fpu: *mut u8,
200 pub new_fpu: *const u8,
201}
202
203const _: () = {
204 assert!(core::mem::size_of::<InterruptReturnDecision>() == 24);
205 assert!(core::mem::align_of::<InterruptReturnDecision>() == 8);
206};
207
208#[unsafe(naked)]
215unsafe extern "C" fn lapic_timer_entry() -> ! {
216 core::arch::naked_asm!(
217 "cld",
218 "test qword ptr [rsp + 8], 0x3",
226 "jz 2f",
227 "swapgs",
228 "2:",
229 "push rax",
231 "push rcx",
232 "push rdx",
233 "push rdi",
234 "push rsi",
235 "push r8",
236 "push r9",
237 "push r10",
238 "push r11",
239 "push rbx",
240 "push rbp",
241 "push r12",
242 "push r13",
243 "push r14",
244 "push r15",
245 "sub rsp, 32",
248 "mov rdi, rsp",
249 "lea rsi, [rsp + 32]",
250 "call {inner}",
251 "mov rax, [rsp + 0]",
253 "mov rdx, [rsp + 8]",
254 "mov rcx, [rsp + 16]",
255 "add rsp, 32",
256 "test rax, rax",
257 "jz 3f",
258 "fxsave [rdx]",
260 "mov rsp, rax",
261 "fxrstor [rcx]",
262 "call {switch_finish}",
263 "3:",
264 "pop r15",
268 "pop r14",
269 "pop r13",
270 "pop r12",
271 "pop rbp",
272 "pop rbx",
273 "pop r11",
274 "pop r10",
275 "pop r9",
276 "pop r8",
277 "pop rsi",
278 "pop rdi",
279 "pop rdx",
280 "pop rcx",
281 "pop rax",
282 "test qword ptr [rsp + 8], 0x3",
284 "jz 4f",
285 "swapgs",
286 "4:",
287 "iretq",
288 inner = sym lapic_timer_inner,
289 switch_finish = sym crate::process::scheduler::finish_interrupt_switch,
290 );
291}
292
293#[unsafe(naked)]
298unsafe extern "C" fn resched_ipi_entry() -> ! {
299 core::arch::naked_asm!(
300 "cld",
301 "test qword ptr [rsp + 8], 0x3",
302 "jz 2f",
303 "swapgs",
304 "2:",
305 "push rax",
306 "push rcx",
307 "push rdx",
308 "push rdi",
309 "push rsi",
310 "push r8",
311 "push r9",
312 "push r10",
313 "push r11",
314 "push rbx",
315 "push rbp",
316 "push r12",
317 "push r13",
318 "push r14",
319 "push r15",
320 "sub rsp, 32",
321 "mov rdi, rsp",
322 "lea rsi, [rsp + 32]",
323 "call {inner}",
324 "mov rax, [rsp + 0]",
325 "mov rdx, [rsp + 8]",
326 "mov rcx, [rsp + 16]",
327 "add rsp, 32",
328 "test rax, rax",
329 "jz 3f",
330 "push rax",
333 "mov al, 0x21", "out 0xe9, al",
335 "pop rax",
336 "jmp 3f",
337 "3:",
338 "pop r15",
339 "pop r14",
340 "pop r13",
341 "pop r12",
342 "pop rbp",
343 "pop rbx",
344 "pop r11",
345 "pop r10",
346 "pop r9",
347 "pop r8",
348 "pop rsi",
349 "pop rdi",
350 "pop rdx",
351 "pop rcx",
352 "pop rax",
353 "test qword ptr [rsp + 8], 0x3",
354 "jz 4f",
355 "swapgs",
356 "4:",
357 "iretq",
358 inner = sym resched_ipi_inner,
359 );
360}
361
362extern "C" fn lapic_timer_inner(
363 frame: &mut crate::syscall::SyscallFrame,
364) -> InterruptReturnDecision {
365 let cpu = crate::arch::x86_64::percpu::current_cpu_index();
366 let ticks = crate::process::scheduler::ticks();
367 let from_ring3 = (frame.iret_cs & 3) == 3;
369 if from_ring3 && (ticks < 5 || ticks % 100 == 0) {
370 unsafe { core::arch::asm!("mov al, 0x48; out 0xe9, al", out("al") _) } }
372 crate::process::scheduler::timer_tick();
373 crate::arch::x86_64::speaker::speaker_tick();
374 crate::ipc::n3::n3_watchdog_tick();
376 super::apic::eoi();
377
378 if from_ring3 {
384 crate::process::signal::deliver_pending_signal_on_interrupt_return(frame);
385 }
386
387 if from_ring3 {
403 if let Some(decision) = crate::process::scheduler::maybe_preempt_from_interrupt(cpu, frame)
404 {
405 if decision.next_rsp != 0 {
406 unsafe {
408 let base = decision.next_rsp;
409 let iret_rip = *(base as *const u64).add(15); let iret_rsp = *(base as *const u64).add(18); let hex = b"0123456789abcdef";
412 core::arch::asm!("out 0xe9, al", in("al") b'@', options(nomem, nostack));
413 core::arch::asm!("out 0xe9, al", in("al") b'R', options(nomem, nostack));
414 for sh in [28usize, 24, 20, 16, 12, 8, 4, 0] {
415 let nib = hex[((iret_rip >> sh) & 0xF) as usize];
416 core::arch::asm!("out 0xe9, al", in("al") nib, options(nomem, nostack));
417 }
418 core::arch::asm!("out 0xe9, al", in("al") b'/', options(nomem, nostack));
419 for sh in [28usize, 24, 20, 16, 12, 8, 4, 0] {
420 let nib = hex[((iret_rsp >> sh) & 0xF) as usize];
421 core::arch::asm!("out 0xe9, al", in("al") nib, options(nomem, nostack));
422 }
423 core::arch::asm!("out 0xe9, al", in("al") b'\n', options(nomem, nostack));
424 }
425 return decision;
426 }
427 }
428 }
429 crate::process::scheduler::request_force_resched_hint(cpu);
430 InterruptReturnDecision::default()
431}
432
433#[inline(never)]
434extern "C" fn resched_ipi_inner(
435 frame: &mut crate::syscall::SyscallFrame,
436) -> InterruptReturnDecision {
437 let cpu = crate::arch::x86_64::percpu::current_cpu_index();
438 let should_trace = RESCHED_IPI_TRACE_BUDGET
439 .try_update(Ordering::AcqRel, Ordering::Relaxed, |budget| {
440 budget.checked_sub(1)
441 })
442 .is_ok();
443 if should_trace {
444 let rsp0 = crate::arch::x86_64::tss::kernel_stack_for(cpu)
445 .map(|addr| addr.as_u64())
446 .unwrap_or(0);
447 let (slot_rip, slot_cs, slot_rsp, slot_ss) = if rsp0 >= 40 {
448 unsafe {
452 let frame_base = (rsp0 - 40) as *const u64;
453 (
454 *frame_base.add(0),
455 *frame_base.add(1),
456 *frame_base.add(3),
457 *frame_base.add(4),
458 )
459 }
460 } else {
461 (0, 0, 0, 0)
462 };
463 crate::e9_println!(
464 "[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}",
465 cpu,
466 rsp0,
467 slot_rip,
468 slot_cs,
469 slot_rsp,
470 slot_ss,
471 frame.iret_rip,
472 frame.iret_cs,
473 frame.iret_rsp,
474 frame.iret_ss,
475 );
476 }
477 super::apic::eoi();
478 crate::process::scheduler::request_force_resched_hint(cpu);
479 InterruptReturnDecision::default()
480}
481
482#[inline]
483fn lock_idt_storage() {
484 while unsafe { &IDT_STORAGEWrapper.lock }
487 .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
488 .is_err()
489 {
490 core::hint::spin_loop();
491 }
492}
493
494#[inline]
495fn unlock_idt_storage() {
496 unsafe { &IDT_STORAGEWrapper.lock }.store(false, Ordering::Release);
498}
499
500pub fn init() {
501 lock_idt_storage();
502 unsafe {
503 let idt = &raw mut IDT_STORAGEWrapper.idt;
504
505 crate::e9_println!("IDT BP");
507 (*idt)
508 .breakpoint
509 .set_handler_fn(breakpoint_handler)
510 .set_code_selector(KERNEL_CODE_SELECTOR);
511 crate::e9_println!("IDT PF");
512 (*idt)
513 .page_fault
514 .set_handler_fn(page_fault_handler)
515 .set_code_selector(KERNEL_CODE_SELECTOR);
516 crate::e9_println!("IDT GP");
517 (*idt)
518 .general_protection_fault
519 .set_handler_fn(general_protection_fault_handler)
520 .set_code_selector(KERNEL_CODE_SELECTOR);
521 (*idt)
522 .stack_segment_fault
523 .set_handler_fn(stack_segment_fault_handler)
524 .set_code_selector(KERNEL_CODE_SELECTOR);
525 (*idt)
526 .non_maskable_interrupt
527 .set_handler_fn(non_maskable_interrupt_handler)
528 .set_code_selector(KERNEL_CODE_SELECTOR);
529 (*idt)
530 .invalid_opcode
531 .set_handler_fn(invalid_opcode_handler)
532 .set_code_selector(KERNEL_CODE_SELECTOR);
533 crate::e9_println!("IDT DF");
534 (*idt)
535 .double_fault
536 .set_handler_fn(double_fault_handler)
537 .set_code_selector(KERNEL_CODE_SELECTOR)
538 .set_stack_index(tss::DOUBLE_FAULT_IST_INDEX);
539
540 let idt_ref = &mut *idt;
542 idt_ref[irq::TIMER as u8]
543 .set_handler_fn(legacy_timer_handler)
544 .set_code_selector(KERNEL_CODE_SELECTOR);
545 idt_ref[irq::KEYBOARD as u8]
546 .set_handler_fn(keyboard_handler)
547 .set_code_selector(KERNEL_CODE_SELECTOR);
548 idt_ref[irq::MOUSE as u8]
549 .set_handler_fn(mouse_handler)
550 .set_code_selector(KERNEL_CODE_SELECTOR);
551
552 idt_ref[0xFF_u8]
554 .set_handler_fn(spurious_handler)
555 .set_code_selector(KERNEL_CODE_SELECTOR);
556
557 idt_ref[super::apic::IPI_RESCHED_VECTOR as u8]
560 .set_handler_addr(VirtAddr::from_ptr(resched_ipi_entry as *const ()))
561 .set_code_selector(KERNEL_CODE_SELECTOR);
562
563 idt_ref[super::apic::IPI_TLB_SHOOTDOWN_VECTOR as u8]
565 .set_handler_fn(tlb_shootdown_handler)
566 .set_code_selector(KERNEL_CODE_SELECTOR);
567
568 idt_ref[super::apic::IPI_N3_MIGRATE_VECTOR as u8]
571 .set_handler_addr(VirtAddr::from_ptr(
572 crate::ipc::n3::n3_migrate_ipi_entry as *const (),
573 ))
574 .set_code_selector(KERNEL_CODE_SELECTOR);
575
576 crate::e9_println!("IDT pre-load");
577 (*idt).load_unsafe();
578 crate::e9_println!("IDT loaded");
579 }
580 crate::e9_println!("IDT unlock");
581 unlock_idt_storage();
582 crate::e9_println!("IDT unlocked");
583
584 crate::e9_println!("IDT done");
585}
586
587pub fn load() {
588 lock_idt_storage();
589 unsafe {
590 let idt = &raw const IDT_STORAGEWrapper.idt;
591 (*idt).load_unsafe();
592 }
593 unlock_idt_storage();
594}
595
596pub fn register_lapic_timer_vector(vector: u8) {
598 lock_idt_storage();
599 unsafe {
600 let idt = &raw mut IDT_STORAGEWrapper.idt;
601 (&mut *idt)[vector]
602 .set_handler_addr(VirtAddr::from_ptr(lapic_timer_entry as *const ()))
603 .set_code_selector(KERNEL_CODE_SELECTOR);
604 (*idt).load_unsafe();
605 }
606 unlock_idt_storage();
607}
608
609pub fn register_ahci_irq(irq: u8) {
613 let vector = if irq < 16 {
614 super::pic::PIC1_OFFSET + irq
615 } else {
616 irq
617 };
618
619 lock_idt_storage();
620 unsafe {
621 let idt = &raw mut IDT_STORAGEWrapper.idt;
622 (&mut *idt)[vector]
623 .set_handler_fn(ahci_handler)
624 .set_code_selector(KERNEL_CODE_SELECTOR);
625 (*idt).load_unsafe();
626 }
627 unlock_idt_storage();
628 log::info!("AHCI IRQ {} registered on vector {:#x}", irq, vector);
629}
630
631pub fn register_nvme_irq_vector(vector: u8) {
636 lock_idt_storage();
637 unsafe {
638 let idt = &raw mut IDT_STORAGEWrapper.idt;
639 (&mut *idt)[vector]
640 .set_handler_fn(nvme_handler)
641 .set_code_selector(KERNEL_CODE_SELECTOR);
642 unlock_idt_storage();
643 }
644 log::info!("NVMe IRQ vector {:#x} registered", vector);
645}
646
647pub fn register_nvme_irq(irq: u8) {
651 let vector = if irq < 16 {
652 super::pic::PIC1_OFFSET + irq
653 } else {
654 irq
655 };
656
657 lock_idt_storage();
658 unsafe {
659 let idt = &raw mut IDT_STORAGEWrapper.idt;
660 (&mut *idt)[vector]
661 .set_handler_fn(nvme_handler)
662 .set_code_selector(KERNEL_CODE_SELECTOR);
663 (*idt).load_unsafe();
664 }
665 unlock_idt_storage();
666 log::info!("NVMe IRQ {} registered on vector {:#x}", irq, vector);
667}
668
669pub fn register_virtio_block_irq(irq: u8) {
674 let vector = if irq < 16 {
677 super::pic::PIC1_OFFSET + irq
678 } else {
679 irq
680 };
681
682 lock_idt_storage();
683 unsafe {
684 let idt = &raw mut IDT_STORAGEWrapper.idt;
685 (&mut *idt)[vector]
686 .set_handler_fn(virtio_block_handler)
687 .set_code_selector(KERNEL_CODE_SELECTOR);
688 (*idt).load_unsafe();
689 }
690 unlock_idt_storage();
691 log::info!("VirtIO-blk IRQ {} registered on vector {:#x}", irq, vector);
692}
693
694pub fn register_xhci_irq_vector(vector: u8) {
699 lock_idt_storage();
700 unsafe {
701 let idt = &raw mut IDT_STORAGEWrapper.idt;
702 (&mut *idt)[vector]
703 .set_handler_fn(xhci_handler)
704 .set_code_selector(KERNEL_CODE_SELECTOR);
705 (*idt).load_unsafe();
706 }
707 unlock_idt_storage();
708 log::info!("xHCI IRQ vector {:#x} registered", vector);
709}
710
711pub fn register_xhci_irq(irq: u8) {
712 let vector = if irq < 16 {
713 super::pic::PIC1_OFFSET + irq
714 } else {
715 irq
716 };
717
718 lock_idt_storage();
719 unsafe {
720 let idt = &raw mut IDT_STORAGEWrapper.idt;
721 (&mut *idt)[vector]
722 .set_handler_fn(xhci_handler)
723 .set_code_selector(KERNEL_CODE_SELECTOR);
724 (*idt).load_unsafe();
725 }
726 unlock_idt_storage();
727 log::info!("xHCI IRQ {} registered on vector {:#x}", irq, vector);
728}
729
730pub fn register_nic_irq(irq: u8) {
738 let vector = if irq < 16 {
739 super::pic::PIC1_OFFSET + irq
740 } else {
741 irq
742 };
743
744 lock_idt_storage();
745 unsafe {
746 let idt = &raw mut IDT_STORAGEWrapper.idt;
747 (&mut *idt)[vector]
748 .set_handler_fn(nic_handler)
749 .set_code_selector(KERNEL_CODE_SELECTOR);
750 (*idt).load_unsafe();
751 }
752 unlock_idt_storage();
753 log::info!("NIC IRQ {} registered on vector {:#x}", irq, vector);
754}
755
756extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {
762 let _gs = SwapGsGuard::new(needs_swapgs(stack_frame.code_segment.0));
763 log::warn!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
764}
765
766extern "x86-interrupt" fn invalid_opcode_handler(stack_frame: InterruptStackFrame) {
773 let cs = stack_frame.code_segment.0;
774 let is_user = (cs & 3) == 3;
775 let _gs = SwapGsGuard::new(needs_swapgs(cs));
776 if is_user {
777 if let Some(tid) = crate::process::current_task_id() {
778 crate::silo::handle_user_fault(
779 tid,
780 crate::silo::SiloFaultReason::InvalidOpcode,
781 stack_frame.instruction_pointer.as_u64(),
782 0,
783 stack_frame.instruction_pointer.as_u64(),
784 );
785 return;
786 }
787 }
788 crate::e9_mark!(b'#');
790 crate::e9_mark!(b'U');
791 crate::e9_mark!(b'D');
792 let rip: u64 = stack_frame.instruction_pointer.as_u64();
796 unsafe {
797 core::arch::asm!(
798 "out 0xe9, al",
799 "shr rcx, 8",
800 "mov al, cl",
801 "out 0xe9, al",
802 "shr rcx, 8",
803 "mov al, cl",
804 "out 0xe9, al",
805 "shr rcx, 8",
806 "mov al, cl",
807 "out 0xe9, al",
808 "shr rcx, 8",
809 "mov al, cl",
810 "out 0xe9, al",
811 "shr rcx, 8",
812 "mov al, cl",
813 "out 0xe9, al",
814 "shr rcx, 8",
815 "mov al, cl",
816 "out 0xe9, al",
817 "shr rcx, 8",
818 "mov al, cl",
819 "out 0xe9, al",
820 inout("rcx") rip => _,
821 in("al") rip as u8,
822 options(nostack, nomem),
823 );
824 }
825 loop {
827 crate::arch::hlt();
828 }
829}
830
831extern "x86-interrupt" fn non_maskable_interrupt_handler(stack_frame: InterruptStackFrame) {
832 let _gs = SwapGsGuard::new(needs_swapgs(stack_frame.code_segment.0));
835 if crate::boot::panic::panic_in_progress() {
836 crate::arch::x86_64::cli();
837 loop {
838 crate::arch::x86_64::hlt();
839 }
840 }
841 crate::serial_force_println!(
842 "[NMI] rip={:#x} cs={:#x}",
843 stack_frame.instruction_pointer.as_u64(),
844 stack_frame.code_segment.0
845 );
846 crate::arch::x86_64::cli();
847 loop {
848 crate::arch::x86_64::hlt();
849 }
850}
851
852extern "x86-interrupt" fn page_fault_handler(
854 stack_frame: InterruptStackFrame,
855 error_code: PageFaultErrorCode,
856) {
857 use x86_64::registers::control::Cr2;
858 let cs = stack_frame.code_segment.0;
859 let is_user = (cs & 3) == 3;
860 let swapgs_needed = needs_swapgs(cs);
865 let _gs = SwapGsGuard::new(swapgs_needed);
866
867 if swapgs_needed && !is_user {
869 let fault_addr = x86_64::registers::control::Cr2::read()
870 .as_ref()
871 .map(|v| v.as_u64())
872 .unwrap_or(0);
873 crate::serial_force_println!(
874 "\x1b[31;1m[pagefault]\x1b[0m SWAPGS-WINDOW: CS={:#x} (Ring0) but GS was user! rip={:#x} addr={:#x} err={:#x}",
875 cs,
876 stack_frame.instruction_pointer.as_u64(),
877 fault_addr,
878 error_code.bits()
879 );
880
881 if let Some(task) = crate::process::current_task_clone() {
888 crate::process::kill_task(task.id);
889 }
890 drop(_gs);
891 crate::process::scheduler::exit_current_task(-11); }
893
894 let fault_addr = Cr2::read();
896 let fault_vaddr = fault_addr.as_ref().map(|v| v.as_u64()).unwrap_or(0);
897 let rip = stack_frame.instruction_pointer.as_u64();
898 let user_rsp = stack_frame.stack_pointer.as_u64();
899
900 let mut trace_ctx = crate::trace::TraceTaskCtx::empty();
901 if is_user {
902 if let Some(task) = crate::process::current_task_clone() {
903 let as_ref = task.process.address_space_arc();
904 trace_ctx = crate::trace::TraceTaskCtx {
905 task_id: task.id.as_u64(),
906 pid: task.pid,
907 tid: task.tid,
908 cr3: as_ref.cr3().as_u64(),
909 };
910 }
911 }
912
913 let do_pf_trace = if is_user {
914 USER_PF_TRACE_BUDGET
915 .try_update(Ordering::Relaxed, Ordering::Relaxed, |v| {
916 if v > 0 {
917 Some(v - 1)
918 } else {
919 None
920 }
921 })
922 .is_ok()
923 } else {
924 true
925 };
926 if do_pf_trace {
927 crate::trace_mem!(
928 crate::trace::category::MEM_PF,
929 crate::trace::TraceKind::MemPageFault,
930 error_code.bits() as u64,
931 trace_ctx,
932 rip,
933 fault_vaddr,
934 user_rsp,
935 0
936 );
937 }
938
939 if error_code.contains(PageFaultErrorCode::PROTECTION_VIOLATION)
942 && error_code.contains(PageFaultErrorCode::CAUSED_BY_WRITE)
943 && is_user
944 {
945 if let Some(task) = crate::process::current_task_clone() {
946 let address_space = task.process.address_space_arc();
947 if let Ok(vaddr) = fault_addr {
948 match crate::syscall::fork::handle_cow_fault(vaddr.as_u64(), &address_space) {
949 Ok(()) => {
950 crate::trace_mem!(
951 crate::trace::category::MEM_COW,
952 crate::trace::TraceKind::MemCow,
953 1,
954 trace_ctx,
955 rip,
956 vaddr.as_u64(),
957 0,
958 0
959 );
960 return;
961 }
962 Err(reason) => {
963 crate::trace_mem!(
964 crate::trace::category::MEM_COW,
965 crate::trace::TraceKind::MemCow,
966 0,
967 trace_ctx,
968 rip,
969 vaddr.as_u64(),
970 0,
971 0
972 );
973 crate::serial_println!(
974 "\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={}",
975 task.id.as_u64(),
976 task.pid,
977 task.tid,
978 vaddr.as_u64(),
979 stack_frame.instruction_pointer.as_u64(),
980 reason
981 );
982 }
983 }
984 }
985 }
986 }
987
988 if is_user {
989 if let Some(task) = crate::process::current_task_clone() {
990 let address_space = task.process.address_space_arc();
991 if let Ok(vaddr) = fault_addr {
992 if do_pf_trace {
993 crate::serial_force_println!(
996 "\x1b[33m[pagefault] USER fault\x1b[0m: tid={} rip={:#x} addr={:#x} err={:#x}",
997 task.tid,
998 rip,
999 vaddr.as_u64(),
1000 error_code.bits()
1001 );
1002 crate::e9_println!(
1004 "[PF] tid={} rip={:#x} addr={:#x} err={:#x}",
1005 task.tid,
1006 rip,
1007 vaddr.as_u64(),
1008 error_code.bits()
1009 );
1010 }
1011
1012 let resolution = if error_code.contains(PageFaultErrorCode::PROTECTION_VIOLATION) {
1017 Err("Unresolved user page protection violation")
1018 } else {
1019 address_space.handle_fault(vaddr.as_u64())
1020 };
1021 match resolution {
1022 Ok(()) => {
1023 if do_pf_trace {
1024 crate::serial_force_println!(
1025 "\x1b[32m[pagefault] USER fault resolved\x1b[0m: tid={} addr={:#x}",
1026 task.tid,
1027 vaddr.as_u64()
1028 );
1029 }
1030 return;
1031 }
1032 Err(e) => {
1033 crate::serial_force_println!(
1034 "\x1b[31m[pagefault] USER fault resolution FAILED\x1b[0m: tid={} addr={:#x} err={:?}",
1035 task.tid,
1036 vaddr.as_u64(),
1037 e
1038 );
1039 crate::e9_println!(
1040 "[PF-FAIL] tid={} rip={:#x} addr={:#x}",
1041 task.tid,
1042 rip,
1043 vaddr.as_u64()
1044 );
1045 dump_user_pf_context(&address_space, rip, user_rsp);
1046 let (active_cr3, _) = x86_64::registers::control::Cr3::read();
1049 crate::serial_force_println!(
1050 "[pagefault] fault mapping: active_cr3={:#x} task_cr3={:#x}",
1051 active_cr3.start_address().as_u64(),
1052 address_space.cr3().as_u64()
1053 );
1054 dump_page_table_walk(vaddr.as_u64(), active_cr3.start_address().as_u64());
1055 }
1056 }
1057 }
1058 }
1059 }
1060
1061 if is_user {
1062 if let Some(tid) = crate::process::current_task_id() {
1063 crate::silo::handle_user_fault(
1064 tid,
1065 crate::silo::SiloFaultReason::PageFault,
1066 fault_addr.as_ref().map(|v| v.as_u64()).unwrap_or(0),
1067 error_code.bits() as u64,
1068 stack_frame.instruction_pointer.as_u64(),
1069 );
1070 return;
1071 }
1072 } else {
1073 crate::serial_force_println!(
1075 "\x1b[31;1m[pagefault] KERNEL fault\x1b[0m: rip={:#x} addr={:#x} err={:#x}",
1076 rip,
1077 fault_addr.as_ref().map(|v| v.as_u64()).unwrap_or(0),
1078 error_code.bits()
1079 );
1080 }
1081
1082 let task_snap = crate::process::scheduler::current_task_clone_try();
1084 dump_page_fault_full(&stack_frame, error_code, fault_addr, &task_snap);
1085}
1086
1087fn decode_error_code(ec: PageFaultErrorCode) -> &'static str {
1103 let p = ec.contains(PageFaultErrorCode::PROTECTION_VIOLATION);
1104 let w = ec.contains(PageFaultErrorCode::CAUSED_BY_WRITE);
1105 let u = ec.contains(PageFaultErrorCode::USER_MODE);
1106 match (p, w, u) {
1107 (false, false, false) => "kernel read of non-present page",
1108 (false, true, false) => "kernel write to non-present page",
1109 (false, false, true) => "user read of non-present page",
1110 (false, true, true) => "user write to non-present page",
1111 (true, false, false) => "kernel read protection violation",
1112 (true, true, false) => "kernel write protection violation (COW / RO page)",
1113 (true, false, true) => "user read protection violation (NX / supervisor-only)",
1114 (true, true, true) => "user write protection violation (COW / RO page)",
1115 }
1116}
1117
1118fn format_pte_flags(entry: u64) -> [u8; 32] {
1120 let mut buf = [b' '; 32];
1121 let mut pos = 0usize;
1122 let flags: &[(&str, u64)] = &[
1123 ("P", 1 << 0),
1124 ("RW", 1 << 1),
1125 ("US", 1 << 2),
1126 ("PWT", 1 << 3),
1127 ("PCD", 1 << 4),
1128 ("A", 1 << 5),
1129 ("D", 1 << 6),
1130 ("PS", 1 << 7),
1131 ("G", 1 << 8),
1132 ("NX", 1 << 63),
1133 ];
1134 for &(name, bit) in flags {
1135 if entry & bit != 0 {
1136 for &b in name.as_bytes() {
1137 if pos < buf.len() {
1138 buf[pos] = b;
1139 pos += 1;
1140 }
1141 }
1142 if pos < buf.len() {
1143 buf[pos] = b'|';
1144 pos += 1;
1145 }
1146 }
1147 }
1148 if pos > 0 && buf[pos - 1] == b'|' {
1149 buf[pos - 1] = b' ';
1150 }
1151 buf
1152}
1153
1154fn translate_via_raw_pt(vaddr: u64, cr3_phys: u64, hhdm: u64) -> Option<u64> {
1162 unsafe {
1163 let l4_ptr = (cr3_phys + hhdm) as *const u64;
1164 let l4e = *l4_ptr.add(((vaddr >> 39) & 0x1FF) as usize);
1165 if l4e & 1 == 0 {
1166 return None;
1167 }
1168
1169 let l3_ptr = ((l4e & 0x000F_FFFF_FFFF_F000) + hhdm) as *const u64;
1170 let l3e = *l3_ptr.add(((vaddr >> 30) & 0x1FF) as usize);
1171 if l3e & 1 == 0 {
1172 return None;
1173 }
1174 if l3e & 0x80 != 0 {
1175 return Some((l3e & 0x000F_FFFF_C000_0000) + (vaddr & 0x3FFF_FFFF));
1176 }
1177
1178 let l2_ptr = ((l3e & 0x000F_FFFF_FFFF_F000) + hhdm) as *const u64;
1179 let l2e = *l2_ptr.add(((vaddr >> 21) & 0x1FF) as usize);
1180 if l2e & 1 == 0 {
1181 return None;
1182 }
1183 if l2e & 0x80 != 0 {
1184 return Some((l2e & 0x000F_FFFF_FFE0_0000) + (vaddr & 0x1F_FFFF));
1185 }
1186
1187 let l1_ptr = ((l2e & 0x000F_FFFF_FFFF_F000) + hhdm) as *const u64;
1188 let l1e = *l1_ptr.add(((vaddr >> 12) & 0x1FF) as usize);
1189 if l1e & 1 == 0 {
1190 return None;
1191 }
1192 Some((l1e & 0x000F_FFFF_FFFF_F000) + (vaddr & 0xFFF))
1193 }
1194}
1195
1196fn dump_memory_bytes(vaddr: u64, cr3_phys: u64, count: usize, prefix: &str) {
1199 let hhdm = crate::memory::hhdm_offset();
1200 let mut offset = 0usize;
1201 while offset < count {
1202 let cur_va = vaddr.wrapping_add(offset as u64);
1203 let page_off = (cur_va & 0xFFF) as usize;
1204 let chunk = core::cmp::min(count - offset, 0x1000 - page_off);
1205 let Some(phys) = translate_via_raw_pt(cur_va, cr3_phys, hhdm) else {
1206 crate::serial_println!("{}(page {:#x} not mapped)", prefix, cur_va);
1207 offset += chunk;
1208 continue;
1209 };
1210 let src = (phys - (cur_va & 0xFFF) + hhdm) as *const u8;
1212 let mut line_off = 0usize;
1213 while line_off < chunk {
1214 let ll = core::cmp::min(16, chunk - line_off);
1215 let line_va = cur_va.wrapping_add(line_off as u64);
1216 let mut hex = [0u8; 48];
1217 let mut asc = [b'.'; 16];
1218 for i in 0..ll {
1219 let byte = unsafe { *src.add(page_off + line_off + i) };
1220 let hi = byte >> 4;
1221 let lo = byte & 0xF;
1222 hex[i * 3] = if hi < 10 { b'0' + hi } else { b'a' + hi - 10 };
1223 hex[i * 3 + 1] = if lo < 10 { b'0' + lo } else { b'a' + lo - 10 };
1224 hex[i * 3 + 2] = b' ';
1225 if byte >= 0x20 && byte < 0x7F {
1226 asc[i] = byte;
1227 }
1228 }
1229 for i in ll..16 {
1230 hex[i * 3] = b' ';
1231 hex[i * 3 + 1] = b' ';
1232 hex[i * 3 + 2] = b' ';
1233 }
1234 crate::serial_println!(
1235 "{}{:#018x}: {} |{}|",
1236 prefix,
1237 line_va,
1238 core::str::from_utf8(&hex[..48]).unwrap_or("???"),
1239 core::str::from_utf8(&asc[..ll]).unwrap_or("???")
1240 );
1241 line_off += ll;
1242 }
1243 offset += chunk;
1244 }
1245}
1246
1247fn dump_page_table_walk(vaddr: u64, cr3_phys: u64) {
1249 let hhdm = crate::memory::hhdm_offset();
1250 let l4_idx = ((vaddr >> 39) & 0x1FF) as usize;
1251 let l3_idx = ((vaddr >> 30) & 0x1FF) as usize;
1252 let l2_idx = ((vaddr >> 21) & 0x1FF) as usize;
1253 let l1_idx = ((vaddr >> 12) & 0x1FF) as usize;
1254
1255 unsafe {
1257 let l4_ptr = (cr3_phys + hhdm) as *const u64;
1258 let l4e = *l4_ptr.add(l4_idx);
1259 let f = format_pte_flags(l4e);
1260 crate::serial_println!(
1261 " PML4[{:>3}] = {:#018x} phys={:#014x} [{}]",
1262 l4_idx,
1263 l4e,
1264 l4e & 0x000F_FFFF_FFFF_F000,
1265 core::str::from_utf8(&f).unwrap_or("?").trim()
1266 );
1267 if l4e & 1 == 0 {
1268 crate::serial_println!(" \x1b[1;31m ==> STOP: PML4 not present !\x1b[0m");
1269 return;
1270 }
1271
1272 let l3_ptr = ((l4e & 0x000F_FFFF_FFFF_F000) + hhdm) as *const u64;
1273 let l3e = *l3_ptr.add(l3_idx);
1274 let f = format_pte_flags(l3e);
1275 crate::serial_println!(
1276 " PDPT[{:>3}] = {:#018x} phys={:#014x} [{}]",
1277 l3_idx,
1278 l3e,
1279 l3e & 0x000F_FFFF_FFFF_F000,
1280 core::str::from_utf8(&f).unwrap_or("?").trim()
1281 );
1282 if l3e & 1 == 0 {
1283 crate::serial_println!(" \x1b[1;31m ==> STOP: PDPT not present !\x1b[0m");
1284 return;
1285 }
1286 if l3e & 0x80 != 0 {
1287 crate::serial_println!(
1288 " ==> 1 GiB huge page => phys {:#x}",
1289 l3e & 0x000F_FFFF_C000_0000
1290 );
1291 return;
1292 } let l2_ptr = ((l3e & 0x000F_FFFF_FFFF_F000) + hhdm) as *const u64;
1295 let l2e = *l2_ptr.add(l2_idx);
1296 let f = format_pte_flags(l2e);
1297 crate::serial_println!(
1298 " PD [{:>3}] = {:#018x} phys={:#014x} [{}]",
1299 l2_idx,
1300 l2e,
1301 l2e & 0x000F_FFFF_FFFF_F000,
1302 core::str::from_utf8(&f).unwrap_or("?").trim()
1303 );
1304 if l2e & 1 == 0 {
1305 crate::serial_println!(" \x1b[1;31m ==> STOP: PD not present !\x1b[0m");
1306 return;
1307 }
1308 if l2e & 0x80 != 0 {
1309 crate::serial_println!(
1310 " ==> 2 MiB huge page => phys {:#x}",
1311 l2e & 0x000F_FFFF_FFE0_0000
1312 );
1313 return;
1314 } let l1_ptr = ((l2e & 0x000F_FFFF_FFFF_F000) + hhdm) as *const u64;
1317 let l1e = *l1_ptr.add(l1_idx);
1318 let f = format_pte_flags(l1e);
1319 crate::serial_println!(
1320 " PT [{:>3}] = {:#018x} phys={:#014x} [{}]",
1321 l1_idx,
1322 l1e,
1323 l1e & 0x000F_FFFF_FFFF_F000,
1324 core::str::from_utf8(&f).unwrap_or("?").trim()
1325 );
1326 if l1e & 1 == 0 {
1327 crate::serial_println!(" \x1b[1;31m ==> STOP: PT not present !\x1b[0m");
1328 } else {
1329 crate::serial_println!(
1330 " \x1b[1;32m ==> PAGE PRESENT\x1b[0m => phys {:#x} (check RW/US/NX flags)",
1331 l1e & 0x000F_FFFF_FFFF_F000
1332 );
1333 }
1334 crate::serial_println!(" --- Neighbouring PT entries ---");
1336 let start = if l1_idx >= 2 { l1_idx - 2 } else { 0 };
1337 for i in start..core::cmp::min(l1_idx + 3, 512) {
1338 let e = *l1_ptr.add(i);
1339 if e != 0 {
1340 let f = format_pte_flags(e);
1341 crate::serial_println!(
1342 " PT[{:>3}] = {:#018x} [{}]{}",
1343 i,
1344 e,
1345 core::str::from_utf8(&f).unwrap_or("?").trim(),
1346 if i == l1_idx { " <<<" } else { "" }
1347 );
1348 }
1349 }
1350 }
1351}
1352
1353fn dump_nearby_vma_regions(as_ref: &crate::memory::AddressSpace, fault_vaddr: u64) {
1355 let page_start = fault_vaddr & !0xFFF;
1356 let probes = [
1357 page_start,
1358 fault_vaddr & !0x1F_FFFF,
1359 fault_vaddr & !0x3FFF_FFFF,
1360 0x0000_0001_0000_0000,
1361 0x0000_0000_0040_0000,
1362 0x0000_7FFF_F000_0000,
1363 ];
1364 let mut found_any = false;
1365 for &p in &probes {
1366 if let Some(vma) = as_ref.region_by_start(p) {
1367 let end = vma.start + (vma.page_count as u64) * vma.page_size.bytes();
1368 let hit = fault_vaddr >= vma.start && fault_vaddr < end;
1369 crate::serial_println!(
1370 " VMA {:#014x}..{:#014x} pages={:<5} type={:?} flags={:?} pgsz={:?}{}",
1371 vma.start,
1372 end,
1373 vma.page_count,
1374 vma.vma_type,
1375 vma.flags,
1376 vma.page_size,
1377 if hit {
1378 " \x1b[1;32m<<< FAULT\x1b[0m"
1379 } else {
1380 ""
1381 }
1382 );
1383 found_any = true;
1384 }
1385 }
1386 if as_ref.has_mapping_in_range(page_start, 0x1000) {
1387 crate::serial_println!(
1388 " Note: fault page {:#x} IS within a tracked mapping range",
1389 page_start
1390 );
1391 } else {
1392 crate::serial_println!(
1393 " Note: fault page {:#x} is NOT within any tracked mapping range",
1394 page_start
1395 );
1396 }
1397 if !found_any {
1398 crate::serial_println!(" (no VMA regions found at probed addresses)");
1399 }
1400}
1401
1402fn dump_page_fault_full(
1407 stack_frame: &InterruptStackFrame,
1408 error_code: PageFaultErrorCode,
1409 fault_addr: Result<x86_64::VirtAddr, x86_64::addr::VirtAddrNotValid>,
1410 task: &Option<alloc::sync::Arc<crate::process::task::Task>>,
1411) -> ! {
1412 use x86_64::registers::control::{Cr0, Cr3, Cr4};
1413
1414 let rip = stack_frame.instruction_pointer.as_u64();
1415 let rsp = stack_frame.stack_pointer.as_u64();
1416 let cs = stack_frame.code_segment.0;
1417 let ss = stack_frame.stack_segment.0;
1418 let rflags = stack_frame.cpu_flags.bits();
1419 let fault_vaddr = fault_addr.as_ref().map(|v| v.as_u64()).unwrap_or(0);
1420 let is_user = (cs & 3) == 3;
1421
1422 crate::serial_println!("\x1b[1;31m");
1423 crate::serial_println!(
1424 "****************************************************************************"
1425 );
1426 crate::serial_println!("* KERNEL PAGE FAULT EXCEPTiON *");
1427 crate::serial_println!(
1428 "********************************************************************\x1b[0m"
1429 );
1430
1431 crate::serial_println!("\x1b[1;33m--- Error code ---\x1b[0m");
1433 crate::serial_println!(" Raw : {:#06x}", error_code.bits());
1434 crate::serial_println!(
1435 " Diagnostic : \x1b[1;31m{}\x1b[0m",
1436 decode_error_code(error_code)
1437 );
1438 crate::serial_println!(
1439 " PRESENT : {} | WRITE : {} | USER : {} | RSVD : {} | FETCH : {}",
1440 error_code.contains(PageFaultErrorCode::PROTECTION_VIOLATION) as u8,
1441 error_code.contains(PageFaultErrorCode::CAUSED_BY_WRITE) as u8,
1442 error_code.contains(PageFaultErrorCode::USER_MODE) as u8,
1443 (error_code.bits() >> 3) & 1,
1444 (error_code.bits() >> 4) & 1
1445 );
1446
1447 crate::serial_println!("\x1b[1;33m--- Faulting context ---\x1b[0m");
1449 crate::serial_println!(" CR2 (addr) : \x1b[1;35m{:#018x}\x1b[0m", fault_vaddr);
1450 crate::serial_println!(" RIP : \x1b[1;36m{:#018x}\x1b[0m", rip);
1451 crate::serial_println!(" RSP : {:#018x}", rsp);
1452 crate::serial_println!(
1453 " CS : {:#06x} (ring={}{}) | SS : {:#06x}",
1454 cs,
1455 cs & 3,
1456 if is_user { " USER" } else { " KERNEL" },
1457 ss
1458 );
1459
1460 let mut rf_str = [0u8; 64];
1462 let mut rfp = 0usize;
1463 for &(name, bit) in &[
1464 ("CF", 1u64),
1465 ("PF", 4),
1466 ("AF", 16),
1467 ("ZF", 64),
1468 ("SF", 128),
1469 ("TF", 256),
1470 ("IF", 512),
1471 ("DF", 1024),
1472 ("OF", 2048),
1473 ] {
1474 if rflags & bit != 0 {
1475 for &b in name.as_bytes() {
1476 if rfp < rf_str.len() {
1477 rf_str[rfp] = b;
1478 rfp += 1;
1479 }
1480 }
1481 if rfp < rf_str.len() {
1482 rf_str[rfp] = b' ';
1483 rfp += 1;
1484 }
1485 }
1486 }
1487 crate::serial_println!(
1488 " RFLAGS : {:#018x} [{}]",
1489 rflags,
1490 core::str::from_utf8(&rf_str[..rfp]).unwrap_or("?")
1491 );
1492
1493 crate::serial_println!("\x1b[1;33m--- Control registers ---\x1b[0m");
1495 let cr0 = Cr0::read_raw();
1496 let (cr3_frame, cr3_flags) = Cr3::read();
1497 let cr3_phys = cr3_frame.start_address().as_u64();
1498 let cr4 = Cr4::read_raw();
1499 let efer: u64 = x86_64::registers::model_specific::Efer::read_raw();
1500 crate::serial_println!(" CR0 : {:#018x}", cr0);
1501 crate::serial_println!(
1502 " CR3 : {:#018x} (flags={:#x})",
1503 cr3_phys,
1504 cr3_flags.bits()
1505 );
1506 crate::serial_println!(" CR4 : {:#018x}", cr4);
1507 crate::serial_println!(
1508 " EFER : {:#018x} [{}{}{}]",
1509 efer,
1510 if efer & 1 != 0 { "SCE " } else { "" },
1511 if efer & (1 << 8) != 0 { "LME " } else { "" },
1512 if efer & (1 << 11) != 0 { "NXE" } else { "" }
1513 );
1514
1515 crate::serial_println!("\x1b[1;33m--- CPU context ---\x1b[0m");
1517 crate::serial_println!(" LAPIC ID : {}", super::apic::lapic_id());
1518 crate::serial_println!(" Ticks sched : {}", crate::process::scheduler::ticks());
1519 crate::serial_println!(" HHDM offset : {:#x}", crate::memory::hhdm_offset());
1520
1521 crate::serial_println!("\x1b[1;33m--- Task context ---\x1b[0m");
1523 if let Some(ref t) = *task {
1524 crate::serial_println!(
1525 " ID={} PID={} TID={} TGID={} name=\"{}\" prio={:?} ticks={}",
1526 t.id.as_u64(),
1527 t.pid,
1528 t.tid,
1529 t.tgid,
1530 t.name,
1531 t.priority,
1532 t.ticks.load(core::sync::atomic::Ordering::Relaxed)
1533 );
1534 let task_cr3: u64 = {
1547 let hhdm = crate::memory::hhdm_offset();
1548 let _proc_ptr: u64 = alloc::sync::Arc::as_ptr(&t.process) as u64;
1550 let as_cell_addr: u64 =
1555 unsafe { (*alloc::sync::Arc::as_ptr(&t.process)).address_space.get() as u64 };
1556 let as_inner_u64: u64 = match translate_via_raw_pt(as_cell_addr, cr3_phys, hhdm) {
1559 Some(phys) => unsafe { *((phys + hhdm) as *const u64) },
1560 None => 0,
1561 };
1562 if as_inner_u64 == 0 {
1563 0u64
1564 } else {
1565 let as_data_ptr: u64 = as_inner_u64 + 2 * core::mem::size_of::<usize>() as u64;
1570 match translate_via_raw_pt(as_data_ptr, cr3_phys, hhdm) {
1572 Some(phys) => unsafe { *((phys + hhdm) as *const u64) },
1573 None => 0,
1574 }
1575 }
1576 };
1577 if task_cr3 == 0 {
1578 crate::serial_println!(
1579 " Task CR3 : <unreadable : null/unmapped Arc<AddressSpace>>"
1580 );
1581 } else {
1582 crate::serial_println!(
1583 " Task CR3 : {:#018x}{}",
1584 task_cr3,
1585 if task_cr3 != cr3_phys {
1586 " *** DIFFERS from hardware CR3! ***"
1587 } else {
1588 " (matches hardware CR3)"
1589 }
1590 );
1591 }
1592 } else {
1593 crate::serial_println!(" (no current task : scheduler idle or unavailable)");
1594 }
1595
1596 crate::serial_println!("\x1b[1;33m--- Memory stats ---\x1b[0m");
1598 if let Some(guard) = crate::memory::get_allocator().try_lock() {
1599 if let Some(ref alloc) = *guard {
1600 let (total, allocated) = alloc.page_totals();
1601 let free = total.saturating_sub(allocated);
1602 crate::serial_println!(
1603 " Total={} pages ({} MiB) Alloc={} ({} MiB) Free={} ({} MiB)",
1604 total,
1605 total * 4 / 1024,
1606 allocated,
1607 allocated * 4 / 1024,
1608 free,
1609 free * 4 / 1024
1610 );
1611 let mut zones =
1612 [crate::memory::buddy::ZoneStats::empty(); crate::memory::zone::ZoneType::COUNT];
1613 let n = alloc.zone_snapshot(&mut zones);
1614 for i in 0..n {
1615 let zone = zones[i];
1616 let zone_ref = alloc.get_zone(i);
1617 crate::serial_println!(
1618 " Zone {} ({}): base={:#x} managed={} present={} spanned={} reserved={} alloc={} free={} state={:?} seg={}/{} largest={:?}",
1619 i,
1620 match zone.zone_type {
1621 crate::memory::zone::ZoneType::DMA => "DMA",
1622 crate::memory::zone::ZoneType::Normal => "Normal",
1623 crate::memory::zone::ZoneType::HighMem => "High",
1624 },
1625 zone.base,
1626 zone.managed_pages,
1627 zone.present_pages,
1628 zone.spanned_pages,
1629 zone.reserved_pages,
1630 zone.allocated_pages,
1631 zone.free_pages,
1632 zone.pressure(),
1633 zone.segment_count,
1634 zone.segment_capacity,
1635 zone.largest_free_order
1636 );
1637 crate::serial_println!(
1638 " reserve={} avail={} holes={} cached[u/m]={}/{} free[u/m]={}/{} pageblocks[u/m]={}/{} total={} order={}",
1639 zone.reserve_floor_pages(),
1640 zone.available_after_reserve_pages(),
1641 zone.hole_pages(),
1642 zone.cached_unmovable_pages,
1643 zone.cached_movable_pages,
1644 zone.unmovable_free_pages,
1645 zone.movable_free_pages,
1646 zone.unmovable_pageblocks,
1647 zone.movable_pageblocks,
1648 zone.pageblock_count,
1649 crate::memory::zone::PAGEBLOCK_ORDER
1650 );
1651 crate::serial_println!(
1652 " frag/order: o1={}%% o4={}%% o{}={}%%",
1653 zone_ref.fragmentation_score(1, zone.cached_pages),
1654 zone_ref.fragmentation_score(4, zone.cached_pages),
1655 crate::memory::zone::PAGEBLOCK_ORDER,
1656 zone_ref.fragmentation_score(
1657 crate::memory::zone::PAGEBLOCK_ORDER as u8,
1658 zone.cached_pages,
1659 )
1660 );
1661 }
1662 } else {
1663 crate::serial_println!(" (allocator not initialized)");
1664 }
1665 } else {
1666 crate::serial_println!(" (allocator lock contended : skipping)");
1667 }
1668
1669 let quarantine = crate::memory::buddy::poison_quarantine_pages_snapshot();
1670 let fail_counts = crate::memory::buddy::buddy_alloc_fail_counts_snapshot();
1671 let compaction = crate::memory::buddy::compaction_stats_snapshot();
1672 crate::serial_println!(" Poison quarantine : {} pages", quarantine);
1673
1674 let mut printed_fail = false;
1675 for (order, count) in fail_counts.iter().enumerate() {
1676 if *count == 0 {
1677 continue;
1678 }
1679 printed_fail = true;
1680 crate::serial_println!(" Buddy alloc fail : order={} count={}", order, count);
1681 }
1682 if !printed_fail {
1683 crate::serial_println!(" Buddy alloc fail : none");
1684 }
1685
1686 if compaction.attempts == 0 {
1687 crate::serial_println!(" Compaction assist : none");
1688 } else {
1689 crate::serial_println!(
1690 " Compaction assist : attempts={} success={} last_order={:?} migratetype={:?} zone={:?} pressure={:?}",
1691 compaction.attempts,
1692 compaction.successes,
1693 compaction.last_order,
1694 compaction.last_migratetype,
1695 compaction.last_zone,
1696 compaction.last_pressure
1697 );
1698 crate::serial_println!(
1699 " frag={}%% req={} avail={} usable={} cached={} drained={} pageblocks={}/{}",
1700 compaction.last_fragmentation_score,
1701 compaction.last_requested_pages,
1702 compaction.last_available_pages,
1703 compaction.last_usable_pages,
1704 compaction.last_cached_pages,
1705 compaction.last_drained_pages,
1706 compaction.last_matching_pageblocks,
1707 compaction.last_pageblock_count
1708 );
1709 }
1710
1711 crate::serial_println!("\x1b[1;33m--- Code at RIP ({:#x}) ---\x1b[0m", rip);
1713 dump_memory_bytes(rip, cr3_phys, 32, " ");
1714
1715 crate::serial_println!("\x1b[1;33m--- Stack dump (RSP={:#x}) ---\x1b[0m", rsp);
1717 dump_memory_bytes(rsp, cr3_phys, 128, " ");
1718
1719 crate::serial_println!(
1721 "\x1b[1;33m--- Page table walk (CR2={:#x}, CR3={:#x}) ---\x1b[0m",
1722 fault_vaddr,
1723 cr3_phys
1724 );
1725 if fault_addr.is_ok() {
1726 dump_page_table_walk(fault_vaddr, cr3_phys);
1727 } else {
1728 crate::serial_println!(" (CR2 is a non-canonical address: {:#x})", fault_vaddr);
1729 }
1730
1731 if let Some(ref t) = *task {
1733 crate::serial_println!("\x1b[1;33m--- VMA regions near fault ---\x1b[0m");
1734 let hhdm_vma = crate::memory::hhdm_offset();
1739 let safe_as: Option<*const crate::memory::AddressSpace> = unsafe {
1740 let as_cell_addr: u64 =
1741 (*alloc::sync::Arc::as_ptr(&t.process)).address_space.get() as u64;
1742 match translate_via_raw_pt(as_cell_addr, cr3_phys, hhdm_vma) {
1743 Some(phys) => {
1744 let as_inner_u64 = *((phys + hhdm_vma) as *const u64);
1746 if as_inner_u64 == 0 {
1747 None
1748 } else {
1749 let as_data_ptr = (as_inner_u64 + 2 * core::mem::size_of::<usize>() as u64)
1751 as *const crate::memory::AddressSpace;
1752 if translate_via_raw_pt(as_data_ptr as u64, cr3_phys, hhdm_vma).is_some() {
1754 Some(as_data_ptr)
1755 } else {
1756 None
1757 }
1758 }
1759 }
1760 None => None,
1761 }
1762 };
1763 if let Some(as_ptr) = safe_as {
1764 let as_ref = unsafe { &*as_ptr };
1766 dump_nearby_vma_regions(as_ref, fault_vaddr);
1767 } else {
1768 crate::serial_println!(" (AddressSpace unreadable : skipping VMA dump)");
1769 }
1770 }
1771
1772 crate::serial_println!("\x1b[1;31m***********************************************************");
1773 crate::serial_println!("* END OF PAGE FAULT DuMP *");
1774 crate::serial_println!(
1775 "*******************************************************************\x1b[0m"
1776 );
1777
1778 panic!(
1779 "PAGE FAULT: {} at {:#x}, RIP={:#x}, CR3={:#x}, err={:#x}",
1780 decode_error_code(error_code),
1781 fault_vaddr,
1782 rip,
1783 cr3_phys,
1784 error_code.bits()
1785 );
1786}
1787
1788fn dump_user_pf_context(as_ref: &crate::memory::AddressSpace, rip: u64, rsp: u64) {
1790 use x86_64::VirtAddr;
1791
1792 let hhdm = crate::memory::hhdm_offset();
1793
1794 if let Some(phys) = as_ref.translate(VirtAddr::new(rip)) {
1795 let off = (rip & 0xfff) as usize;
1796 let mut bytes = [0u8; 8];
1797 unsafe {
1799 let src = (phys.as_u64() - (rip & 0xfff) + hhdm + off as u64) as *const u8;
1800 core::ptr::copy_nonoverlapping(src, bytes.as_mut_ptr(), bytes.len());
1801 }
1802 crate::serial_println!(
1803 "[pagefault] ctx: rsp={:#x} rip-bytes={:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",
1804 rsp,
1805 bytes[0],
1806 bytes[1],
1807 bytes[2],
1808 bytes[3],
1809 bytes[4],
1810 bytes[5],
1811 bytes[6],
1812 bytes[7],
1813 );
1814 } else {
1815 crate::serial_println!("[pagefault] ctx: rsp={:#x} rip page unmapped", rsp);
1816 }
1817
1818 if let Some(phys) = as_ref.translate(VirtAddr::new(rsp)) {
1819 crate::serial_println!(
1820 "[pagefault] stack-top: rsp mapped (phys={:#x})",
1821 phys.as_u64()
1822 );
1823 } else {
1824 crate::serial_println!("[pagefault] stack-top: rsp unmapped");
1825 }
1826}
1827
1828extern "x86-interrupt" fn general_protection_fault_handler(
1830 stack_frame: InterruptStackFrame,
1831 error_code: u64,
1832) {
1833 let cs = stack_frame.code_segment.0;
1834 let is_user = (cs & 3) == 3;
1835 let swapgs_needed = needs_swapgs(cs);
1839 let _gs = SwapGsGuard::new(swapgs_needed);
1840 if swapgs_needed && !is_user {
1842 crate::serial_force_println!(
1843 "\x1b[31;1m[GPF]\x1b[0m SWAPGS-WINDOW: CS={:#x} (Ring0) but GS was user! rip={:#x} err={:#x} rsp={:#x}",
1844 cs,
1845 stack_frame.instruction_pointer.as_u64(),
1846 error_code,
1847 stack_frame.stack_pointer.as_u64()
1848 );
1849 if let Some(task) = crate::process::current_task_clone() {
1851 crate::process::kill_task(task.id);
1852 }
1853 drop(_gs);
1854 crate::process::scheduler::exit_current_task(-11); }
1856 if is_user {
1857 if let Some(tid) = crate::process::current_task_id() {
1858 crate::serial_force_println!(
1859 "\x1b[31;1m[GPF]\x1b[0m USER tid={} rip={:#x} err={:#x}",
1860 tid,
1861 stack_frame.instruction_pointer.as_u64(),
1862 error_code
1863 );
1864 crate::silo::handle_user_fault(
1865 tid,
1866 crate::silo::SiloFaultReason::GeneralProtection,
1867 stack_frame.instruction_pointer.as_u64(),
1868 error_code,
1869 stack_frame.instruction_pointer.as_u64(),
1870 );
1871 return;
1872 }
1873 }
1874 crate::serial_force_println!(
1875 "\x1b[31;1m[GPF]\x1b[0m KERNEL rip={:#x} err={:#x} cs={:#x} rsp={:#x}",
1876 stack_frame.instruction_pointer.as_u64(),
1877 error_code,
1878 stack_frame.code_segment.0,
1879 stack_frame.stack_pointer.as_u64()
1880 );
1881 panic!("General protection fault");
1882}
1883
1884extern "x86-interrupt" fn stack_segment_fault_handler(
1886 stack_frame: InterruptStackFrame,
1887 error_code: u64,
1888) {
1889 let _gs = SwapGsGuard::new(needs_swapgs(stack_frame.code_segment.0));
1892 crate::serial_force_println!(
1893 "\x1b[31;1m[STACK_FAULT]\x1b[0m rip={:#x} err={:#x} cs={:#x} rsp={:#x}",
1894 stack_frame.instruction_pointer.as_u64(),
1895 error_code,
1896 stack_frame.code_segment.0,
1897 stack_frame.stack_pointer.as_u64()
1898 );
1899 panic!("Stack segment fault");
1900}
1901
1902extern "x86-interrupt" fn double_fault_handler(
1913 stack_frame: InterruptStackFrame,
1914 error_code: u64,
1915) -> ! {
1916 unsafe {
1926 let lo: u32;
1927 let hi: u32;
1928 core::arch::asm!(
1929 "rdmsr",
1930 in("ecx") 0xC000_0101u32, out("eax") lo,
1932 out("edx") hi,
1933 options(nostack, preserves_flags),
1934 );
1935 let gs_base = (lo as u64) | ((hi as u64) << 32);
1936 if gs_base < 0xFFFF_8000_0000_0000 {
1938 core::arch::asm!("swapgs", options(nostack, preserves_flags));
1939 }
1940 }
1941 crate::serial_force_println!(
1942 "\x1b[31;1m[DOUBLE_FAULT]\x1b[0m rip={:#x} err={:#x} cs={:#x} rsp={:#x}",
1943 stack_frame.instruction_pointer.as_u64(),
1944 error_code,
1945 stack_frame.code_segment.0,
1946 stack_frame.stack_pointer.as_u64()
1947 );
1948 panic!(
1949 "EXCEPTION: DOUBLE FAULT (error code: {:#x})\n{:#?}",
1950 error_code, stack_frame
1951 );
1952}
1953
1954extern "x86-interrupt" fn legacy_timer_handler(stack_frame: InterruptStackFrame) {
1962 let _gs = SwapGsGuard::new((stack_frame.code_segment.0 & 3) == 3);
1964 if crate::arch::x86_64::timer::is_apic_timer_active() {
1965 if super::apic::is_initialized() {
1967 super::apic::eoi();
1968 } else {
1969 pic::end_of_interrupt(0);
1970 }
1971 return;
1972 }
1973
1974 let ticks = crate::process::scheduler::ticks();
1978 unsafe {
1979 core::arch::asm!("out 0xe9, al", in("al") b't', options(nomem, nostack));
1980 }
1981
1982 crate::process::scheduler::timer_tick();
1984 if super::apic::is_initialized() {
1989 super::apic::eoi();
1990 } else {
1991 pic::end_of_interrupt(0);
1992 }
1993
1994 let cpl = stack_frame.code_segment.0 & 3;
2006 if cpl == 3 {
2007 crate::arch::x86_64::apic::self_ipi_resched();
2008 } else {
2009 crate::process::scheduler::maybe_preempt();
2010 }
2011}
2012
2013extern "x86-interrupt" fn lapic_timer_handler(stack_frame: InterruptStackFrame) {
2015 let cs = stack_frame.code_segment.0;
2017 let _gs = SwapGsGuard::new((cs & 3) == 3);
2018 let cpu = crate::arch::x86_64::percpu::current_cpu_index();
2019 let ticks = crate::process::scheduler::ticks();
2020 unsafe {
2023 core::arch::asm!("out 0xe9, al", in("al") b'T', options(nomem, nostack));
2025 }
2026
2027 crate::process::scheduler::timer_tick();
2032 unsafe { core::arch::asm!("mov al, '1'; out 0xe9, al", out("al") _) };
2033 super::apic::eoi();
2034 if (cs & 3) == 3 {
2046 crate::process::scheduler::request_force_resched_hint(cpu);
2047 } else {
2048 crate::process::scheduler::maybe_preempt();
2049 }
2050}
2051
2052extern "x86-interrupt" fn mouse_handler(_stack_frame: InterruptStackFrame) {
2054 unsafe {
2056 core::arch::asm!("out 0xe9, al", in("al") b'M', options(nomem, nostack));
2057 core::arch::asm!("out 0xe9, al", in("al") b'\n', options(nomem, nostack));
2058 }
2059 crate::arch::x86_64::mouse::handle_irq();
2060 pic::end_of_interrupt(12);
2064}
2065
2066extern "x86-interrupt" fn keyboard_handler(_stack_frame: InterruptStackFrame) {
2068 let raw = unsafe { super::io::inb(0x60) };
2069 unsafe {
2071 core::arch::asm!("out 0xe9, al", in("al") b'K', options(nomem, nostack));
2072 core::arch::asm!("out 0xe9, al", in("al") raw, options(nomem, nostack));
2073 core::arch::asm!("out 0xe9, al", in("al") b'\n', options(nomem, nostack));
2074 }
2075
2076 crate::entropy::add_entropy(1, (raw as u64) ^ super::rdtsc());
2078
2079 if let Some(ch) = super::keyboard_layout::handle_scancode_raw(raw) {
2080 crate::arch::x86_64::keyboard::add_to_buffer(ch);
2081 }
2082
2083 pic::end_of_interrupt(1);
2087}
2088
2089extern "x86-interrupt" fn spurious_handler(_stack_frame: InterruptStackFrame) {
2092 }
2094
2095extern "x86-interrupt" fn ahci_handler(_stack_frame: InterruptStackFrame) {
2100 crate::entropy::add_entropy(3, super::rdtsc());
2102
2103 crate::hardware::storage::ahci::handle_interrupt();
2104
2105 if super::apic::is_initialized() {
2106 super::apic::eoi();
2107 } else {
2108 let irq = crate::hardware::storage::ahci::AHCI_IRQ_LINE
2109 .load(core::sync::atomic::Ordering::Relaxed);
2110 pic::end_of_interrupt(irq);
2111 }
2112}
2113
2114extern "x86-interrupt" fn nvme_handler(_stack_frame: InterruptStackFrame) {
2118 crate::entropy::add_entropy(3, super::rdtsc());
2119
2120 crate::hardware::storage::nvme::handle_interrupt();
2121
2122 if super::apic::is_initialized() {
2123 super::apic::eoi();
2124 } else {
2125 let irq = crate::hardware::storage::nvme::NVME_IRQ_LINE
2126 .load(core::sync::atomic::Ordering::Relaxed);
2127 pic::end_of_interrupt(irq);
2128 }
2129}
2130
2131extern "x86-interrupt" fn virtio_block_handler(_stack_frame: InterruptStackFrame) {
2136 crate::hardware::storage::virtio_block::handle_interrupt();
2138
2139 if super::apic::is_initialized() {
2141 super::apic::eoi();
2142 } else {
2143 let irq = crate::hardware::storage::virtio_block::get_irq();
2145 pic::end_of_interrupt(irq);
2146 }
2147}
2148
2149extern "x86-interrupt" fn xhci_handler(_stack_frame: InterruptStackFrame) {
2154 crate::hardware::usb::xhci::handle_interrupt();
2155
2156 if super::apic::is_initialized() {
2157 super::apic::eoi();
2158 } else {
2159 let irq =
2160 crate::hardware::usb::xhci::XHCI_IRQ_LINE.load(core::sync::atomic::Ordering::Relaxed);
2161 pic::end_of_interrupt(irq);
2162 }
2163}
2164
2165extern "x86-interrupt" fn nic_handler(stack_frame: InterruptStackFrame) {
2172 let _gs = SwapGsGuard::new((stack_frame.code_segment.0 & 3) == 3);
2176
2177 crate::hardware::nic::handle_interrupt();
2178
2179 if super::apic::is_initialized() {
2180 super::apic::eoi();
2181 } else {
2182 let irq = crate::hardware::nic::NIC_IRQ_LINE.load(core::sync::atomic::Ordering::Relaxed);
2183 pic::end_of_interrupt(irq);
2184 }
2185}
2186
2187extern "x86-interrupt" fn resched_ipi_handler(stack_frame: InterruptStackFrame) {
2197 let _gs = SwapGsGuard::new((stack_frame.code_segment.0 & 3) == 3);
2199 super::apic::eoi();
2200 crate::process::scheduler::maybe_preempt();
2201}
2202
2203extern "x86-interrupt" fn tlb_shootdown_handler(stack_frame: InterruptStackFrame) {
2205 let _gs = SwapGsGuard::new((stack_frame.code_segment.0 & 3) == 3);
2207 super::tlb::tlb_shootdown_ipi_handler();
2209}