由于堆栈指针无效,无法调用程序集页面错误处理程序
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 (在函数尾声中),以便在仍有足够的内核堆栈时发生页面错误留给页面错误处理程序。
当我的页面错误处理程序中断被调用时(它应该挂起系统),在它被调用之前有一些变量被压入堆栈。我启用了虚拟内存,当我设置一个无效的堆栈指针 (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 (在函数尾声中),以便在仍有足够的内核堆栈时发生页面错误留给页面错误处理程序。