You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

长模式(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) to isr1, 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 03:57:45