实现 IRQ 时无效的操作代码异常

问题描述

我一直试图在基本内核开发方面松散地遵循 this tutorial。目前目标架构是i386。

IRQ 的实现给我带来了问题;每当我尝试将寄存器(定义为 Invalid Op Code)作为参数传递给函数时,我的中断处理程序都会报告一连串的 struct 异常。这是引发异常的中断处理程序的代码:

void interrupt_handler(registers_t all_registers) {
    // Printing exception's name
    kprint("interrupt_handler.c (l. 53) : Exception raised was:",0xB0);
    kprint(exception_messages[(int) all_registers.int_no],0xB0);
    kprint("\n",0xB0);

    // Celling test_handle to display the value of some registers
    // INVALID OP CODE ================>
    test_handle(all_registers); // works as expected if this line is commented out
   
}

void test_handle(registers_t all_registers) {
    kprint("interrupt_handler.c (l. 78) : Register DS contains",0xD0);
    kprint("to be implemented",0xD0);
}

结构registers_t定义如下(从教程中复制):

typedef struct {
   u32int ds;                                      /* Data segment selector */
   u32int edi,esi,ebp,esp,ebx,edx,ecx,eax;  /* Pushed by pusha. */
   u32int int_no,err_code;                        /* Interrupt number and error code (if applicable) */
   u32int eip,cs,eflags,useresp,ss;            /* Pushed by the processor automatically */
} __attribute__((packed)) registers_t;

尝试使用其他结构体调用函数,我发现 struct 中的变量数量很重要;任何具有 5 到 16 个 u32int 之间的 struct 都会触发异常。例如,以下结构在初始化并将空传递给 test_handle 时不会引发异常:

// Same as registers_t with less arguments
typedef struct {
    u32int ds;
    u32int edi,esi;
}  __attribute__((packed))  test_t;

反汇编.o 文件显示生成的代码使用mov 指令传递test_t 结构和movsd 传递registers_t。所以我怀疑是编译过程出了问题,因为编译器生成了无法识别的指令。

以下是我的Makefile的相关摘录:

C_FLAGS=-ffreestanding -nostartfiles -nodefaultlibs -fno-builtin -Wall -Wextra -fno-exceptions -m32 -target i386-pc-elf  -fno-rtti

# Compiling C code
%.o: %.c
    clang $(C_FLAGS) -c $< -o $@ 

# Linking
kernel/kernel.bin: $(O_FILES)
    ld -o $@ -Ttext 0x1000 $^ --oformat binary -m elf_i386

编译过程有什么问题吗?还是问题源于其他地方?

解决方法

@Ross Ridge 想通了(感谢他!)。下面的细节是我从the OSDev wiki

中学到的

流式 SIMD 扩展 (SSE) 扩展了 CPU 可识别的指令集,增加了约 70 条指令,并添加了更多寄存器。 SSE 需要先使能,然后才能使用其指令和寄存器。编译器生成的机器码可以包含 SSE 指令,因此需要启用 SSE。

在上面的代码中,将 struct 传递给函数被编译成机器代码,其中涉及 xmm0 寄存器,这是 SSE 的一部分。

下面给出了启用 SSE 的汇编代码(改编自 OSDev wiki)。我在进入 32 位保护模式之后和进入内核之前将它添加到我的引导加载程序中。这解决了问题!

mov eax,cr0        ; cr0 cannot be manipulated directly,manipulate eax instead
and ax,0xFFFB      ; clear coprocessor emulation CR0.EM
or ax,0x2          ; set coprocessor monitoring  CR0.MP
mov cr0,eax
mov eax,cr4        ; cr4 too cannot be manipulated directly
or ax,3 << 9       ; set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time
mov cr4,eax

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...