1

我正在开发一个操作系统。我的 GDT 有三个条目。我创建了 IDT 并实现了 ISR 和 IQR。我还重新映射了图片。问题是在每个中断处理程序都遵循一般保护错误之后。这是调用中断的汇编代码:

.extern fault_handler
isr_common_stub: 
    pusha
    push %ds
    push %es
    push %fs
    push %gs
    movw $0x10,%ax # Load the Kernel Data Segment descriptor!
    movw %ax,%ds
    movw %ax,%es
    movw %ax,%fs
    movw %ax,%gs
    movl %esp,%eax # Push us the stack
    pushl %eax
    movl $fault_handler, %eax
    call *%eax     # A special call, preserves the 'eip' register
    popl %eax
    popl %gs # I discovered that the error occures on this line
    popl %fs
    popl %es
    popl %ds
    popa
    addl $8,%esp   # Cleans up the pushed error code and pushed ISR number
    iret           # pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP!

.extern irq_handler
irq_common_stub: 
    pusha
    push %ds
    push %es
    push %fs
    push %gs
    movw $0x10,%ax
    movw %ax,%ds
    movw %ax,%es
    movw %ax,%fs
    movw %ax,%gs
    movl %esp,%eax
    pushl %eax
    movl $irq_handler, %eax
    call *%eax
    popl %eax
    popl %gs # I discovered that the error occures on this line
    popl %fs
    popl %es
    popl %ds
    popa
    addl $8,%esp
    iret

我发现了一些奇怪的东西。当我使用 QEMU 作为磁盘映像运行操作系统时.iso,它不起作用。但是当我通过指定选项将其称为内核时-kernel,它按预期工作。我决定更改以下代码:

    popl %gs # I discovered that the error occures on this line
    popl %fs
    popl %es
    popl %ds

我将上面的代码更改为:

    pop %gs
    pop %fs
    pop %es
    pop %ds

我仍然得到GPF。难道我做错了什么?有什么建议么?

4

2 回答 2

3
   addl $8,%esp   # Cleans up the pushed error code and pushed ISR number

您的isr_common_stub例程认为错误代码将始终存在。有些故障不会推送错误代码!只有与中断号 10、11、12、13、14 和 17 相关的故障才使用错误代码。

http://www.brokenthorn.com/Resources/OSDev15.html我们了解到:

如果要在较低特权级别(描述符的第 42-45 位)执行处理程序,则会发生堆栈切换。
处理程序要使用的堆栈的段选择器和堆栈指针从当前正在执行的任务的 TSS 中获得。处理器将中断处理程序的堆栈段选择器和堆栈指针压入这个新堆栈上。处理器将EFLAGS、CS和EIP的当前状态保存在新堆栈上。
如果异常导致错误代码被保存,则错误代码在 EIP 之后被推送到新的堆栈上。

并且

如果处理程序要以相同的特权级别执行(当前特权级别(cpl)与(描述符的第 42-45 位)相同)
处理器将 EFLAGS、CS、EIP 的当前状态保存在当前堆栈上。
如果异常导致错误代码被保存,错误代码在 EIP 之后被压入当前堆栈

知道调用中断处理程序时堆栈是如何被压入的,以及哪些异常也会压入错误代码是非常重要的。

于 2016-05-04T10:53:53.747 回答
3

如果我的旧记忆正确地为我服务,则段寄存器是 16 位,并且您正在尝试执行popl弹出 32 位值的操作……因此这是一个问题。

由于您正在执行push %gs上述操作,因此您还应该执行pop %gs. 与其他寄存器相同,请确保pop以与您相同的方式使用push它们。

于 2016-05-04T06:09:17.913 回答