我正在进行一些操作系统开发,这是我的目标。我已经启用了分页,并希望删除标识映射。以前,我有两个映射,0-4M的标识映射和虚拟地址0xC0000000映射到物理地址1M的内核。我的引导加载程序在跳转到内核之前执行此操作,内核负责删除标识映射。我的堆栈指针现在的值为0x90000。我的策略如下:
进入内核后,我使用递归页面表来访问我的页面目录和所有其他页面表(我得到它们的虚拟地址:当你失去页面目录的虚拟地址时会发生什么?(。递归页表:页目录的最后一个条目指向它自己。
我想重新映射堆栈并给它一个虚拟地址。由于递归页面技术使用了0xFFC00000-0xFFFFFFFF,因此我使用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();
}
其中flush_tlb:
flush_tlb:
mov eax,cr3
mov cr3,eax
ret
一个最小可重复的例子(大致(
void kmain()
{
set_recursive_map();
refresh_stack();
install_gdt(); //The standard 4 entries Data/Code * User/kernel
remove_identity_map();
.......
.......
}
它出错的地方是当我flush_tlb((时。如果我注释掉这一行,代码会按预期工作,但我们会看到缓存的副本。然而,当我保留它时,系统(模拟器(会从BIOS重新启动!(三重故障??(
此外,我正在使用qemu,在刷新tlb之后,我尝试了for(;;);
将qemu监视器用于info tlb
。。。它似乎没有脸红。以前的所有条目都存在。。
我哪里会出错?
注意:当我用gdb调试(单步(时,在我刷新tlb后,访问地址0xFFC00000是不可能的。。。但这确实有道理,我只是删除了与页面表0.相对应的页面目录条目
更新:我还没有做的一个重映射是视频内存!对于文本模式,我仍在访问0xB8000。由于我的异常处理程序也打印到屏幕上,这肯定是三重错误的来源!!!
看来我忘了重新映射视频内存了!我仍在访问0xb8000以获得VGA文本模式。这个问题的原因是我没有识别出低于1M的访问权限。如果我有,我就不会问任何问题了;(。
因此,对于那些希望在启用寻呼后删除身份图的人来说,这可能是一个很好的教训:
- 重映射gdt
- Remap idt
- 重映射堆栈
- 重映射视频记忆(_M(