执行 IRQ 时出现无效操作码异常
Invalid Op Code Exception when implementing IRQs
我一直在尝试松散地遵循 this tutorial 基本内核开发。目前,目标架构是i386。
IRQ 的实现给我带来了问题;每当我尝试将寄存器(定义为 struct
)作为函数参数传递时,我的中断处理程序都会报告一连串 Invalid Op Code
异常。这是引发异常的中断处理程序的代码:
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
中学到的
Streaming SIMD Extension (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
我一直在尝试松散地遵循 this tutorial 基本内核开发。目前,目标架构是i386。
IRQ 的实现给我带来了问题;每当我尝试将寄存器(定义为 struct
)作为函数参数传递时,我的中断处理程序都会报告一连串 Invalid Op Code
异常。这是引发异常的中断处理程序的代码:
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
中学到的Streaming SIMD Extension (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