键盘中断处理程序在系统ISO中不起作用

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相同。我确实有其他引导程序。

Mic*_*tch 3

根据经验,我认为这个问题与未设置 GDT 有关。通常,当有人说中断可以使用 QEMU 选项-kernel而不是真正版本的 GRUB 时,这通常与内核开发人员没有创建和加载自己的 GDT 有关。多重引导规范指出:

\n\n
\n

\xe2\x80\x98GDTR\xe2\x80\x99\n 即使段寄存器按上述方式设置,\xe2\x80\x98GDTR\xe2\x80\x99 可能无效,因此操作系统映像不得加载任何段寄存器(即使只是重新加载相同的值!)直到它建立自己的\xe2\x80\x98GDT\xe2\x80\x99。

\n
\n\n

-kernel当使用带选项的QEMU 时,GDTR通常是有效的,但不能保证如此。当使用真实版本的 GRUB(安装到硬盘驱动器、虚拟映像、ISO 等)引导时,您可能会发现 GDTR 实际上无效。第一次尝试使用选择器重新加载任何段寄存器(即使它是相同的值)时,它可能会出错。当使用中断代码段(CS)将被修改,这可能会导致三重故障并重新启动。

\n\n

此外,多重引导规范没有说明哪些选择器指向代码或数据描述符。由于多重引导规范不知道或保证 GDT 条目的布局,因此它给填充IDT条目带来了问题。每个IDT条目需要指定一个指向代码段的特定选择器。

\n\n

OSDev 上的Meaty Skeleton教程不会设置中断,也不会修改任何段寄存器,因此代码可能适用于 QEMU 的-kernel选项和真实版本的 GRUB。在基础教程之上添加 IDT 和中断代码可能会导致您在使用 GRUB 引导时看到的失败。该教程可能应该更清楚地表明您应该设置自己的 GDT,而不是依赖多重引导加载程序设置的 GDT。

\n