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

DrC*_*ore 1 x86 assembly kernel interrupt virtual-machine

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

我的 int14 代码:

isr14:
    ; interrupt handler for isr14
    jmp $
    iretd
Run Code Online (Sandbox Code Playgroud)

导致它中断的代码:

mov esp, 0x1000 ; 0x1000 is not mapped in the VM directory
push dword 'A'
jmp $
Run Code Online (Sandbox Code Playgroud)

我的 IDT 表的部分:

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

irq15:
........
Run Code Online (Sandbox Code Playgroud)

Bre*_*dan 5

我应该如何解决这种情况?

我会通过使用避免来解决这种情况 - 首先不要让内核有一个狡猾的堆栈指针(并且不要让内核堆栈被发送到交换空间,不要将页面错误用于“自动增长的内核”堆栈”等)。请注意,如果在用户空间(在 CPL=3 时)发生页面错误,CPU 将自动切换到内核堆栈,因此用户空间是否有一个狡猾的堆栈指针并不重要。

替代方案是:

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

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

注 1:请注意,硬件任务切换/任务门和 IST 都不是可重入的。对于硬件任务切换,如果在您处理第一个页面错误时发生第二个页面错误,您将得到一个通用保护错误(因为“页面错误任务”正忙);对于 IST,如果在您处理第一个页面错误时发生第二个页面错误,第二个页面错误将破坏/覆盖第一个页面错误的堆栈,并使其无法恢复。理论上,您可以通过尽快切换到不同的任务或不同的堆栈来缓解这些问题,但这很复杂/混乱,并且可能会导致更多问题。

注 2:您可能会使用硬件任务切换或 IST 获得避免和双重故障的组合;双故障处理程序执行“冻结系统并转储信息/恐慌”作为灾难性内核故障的通用后备(本来应该避免但没有)。

注3:如果你想支持“自动增长的内核堆栈”;您可以使用“堆栈探测”来代替 - 基本上,在使用堆栈内存之前,只需从“未来堆栈”中执行虚拟读取/s(在函数尾声中),以便在仍有足够的内核堆栈留给页面时发生页面错误故障处理程序。