Memory management 删除x86内核中的标识映射时出现的问题

Memory management 删除x86内核中的标识映射时出现的问题,memory-management,x86,kernel,paging,osdev,Memory Management,X86,Kernel,Paging,Osdev,我正在做一些操作系统开发,这是我的目标。我已启用分页,并希望删除标识映射。以前,我有两个映射,0-4M的标识映射和虚拟地址0xC0000000处的内核映射到物理地址1M。我的引导加载程序在跳转到内核之前执行此操作,内核负责删除标识映射。我的堆栈指针现在的值为0x90000。我的策略如下: 进入内核后,我使用递归页面表来访问(我得到它们的虚拟地址:)我的页面目录和所有其他页面表。递归页面表:页面目录的最后一个条目指向自身 我想重新映射堆栈并给它一个虚拟地址。由于递归页面技术使用0xFFC00000

我正在做一些操作系统开发,这是我的目标。我已启用分页,并希望删除标识映射。以前,我有两个映射,0-4M的标识映射和虚拟地址0xC0000000处的内核映射到物理地址1M。我的引导加载程序在跳转到内核之前执行此操作,内核负责删除标识映射。我的堆栈指针现在的值为0x90000。我的策略如下:

进入内核后,我使用递归页面表来访问(我得到它们的虚拟地址:)我的页面目录和所有其他页面表。递归页面表:页面目录的最后一个条目指向自身

我想重新映射堆栈并给它一个虚拟地址。由于递归页面技术使用0xFFC00000-0xFFFFFF,因此我使用0xFFC00000作为堆栈的虚拟地址,并将其映射到0x90000物理地址

然后我重新初始化gdt

现在,我的系统状态如下:

(qemu) info registers 
EAX=000241a0 EBX=c00019de ECX=00000001 EDX=000241a0
ESI=00008137 EDI=00103800 EBP=ffc00000 ESP=ffc00000
EIP=c00019de EFL=00000087 [--S--PC] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT=     c0004100 00000027
IDT=     00000000 000003ff
CR0=80000011 CR2=00000000 CR3=0009c000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000 
DR6=ffff0ff0 DR7=00000400
EFER=0000000000000000
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
XMM00=00000000000000000000000000000000 XMM01=00000000000000000000000000000000
XMM02=00000000000000000000000000000000 XMM03=00000000000000000000000000000000
XMM04=00000000000000000000000000000000 XMM05=00000000000000000000000000000000
XMM06=00000000000000000000000000000000 XMM07=00000000000000000000000000000000
堆栈重新映射代码:

STACK equ 0xFFC00000
STACK_PHY equ 0x90000

global refresh_stack ;A solution for switching stacks

refresh_stack:
    mov ebx,[esp] ;Return address
    mov esp,STACK
    mov ebp,STACK
    jmp ebx
我想删除标识映射,如下所示:

void remove_identity_map()  //This would remove the 4M identity map
{
         if (entry_is_present(_page_directory[0]))
                _page_directory[0] = 0;  //Unmapping the whole of 4M

         flush_tlb();
}
其中,tlb:

flush_tlb:
    mov eax,cr3
    mov cr3,eax
    ret
最小可复制示例(大致)

出问题的地方是当我冲洗_tlb()时。如果我注释掉这一行,代码将按预期工作,但我们将看到缓存副本。但是,当我保留它时,系统(仿真器)会从BIOS重新启动!(三重故障??)

另外,我正在使用qemu,在刷新tlb之后,我尝试了(;;)的
使用qemu监视器来
信息tlb
。。。它似乎没有被冲红。以前的所有条目都存在

我哪里会出错

注意:当我使用gdb调试(单步)时,在刷新tlb后,访问地址0xFFC00000是不可能的。。。但这是有道理的,我刚刚删除了与页表0对应的页目录条目


更新:我还没有重新映射视频内存!对于文本模式,我仍在访问0xB8000。由于我的异常处理程序也会打印到屏幕上,这肯定是三重错误的根源

看来我忘了重新映射视频内存了!我仍然在访问0xb8000的VGA文本模式。问这个问题的原因是,我没有在1M以下找到这个通道。如果我有,我就不会问问题了

因此,对于希望在启用分页后删除其身份映射的人来说,这是一个很好的教训:

  • 重新映射gdt
  • 重新映射idt
  • 重映射堆栈
  • 重新映射视频存储

系统(仿真器)从BIOS重新启动!您是否尝试过使用调试器来查看是什么错误导致了这种情况?它是否在指令中紧跟在
invlpg
mov cr3,eax
之后?是否存在页面错误和试图从无效堆栈传递该错误的双重错误?从而导致三重fault@PeterCordes我这里不使用invlpg。。这是一次彻底的刷新。此外,qemu不会刷新它,即刷新后信息tlb不会更改_tlb@PeterCordes,我有一些异常处理程序,它们基本上用于(;);在显示异常名称之前,如果由于堆栈地址无效而导致出现三重错误,则异常处理程序无法运行,正如我所描述的。这就是为什么您应该使用调试器。e、 g.BOCHS有一个内置的调试器,我想它会告诉你,如果机器出现三重故障,或者在你当前设置中单步通过
mov
到CR3,那么如果它很快在其中一条指令上重置,就会告诉你一些事情。您的问题似乎没有显示哪个指令出错,因此这不是一个很好的答案。如果你确实知道哪个指令是个问题,就把它放在问题中。如果不是,请找出。看起来IDT base仍然是0,所以任何类型的中断或故障都将是三重故障,除非您为此添加了映射。中断是否被禁用?
void kmain()
{
set_recursive_map();
refresh_stack();
install_gdt(); //The standard 4 entries Data/Code * User/kernel
remove_identity_map();
   .......
   .......
}