由于堆栈指针无效,无法调用程序集页面错误处理程序

Assembly page fault handler cannot be called due to invalid stack pointer

当我的页面错误处理程序中断被调用时(它应该挂起系统),在它被调用之前有一些变量被压入堆栈。我启用了虚拟内存,当我设置一个无效的堆栈指针 (esp) 并且调用 int14 处理程序时,它会立即导致另一个页面错误等等。我该如何解决这种情况?

我的 int14 代码:

isr14:
    ; interrupt handler for isr14
    jmp $
    iretd

导致它中断的代码:

mov esp, 0x1000 ; 0x1000 is not mapped in the VM directory
push dword 'A'
jmp $

我的 IDT 部分 table:

irq14:
    dw isr14
    dw 0x0008
    db 0x00
    db 10101110b
    dw 0x0000

irq15:
........

How should I resolve this situation?

我会通过回避来解决这种情况 - 首先不要让内核有一个狡猾的堆栈指针(并且不要让内核堆栈被发送到交换 space,不要对 "auto-growing kernel stack" 等使用页面错误)。请注意,如果 user-space(在 CPL=3)发生页面错误,CPU 将自动切换到内核堆栈,因此 user-space 是否有一个狡猾的堆栈并不重要指针。

备选方案是:

  • 当内核代码 (CPL=0) 导致页面错误时强制执行内核堆栈切换。这可以使用硬件任务切换(保护模式)或页面错误异常处理程序的 IST 机制(长模式)来完成。这将是恢复的最佳选择(例如,更容易找出问题所在,修复它,然后 return)。

  • 当内核代码 (CPL=0) 导致双重错误时强制执行内核堆栈切换。这可以使用硬件任务切换(保护模式)或双故障异常处理程序的 IST 机制(长模式)来完成。这将是性能的最佳选择(不会增加正常页面错误的开销)。

注意 1:请注意,硬件任务 switching/task 门和 IST 都不可重入。对于硬件任务切换,如果在处理第一个页面错误时发生第二个页面错误,您将遇到一般保护错误(因为 "page fault task" 正忙);对于 IST,如果在处理第一个页面错误时发生第二个页面错误,则第二个页面错误将 trash/overwrite 第一个页面错误的堆栈并使其无法恢复。理论上,您可以通过尽快切换到不同的任务或不同的堆栈来缓解这些问题,但那是 complicated/messy 并且可能会导致更多问题。

注意 2:您可能最终会使用硬件任务切换或 IST 结合避免和双重故障;使用双故障处理程序 "freeze system and dump info/panic" 作为灾难性内核故障的一般回退(应该避免但没有)。

注3:如果要支持"auto-growing kernel stacks";您可以改用 "stack probes" - 基本上,在将内存用于堆栈之前,只需从 "future stack" 执行虚拟 read/s (在函数尾声中),以便在仍有足够的内核堆栈时发生页面错误留给页面错误处理程序。