Ant*_*zka 5 c x86 assembly osdev interrupt-handling
我正在尝试使用OSDev等编写操作系统。现在,我一直在制作键盘中断处理程序。当我编译操作系统并运行内核时,qemu-system-i386 -kernel kernel/myos.kernel一切运行正常。当我将所有内容放入ISO映像并尝试使用进行运行时qemu-system-i386 -cdrom myos.iso,当我按一个键时它将重新启动。我认为这是由我的中断处理程序中的某些问题或错误的IDT条目引起的。
我的键盘处理程序(AT&T语法):
.globl keyboard_handler
.align 4
keyboard_handler:
pushal
cld
call keyboard_handler_main
popal
iret
Run Code Online (Sandbox Code Playgroud)
我在C中的主要处理程序:
void keyboard_handler_main(void) {
unsigned char status;
char keycode;
/* write EOI */
write_port(0x20, 0x20);
status = read_port(KEYBOARD_STATUS_PORT);
/* Lowest bit of status will be set if buffer is not empty */
if (status & 0x01) {
keycode = read_port(KEYBOARD_DATA_PORT);
if(keycode < 0)
return;
if(keycode == ENTER_KEY_CODE) {
printf("\n");
return;
}
printf("%c", keyboard_map[(unsigned char) keycode]);
}
}
Run Code Online (Sandbox Code Playgroud)
我用来加载的C函数:
void idt_init(void)
{
//unsigned long keyboard_address;
unsigned long idt_address;
unsigned long idt_ptr[2];
auto keyboard_address = (*keyboard_handler);
IDT[0x21].offset_lowerbits = keyboard_address & 0xffff;
IDT[0x21].selector = KERNEL_CODE_SEGMENT_OFFSET;
IDT[0x21].zero = 0;
IDT[0x21].type_attr = INTERRUPT_GATE;
IDT[0x21].offset_higherbits = (keyboard_address & 0xffff0000) >> 16;
/* Ports
* PIC1 PIC2
*Command 0x20 0xA0
*Data 0x21 0xA1
*/
write_port(0x20 , 0x11);
write_port(0xA0 , 0x11);
write_port(0x21 , 0x20);
write_port(0xA1 , 0x28);
write_port(0x21 , 0x00);
write_port(0xA1 , 0x00);
write_port(0x21 , 0x01);
write_port(0xA1 , 0x01);
write_port(0x21 , 0xff);
write_port(0xA1 , 0xff);
idt_address = (unsigned long)IDT ;
idt_ptr[0] = (sizeof (struct IDT_entry) * IDT_SIZE) + ((idt_address & 0xffff) << 16);
idt_ptr[1] = idt_address >> 16 ;
load_idt(idt_ptr);
printf("%s\n", "loadd");
}
Run Code Online (Sandbox Code Playgroud)
文件的组织方式与OSDev的Meaty Skeleton相同。我确实有其他引导程序。
根据经验,我认为这个问题与未设置 GDT 有关。通常,当有人说中断可以使用 QEMU 选项-kernel而不是真正版本的 GRUB 时,这通常与内核开发人员没有创建和加载自己的 GDT 有关。多重引导规范指出:
\n\n\n\n
\xe2\x80\x98GDTR\xe2\x80\x99\n 即使段寄存器按上述方式设置,\xe2\x80\x98GDTR\xe2\x80\x99 可能无效,因此操作系统映像不得加载任何段寄存器(即使只是重新加载相同的值!)直到它建立自己的\xe2\x80\x98GDT\xe2\x80\x99。
-kernel当使用带选项的QEMU 时,GDTR通常是有效的,但不能保证如此。当使用真实版本的 GRUB(安装到硬盘驱动器、虚拟映像、ISO 等)引导时,您可能会发现 GDTR 实际上无效。第一次尝试使用选择器重新加载任何段寄存器(即使它是相同的值)时,它可能会出错。当使用中断代码段(CS)将被修改,这可能会导致三重故障并重新启动。
此外,多重引导规范没有说明哪些选择器指向代码或数据描述符。由于多重引导规范不知道或保证 GDT 条目的布局,因此它给填充IDT条目带来了问题。每个IDT条目需要指定一个指向代码段的特定选择器。
\n\nOSDev 上的Meaty Skeleton教程不会设置中断,也不会修改任何段寄存器,因此代码可能适用于 QEMU 的-kernel选项和真实版本的 GRUB。在基础教程之上添加 IDT 和中断代码可能会导致您在使用 GRUB 引导时看到的失败。该教程可能应该更清楚地表明您应该设置自己的 GDT,而不是依赖多重引导加载程序设置的 GDT。