长模式(Long Mode)下64位自制OS键盘中断触发QEMU重置问题排查求助
Hey Alexander, let's dig into the issues causing your double fault when handling keyboard interrupts. Looking at your code, there are several critical mistakes in how you're setting up the IDT and PIC that are leading to the system reset. Let's break them down one by one and fix them:
1. Critical Flaws in load_idt_entry()
Your current load_idt_entry() function has major issues that break interrupt handling entirely:
- All 256 IDT entries point to
isr1: This means every interrupt (including critical exceptions like page faults or double faults) will trigger the same keyboard handler, which is completely incorrect. You should only map the keyboard interrupt (IRQ1, which maps to IDT index 33 after PIC remapping) toisr1, and map other entries to a generic fault handler for now. - Repeating PIC setup and IDT loading 256 times: PIC remapping, interrupt mask configuration, and IDT loading only need to run once. Doing this in a loop corrupts the PIC's state and leads to unexpected hardware behavior.
Fixed load_idt_entry() Code:
First, add the missing PIC1 port definitions at the top of your C file:
#define PIC1_COMMAND 0x20 #define PIC1_DATA 0x21
Then rewrite the function:
void load_idt_entry() { // Remap PIC ONCE to fix IRQ-to-IDT index mapping RemapPic(); // Set interrupt masks: only enable IRQ1 (keyboard), mask all others outb(PIC1_DATA, 0xfd); // 0xfd = 11111101, unmask IRQ1 outb(PIC2_DATA, 0xff); // Mask all IRQs from PIC2 // Initialize all IDT entries to a generic fault handler first for(unsigned long long int t = 0; t < 256; t++) { IDT[t].offset_lowerbits = (unsigned short)(((unsigned long long int)&isr_default & 0x000000000000ffff)); IDT[t].offset_mid = (unsigned short)(((unsigned long long int)&isr_default & 0x00000000ffff0000) >> 16); IDT[t].offset_higherbits = (unsigned int)(((unsigned long long int)&isr_default & 0xffffffff00000000) >> 32); IDT[t].selector = 0x08; // 64-bit code segment from your GDT IDT[t].type_attr = 0x8e; // Present, ring 0, 64-bit interrupt gate IDT[t].zero = 0; IDT[t].ist = 0; } // Map IRQ1 (keyboard) to IDT index 33 using isr1 unsigned int irq1_idx = 33; IDT[irq1_idx].offset_lowerbits = (unsigned short)(((unsigned long long int)&isr1 & 0x000000000000ffff)); IDT[irq1_idx].offset_mid = (unsigned short)(((unsigned long long int)&isr1 & 0x00000000ffff0000) >> 16); IDT[irq1_idx].offset_higherbits = (unsigned int)(((unsigned long long int)&isr1 & 0xffffffff00000000) >> 32); // Load the IDT ONCE LoadIDT(); }
2. Missing EOI (End of Interrupt) in Keyboard Handler
When the keyboard interrupt fires, you must send an EOI signal to the PIC to notify it that the interrupt has been processed. Without this, the PIC will stop sending interrupts, and subsequent interrupts can trigger faults.
Add EOI to Your isr1 Handler:
Here's a corrected 64-bit interrupt handler example in NASM:
global isr1 isr1: ; Save all registers to avoid corrupting system state push rax push rbx push rcx push rdx push rsi push rdi push rbp push r8 push r9 push r10 push r11 push r12 push r13 push r14 push r15 ; Read keyboard scancode from port 0x60 in al, 0x60 ; Call your keyboard processing function (e.g., print the character to VGA buffer) call handle_keyboard_input ; Send EOI to PIC1 (since IRQ1 is managed by PIC1) mov al, 0x20 out 0x20, al ; Restore registers pop r15 pop r14 pop r13 pop r12 pop r11 pop r10 pop r9 pop r8 pop rbp pop rdi pop rsi pop rdx pop rcx pop rbx pop rax ; Return from interrupt (64-bit mode uses iretq) iretq
3. Verify LoadIDT() Implementation
Make sure your LoadIDT() function correctly loads the IDT pointer in 64-bit mode:
global LoadIDT LoadIDT: lidt [rdi] ; Load IDT from pointer passed in RDI ret
4. Add a Double Fault Handler for Debugging
To confirm if a double fault is really the issue, add a handler for IDT index 8 (double fault) that prints a message and halts the system:
global isr_double_fault isr_double_fault: ; Print "DF" to the VGA text buffer (red background, white text) mov qword [0xb8000], 0x4f464f44 hlt ; Halt instead of resetting iretq
Then map this handler to IDT index 8 in your load_idt_entry() function.
After applying these fixes, your keyboard interrupt should no longer trigger a double fault, and you'll be able to process keyboard input correctly.
内容的提问来源于stack exchange,提问作者Alexander

