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

加载IDT时内核触发非零错误码双故障的排查与修复

双故障(Double Fault)错误码非零问题分析与修复方案

问题根源

双故障(中断号8)的错误码标准应为0,出现非零值(如十进制4144)的核心原因是ISR中断服务例程的栈帧结构不一致:

  • x86架构中,部分中断(如页故障、段保护故障)会由CPU自动压入错误码到栈中,而大部分中断(如除零、时钟中断)不会。
  • 若ISR的汇编入口未给无错误码的中断手动填充一个占位错误码,会导致不同中断的栈帧长度不一致。当双故障触发时,CPU读取错误码的位置会拿到栈中错位的垃圾数据(即4144),同时栈错位会引发ISR执行时的非法内存访问,最终触发双故障。

修复方案

1. 统一汇编层面的ISR入口栈帧

在isr.S中,为所有无错误码的中断添加手动压入0的操作,确保所有中断的栈帧结构完全一致:

; 无错误码的中断入口宏:手动压入占位错误码
%macro ISR_NOERRCODE 1
global isr%1
isr%1:
    cli
    push 0                  ; 填充占位错误码,对齐栈帧
    push %1                 ; 压入中断号
    jmp isr_common_stub
%endmacro

; 带错误码的中断入口宏:CPU已自动压入错误码,仅压入中断号
%macro ISR_ERRCODE 1
global isr%1
isr%1:
    cli
    push %1                 ; 压入中断号(错误码已由CPU压入栈)
    jmp isr_common_stub
%endmacro

; 实例化所有中断(注意区分带/不带错误码的中断)
; 带错误码的中断:8(Double Fault)、10、11、12、13、14
ISR_ERRCODE 8
ISR_ERRCODE 10
ISR_ERRCODE 11
ISR_ERRCODE 12
ISR_ERRCODE 13
ISR_ERRCODE 14

; 其余中断均为无错误码类型,示例:
ISR_NOERRCODE 0
ISR_NOERRCODE 1
ISR_NOERRCODE 2
; ... 实例化剩余所有中断号

2. 修正C语言中断帧结构体

确保中断帧结构体的字段顺序与汇编栈帧的压栈顺序完全匹配(x86栈向下生长,结构体字段顺序需从栈顶到栈底排列):

#include <stdint.h>

typedef struct {
    // 手动/CPU压入的内容
    uint32_t err_code;       // 错误码(占位或实际值)
    uint32_t int_no;         // 中断号
    // CPU自动压入的上下文
    uint32_t eip;
    uint32_t cs;
    uint32_t eflags;
    uint32_t useresp;
    uint32_t ss;
    // 通用寄存器(由isr_common_stub压入)
    uint32_t eax, ecx, edx, ebx, esp, ebp, esi, edi;
    // 数据段选择子(由isr_common_stub压入)
    uint32_t ds;
} interrupt_frame_t;

3. 完善ISR通用处理Stub

确保在调用C处理函数前正确切换到内核数据段,避免段错误:

isr_common_stub:
    ; 保存通用寄存器
    pusha
    ; 保存当前数据段选择子
    mov ax, ds
    push eax
    ; 切换到内核数据段(假设内核数据段选择子为0x10)
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    ; 调用C语言的ISR处理函数
    call isr_handler
    ; 恢复数据段选择子
    pop eax
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    ; 恢复通用寄存器
    popa
    ; 弹出错误码和中断号(共8字节)
    add esp, 8
    ; 恢复中断并返回
    sti
    iret

4. 验证IDT加载逻辑

确保idt.c中双故障中断的描述符正确指向对应的ISR入口(isr8),并正确设置特权级、段选择子等字段:

void idt_set_gate(uint8_t num, uint32_t base, uint16_t sel, uint8_t flags) {
    idt_entries[num].base_low = base & 0xFFFF;
    idt_entries[num].base_high = (base >> 16) & 0xFFFF;
    idt_entries[num].sel = sel;
    idt_entries[num].always0 = 0;
    idt_entries[num].flags = flags; // 双故障需设置为0x8E(内核特权级、32位中断门)
}

// 初始化IDT时:
idt_set_gate(8, (uint32_t)isr8, 0x08, 0x8E);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 23:33:09