x86保护模式IDT配置后中断触发三重故障排查
根因汇总
从QEMU日志和代码逐行排查,共有5处直接触发三重故障的错误:
- IDT/IDTR结构体未加紧凑属性,内存布局不符合CPU要求
idt_t、idtr_t结构体未使用__attribute__((packed))修饰,编译器自动插入对齐填充字节:idt_t实际大小大于8字节,所有IDT表项错位,写入的门描述符位置全部偏移idtr_t结构中2字节limit和4字节base之间被插入2字节填充,lidt指令加载时读到错误的limit值(日志中IDT limit为0就是该问题直接导致),CPU认为IDT长度仅1字节,所有向量号≥1的中断/异常触发时直接抛出#GP(一般保护异常,向量号0xd)
- IDT数组长度不足
代码定义static idt_t idt[31]仅分配31个表项空间,索引范围0~30,但后续调用set_idt(31, ...)直接越界写内存,破坏相邻变量数据。 - ISR入口地址高低16位写反
set_idt函数中IDT表项的高低位偏移赋值逻辑完全颠倒,注释标注low字段存地址低16位、high存高16位,实际代码调用的宏取值完全相反,即使IDT位置正确,CPU也会跳转到错误地址执行ISR。 - 中断处理C函数触发页错误
isr.c中char* str = "0"; *str += interupt;直接修改只读数据段的字符串常量,X86保护模式下只读段不可写,执行该代码时直接触发#PF(页错误,向量号0xe),和日志中反复出现的INT=0x0e完全吻合。 - 8259可编程中断控制器未重映射
初始化流程中没有对8259PIC做中断向量重映射,默认硬件中断起始向量为0x8,和CPU异常向量号完全重叠,定时器、键盘等硬件中断会直接抢占CPU异常的IDT表项,触发异常嵌套。
修复步骤
按以下顺序修改即可解决三重故障问题:
- 给
idt_t、idtr_t结构体添加__attribute__((packed))属性,强制编译器不插入对齐填充,保证结构体内存布局和CPU要求完全一致。 - 修正IDT数组长度为256:
static idt_t idt[256] __attribute__((aligned(8)));,X86架构IDT必须包含256个表项,未使用的表项可以初始化为存在位为0的中断门,idtr.limit设置为sizeof(idt_t)*256 - 1。 - 修正
set_idt的地址赋值逻辑:
删除错误的idt[vector].low = offset & 0xFFFF; idt[vector].high = (offset >> 16) & 0xFFFF;low_16/high_16宏,直接做位运算避免宏定义错误。 - 修正
isr.c的打印逻辑,不要修改只读字符串:申请可写栈内存或全局缓冲区做数字转字符串,或实现通用的十六进制/十进制打印函数,禁止直接修改字符串常量。 - 在加载IDT后、开启中断前完成8259PIC重映射:将主片中断起始向量设为0x20,从片设为0x28,避开0~31的CPU异常向量号,配置中断屏蔽寄存器,中断处理完成后发送EOI命令。
- 补充TSS初始化和TR寄存器加载流程,保证异常发生时CPU可以正确切换内核栈,避免栈切换失败触发新的异常。
调试建议
修复后先不要直接开硬件中断,先手动执行int $3触发断点异常,确认可以正常进入断点处理函数,再逐个验证CPU异常处理逻辑,最后开启中断调试硬件IRQ。
内容的提问来源于stack exchange,提问作者Cyao
相关产品推荐
相关产品推荐

