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

无法用PIC屏蔽IRQ:x86_64保护模式内核中断异常求助

问题分析:x86_64保护模式下键盘中断配置引发持续INT8(双故障)

问题描述

我正在x86_64保护模式内核中配置键盘中断,已完成IDT加载、PIC重映射,键盘IRQ能正常工作,但持续触发INT8(疑似双故障)。尝试屏蔽定时器中断(IRQ0)及所有中断均无法解决问题。

相关代码

pic.h

#pragma once
#define PIC1_COMMAND 0x20
#define PIC1_DATA    0x21
#define PIC2_COMMAND 0xA0
#define PIC2_DATA    0xA1

#define NEW_IRQ_0 0x20
#define NEW_IRQ_8 0x28

#define ICW1_INIT    0x10
#define ICW1_ICW4    0x01
#define ICW4_8086    0x01

void remap_pic()
{
    uint8_t pic1_mask, pic2_mask;

    pic1_mask = inb(PIC1_DATA);
    pic2_mask = inb(PIC2_DATA);

    outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4);
    io_wait();
    outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4);
    io_wait();
    outb(PIC1_DATA, NEW_IRQ_0);
    io_wait();
    outb(PIC2_DATA, NEW_IRQ_8);
    io_wait();
    outb(PIC1_DATA, 0x4);
    io_wait();
    outb(PIC2_DATA, 0x2);
    io_wait();
    outb(PIC1_DATA, ICW4_8086);
    io_wait();
    outb(PIC2_DATA, ICW4_8086);
    io_wait();

    outb(PIC1_DATA, pic1_mask);
    outb(PIC2_DATA, pic2_mask);
}

idt.c

void idt_init() {
    for(uint64_t t = 0; t < 256; t++) {
        _idt[t].zero = 0;
        _idt[t].offset_low = (uint16_t)(((uint64_t)&isr1 & 0x000000000000ffff));
        _idt[t].offset_mid = (uint16_t)(((uint64_t)&isr1 & 0x00000000ffff0000) >> 16);
        _idt[t].offset_high = (uint32_t)(((uint64_t)&isr1 & 0xffffffff00000000) >> 32);
        _idt[t].ist = 0;
        _idt[t].selector = 0x08;
        _idt[t].types_attr = 0x8e;
    }
    remap_pic();
    outb(0x21, 0xfd);
    outb(0xa1, 0xff);
    load_idt();
}

extern void isr1_handler() {
    uint8_t scancode = inb(0x60);
    print_char(SCANCODE_LOOKUP_TABLE[scancode]);
    outb(0x20, 0x20);
    outb(0xa0, 0x20);
}

idt_asm.asm

extern _idt
idtDescriptor:
    dw 4095
    dq _idt

    %macro PUSHALL 0
        push rax
        push rcx
        push rdx
        push r8
        push r9
        push r10
        push r11
    %endmacro

    %macro POPALL 0
        pop r11
        pop r10
        pop r9
        pop r8
        pop rdx
        pop rcx
        pop rax
    %endmacro

extern isr1_handler
isr1:
    PUSHALL
    call isr1_handler
    POPALL
    iretq
    global isr1

load_idt:
    lidt[idtDescriptor]
    sti
    ret
    global load_idt

问题根源

  1. IDT全局错误映射:idt_init中循环将所有256个IDT条目都指向isr1,包括CPU异常(如INT8双故障、页错误等)的条目。isr1仅适配外部中断处理,无法处理CPU异常的栈帧要求——部分异常会自动向栈中压入错误码,isr1的PUSHALL/POPALL和iretq流程会导致栈不平衡,触发双故障。
  2. 不必要的EOI发送:键盘中断属于主PIC(IRQ1),仅需向主PIC发送EOI指令,isr1_handler中同时向从PIC发送EOI会干扰PIC正常状态,可能引发额外中断异常。

解决步骤

  1. 拆分IDT条目配置:

    • 为CPU异常(0~0x1F)编写专用处理例程,区分带错误码和不带错误码的异常:带错误码的异常需要在PUSHALL前弹出错误码(或在处理函数中处理),保证栈平衡。
    • 仅将键盘中断对应的IDT条目(重映射后IRQ1对应0x21)指向isr1,其他条目配置为默认异常处理例程。

    示例修正idt_init:

    void idt_init() {
        // 初始化所有条目为默认异常处理例程(需提前定义isr_default)
        for(uint64_t t = 0; t < 256; t++) {
            _idt[t].zero = 0;
            _idt[t].offset_low = (uint16_t)(((uint64_t)&isr_default & 0x000000000000ffff));
            _idt[t].offset_mid = (uint16_t)(((uint64_t)&isr_default & 0x00000000ffff0000) >> 16);
            _idt[t].offset_high = (uint32_t)(((uint64_t)&isr_default & 0xffffffff00000000) >> 32);
            _idt[t].ist = 0;
            _idt[t].selector = 0x08;
            _idt[t].types_attr = 0x8e;
        }
        // 单独配置键盘中断条目
        uint64_t irq1_idx = NEW_IRQ_0 + 1; // 0x21
        _idt[irq1_idx].offset_low = (uint16_t)(((uint64_t)&isr1 & 0x000000000000ffff));
        _idt[irq1_idx].offset_mid = (uint16_t)(((uint64_t)&isr1 & 0x00000000ffff0000) >> 16);
        _idt[irq1_idx].offset_high = (uint32_t)(((uint64_t)&isr1 & 0xffffffff00000000) >> 32);
        
        remap_pic();
        outb(0x21, 0xfd); // 仅开启IRQ1,屏蔽其他主PIC中断
        outb(0xa1, 0xff); // 屏蔽所有从PIC中断
        load_idt();
    }
    
  2. 修正EOI发送逻辑:
    修改isr1_handler,仅向主PIC发送EOI:

    extern void isr1_handler() {
        uint8_t scancode = inb(0x60);
        print_char(SCANCODE_LOOKUP_TABLE[scancode]);
        outb(0x20, 0x20); // 仅主PIC发送EOI
    }
    
  3. 实现默认异常处理例程:
    编写通用异常处理函数,用于处理未自定义的异常,避免因未处理的异常触发双故障。示例汇编实现:

    extern isr_default_handler
    isr_default:
        PUSHALL
        call isr_default_handler
        POPALL
        iretq
        global isr_default
    

内容的提问来源于stack exchange,提问作者Zpher

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 08:10:57