我正在尝试使用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
我在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]);
}
}
我用来加载的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");
}
文件的组织方式与OSDev's Meaty Skeleton相同。我确实有不同的 bootstrap 。
最佳答案
根据经验,我认为此问题与未建立GDT有关。通常,当有人说中断使用QEMU的-kernel
选项而不是GRUB的真实版本时,它通常与内核开发人员未创建和加载自己的GDT有关。 Mulitboot Specification说:
当将QEMU与-kernel
选项一起使用时,GDTR通常是有效的,但不能保证那样。使用GRUB的实际版本(安装到硬盘,虚拟镜像,ISO等)启动时,您可能会发现GDTR实际上无效。首次尝试使用选择器重新加载任何段寄存器(即使其值相同)也可能会出错。使用中断时,将修改代码段(CS),这可能会导致三次故障并重新启动。
同样,Multiboot规范也没有说明哪个选择器指向代码或数据描述符。由于Multiboot规范不知道或不能保证GDT条目的布局,因此在填充IDT条目时会遇到问题。每个IDT条目都需要指定一个指向代码段的特定选择器。
OSDev上的Meaty Skeleton教程没有设置中断,也没有修改任何段寄存器,因此代码可能与QEMU的-kernel
选项和GRUB的实际版本一起使用。在基础教程的顶部添加IDT和中断代码可能会导致使用GRUB引导时看到的失败。该教程可能会更清楚地表明您应该设置自己的GDT,而不要依赖Multiboot加载程序设置的GDT。