高半内核和中断的三重故障

Triple fault with higher-half kernel and interrupts

我正在开发一个用于学习目的的小型操作系统(此处代码:https://github.com/davidedellagiustina/ScratchOS)。

我在 0xc0004000 有一个 higher half kernel 运行,映射到物理 0x4000。默认页面目录正确映射此区域,然后有一个条目用于标识映射第一个 1MB RAM(需要跳转到更高的一半)。进入上半区后,我删除页目录的第一个条目,使 TLB 无效,一切正常(如果我尝试访问地址 0x4000,我会遇到页错误,因此页目录更新成功)。

之后我为时间和键盘设置了一个中断向量和中断处理函数,最后我尝试创建一个新的更完整的页面目录并替换引导目录,但是替换时出现三重错误旧的。

目前我能避免三重错误的唯一方法是加载一个新的页面目录,该目录映射了第一个 1MB 的 RAM,甚至在加载后尝试删除页面目录的第一个条目(就像我所做的那样)在引导过程中)导致三重故障(因此标识映射 必须 留在那里)。

我发现在 切换页面 table 之前 禁用中断(或者甚至在切换到保护模式后从不重新启用它们,在启动时更早 [在文件 src/cpu/isr.c 底部的函数 irq_init() 中注释指令 asm volatile("sti"); 以测试]) 防止 CPU 三重故障。所以,我想我的 IDT(文件 src/cpu/idt.*src/cpu/isr.*)或我的 GDT(在切换到保护模式时在引导过程中重新加载,文件 src/boot/bootsect.asmsrc/boot/gdt.asm)。我已经仔细检查了这两个结构中加载的所有地址。我错过了什么?在我看来,它出现了三重故障,因为在某些时候,没有第一页的标识映射,它无法找到整个中断向量(加载到内核内部的虚拟内存中,因此映射到物理第一页),但是奇怪的行为是(见第一段)如果没有标识映射第一个 1MB 的页面目录是引导目录,它不会抱怨。

谁能帮我找出错误?

gdt 和 idt 寄存器都采用虚拟地址。当您禁用低映射时,这意味着对 gdt 或 idt 的下一次引用将导致页面错误。在删除统一页之前,您需要用它们的高地址等效值重新加载这两个寄存器。