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

Keyboard interrupt handler not working in system iso

我正在尝试使用 OSDev 和其他人编写操作系统。现在,我一直在制作键盘中断处理程序。当我使用 qemu-system-i386 -kernel kernel/myos.kernel 编译 OS 和 运行 内核时,一切正常。当我将所有内容放入 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 相同。我有一个不同的引导加载程序。

根据经验,我认为此问题与未设置 GDT 有关。通常当有人说中断与 QEMU 的 -kernel 选项一起工作而不是真正版本的 GRUB 时,通常与内核开发人员没有创建和加载他们自己的 GDT 有关。 Mulitboot Specification 表示:

‘GDTR’ Even though the segment registers are set up as described above, the ‘GDTR’ may be invalid, so the OS image must not load any segment registers (even just reloading the same values!) until it sets up its own ‘GDT’.

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

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

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