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

ISR无法正常返回:Bochs报check_cs(0x0020)无效代码段错误

问题根源

  1. 直接将C函数作为IDT中断处理函数的错误:x86中断触发时CPU会生成特定栈帧(包含EIP、CS、EFLAGS等),C函数的调用栈帧结构与中断栈帧不兼容。你直接把timer_isr这个C函数注册到IDT中,未通过汇编Stub处理栈帧转换,导致执行iret时栈状态异常,CS寄存器被设置为无效值。
  2. EOI发送错误:定时器IRQ0属于主PIC(PIC1),无需向从PIC(PIC2)发送EOI,多余操作可能干扰中断流程。
  3. C函数内直接使用iret的错误:C函数执行过程中会修改栈指针(如压入EBP、分配局部变量),破坏了中断栈帧结构,此时执行iret无法正确恢复中断前的CPU状态。

修复步骤

1. 添加定时器ISR的汇编Stub

创建汇编文件(如isr_stubs.s),添加对应IRQ0的Stub:

[GLOBAL irq0_stub]
[EXTERN timer_isr]

irq0_stub:
    ; 保存所有通用寄存器
    pusha
    ; 保存段寄存器
    push ds
    push es
    push fs
    push gs
    ; 设置内核数据段选择子(假设GDT中内核数据段选择子为0x10)
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    ; 调用C语言实现的ISR逻辑
    call timer_isr
    ; 恢复段寄存器
    pop gs
    pop fs
    pop es
    pop ds
    ; 恢复通用寄存器
    popa
    ; 仅向主PIC发送EOI(IRQ0属于主PIC)
    mov al, 0x20
    out 0x20, al
    ; 执行中断返回
    iret

2. 修改C语言ISR代码

移除timer_isr中的EOI发送和iret指令,仅保留业务逻辑:

void timer_isr() {
    println("You're in the timer isr.");
}

3. 更新IDT初始化代码

将IRQ0的IDT描述符指向汇编Stubirq0_stub,而非直接指向C函数:

void init_idt() {
    idtr.base = (uintptr_t)&idt[0];
    idtr.limit = (u16)sizeof(idtEntry) * IDT_MAX_DESCRIPTORS - 1;

    for (u8 vector = 0; vector < 32; vector++) {
        idt_set_descriptor(vector, isr_stub_table[vector], 0x8E);
        vectors[vector] = true;
    }

    // 替换为汇编Stub的地址
    idt_set_descriptor(0x20, irq0_stub, 0x8E);
    vectors[0x20] = true;

    asm volatile ("lidt %0" : : "m"(idtr));
    asm volatile ("sti");
}

4. 编译链接时包含汇编Stub

确保构建脚本(如Makefile)将汇编文件编译并链接到内核中,示例片段:

isr_stubs.o: isr_stubs.s
    nasm -f elf32 isr_stubs.s -o isr_stubs.o

额外说明

  • 确认GDT中内核代码段选择子为0x08、数据段为0x10(osdev教程常见配置),若你的GDT配置不同,需调整汇编Stub中的段选择子值。
  • 所有硬件中断(IRQ0-15)都需要类似的汇编Stub处理栈帧转换,不能直接用C函数注册到IDT。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 08:15:12