在Freeldr中存在很多全局变量他们在分页机制启动前后,地址有什么样变化?比如Freeldr在启动保护模式后设置堆栈地址以及引导参数块LoaderBlock当然还有很多的所以讨论这两个是他们有着特殊意义这个特殊意义是在启动分页前后都必须使用并且在权利移交过程中Ntoskrnl在最初化阶段必须从freeldr传递过来LoaderBlock中将些参数计算转换成Ntoskrnl内部使用信息同时还会使用freeldr建立这个堆栈来传递参数直到内核第个进程建立时内核才建立自己堆栈空间
Freeldr在切换到保护模式后化堆栈该位于文件
arch.S (\reactos-0.3.5\boot\Freeldr\Freeldr\arch\i386)
/*
* 切换到保护模式例程
* it destroys eax
*/
EXTERN(switch_to_prot)
.code16
cli /* None of these */
/* We don't know what values are currently */
/* in the segment registers. So we are */
/* going to reload them with sane values. */
/* Of course CS has to already be valid. */
/* We are currently in real-mode so we */
/* need real-mode segment values. */
xorw %ax,%ax
movw %ax,%ds
movw %ax,%es
movw %ax,%fs
movw %ax,%gs
movw %ax,%ss
/* Get the address off the stack */
popw (code32ret)
/* Save 16-bit stack poer */
movw %sp,stack16
/* Load the GDT */
lgdt gdtptr
/* Load the IDT */
lidt i386idtptr
/* Enable Protected Mode */
mov %cr0,%eax
orl $CR0_PE_SET,%eax
mov %eax,%cr0
/* Clear prefetch queue & correct CS */
ljmp $PMODE_CS, $inpmode
.code32
inpmode:
/* 化各个段选择子*/
movw $PMODE_DS,%ax /* PMODE_DS = 0x10PMode data selector, 基址 0 大小 4g */
movw %ax,%ds
movw %ax,%es
movw %ax,%fs
movw %ax,%gs
movw %ax,%ss
// 化保护模式下堆栈
/*
* 其中STACK32ADDR 在Arch.h (\reactos-0.3.5\boot\Freeldr\Freeldr\) 文件中定义如下
* # STACK32ADDR 0x78000 /* The 32-bit stack top will be at 7000:8000, or 0x78000
* 在分页前物理地址为0x78000如果esp不变话在启动分页后这个地址应该就成了虚拟地址
*/
movl stack32,%esp
在分页前esp物理地址为如果为0x78000话在启动分页后这个地址应该就成了虚拟地址它32位 2进制为:(0x78000)16=(0000000000-0001111000-000000000000)2这个地址已经按照页目录页表和页内偏移格式排列好了那么该虚拟地址代表索引为0页目录索引为120页表以及页内偏移为0
这里有意思是分页前代表物理地址分页后该地址却成了虚拟地址因此在启动分页前化页表目录和页表时必须将物理地址映射到数值相同虚拟地址中这样用同个地址来访问才不会出错
如果希望虚拟地址0x78000在启动分页后依然定位到分页前物理地址0x78000话来看看这个物理地址对应于那个页内页4K其实这个地址左移12位就是物理地址对应页框号即(120)10也就是说只要将 2级页表120项页框号设置成120就行了好了看看FrLdrSetupPageDirectory看看页目录第0项所设置页表以及该页表120项是如何设置就知道了
# LowMemPageTableIndex 0 // 页目录低地址索引号0项表示地址范围0x000000000 ~ 0x 003FFFFF
// 共4M空间
# PFN_SHIFT 12 // 页框号移位数
# PAGE_SIZE 4096 // 页面大小4k页
# PaToPfn(p) \ // 将个地址转换成该地址对应页框号
((p) >> PFN_SHIFT)
VOID
FASTCALL
FrLdrSetupPageDirectory(VOID)
{
……
/*
* 取得页目录地址页目录项和页表项结构相同因此
* PageDir在前面用于化页目录后面用于化页表项
*/
PageDir = (PPAGE_DIRECTORY_X86)&startup_pagedirectory;
/*
* 在页目录项(PDE)中映射对应页表地址
* 从低地址开始首先是Low Memory PDE
*/
PageDir->Pde[LowMemPageTableIndex].Valid = 1;
PageDir->Pde[LowMemPageTableIndex].Write = 1;
/*
* lowmem_pagetable位于汇编文件mb.S (\reactos\boot\Freeldr\Freeldr\arch\i386)中
* 大小为4096页表项宏PaPtrToPfn用于取得lowmem_pagetable物理页地址
* 这样就建立了个虚拟地址从0x000000000 ~ 0x 003FFFFF映射共4M空间不过由于页表项还没有映射
* 因此这些地址还没有落实到实际
* 注意LowMemPageTableIndex = 0表示页目录0项页表为lowmem_pagetable
*/
PageDir->Pde[LowMemPageTableIndex].PageFrameNumber = PaPtrToPfn(lowmem_pagetable);
……
/*
* 好了现在正式将虚拟地址从0x000000000 ~ 0x 003FFFFF建立页表项映射
* 共4M空间落实到物理地址0到1023页
* 那么该页表120项页框号是多少呢?
* 如果当 I = 120 话那么
* PageDir->Pde[120].PageFrameNumber = PaToPfn(120 * PAGE_SIZE);
* 其中120 * PAGE_SIZE = (0x78000)16
*/
PageDir = (PPAGE_DIRECTORY_X86)&lowmem_pagetable;
for (i=0; i<1024; i)
{
PageDir->Pde[i].Valid = 1;
PageDir->Pde[i].Write = 1;
PageDir->Pde[i].Owner = 1;
PageDir->Pde[i].PageFrameNumber = PaToPfn(i * PAGE_SIZE);
}
……
}
从上面代码片段可以看出0x78000这个物理地址却被映射到了0x78000这个虚拟地址上其实不仅仅是堆栈地址0x78000所有从0x000000000 ~ 0x 003FFFFF这个范围内地址他们物理地址都等于他们虚拟地址而Freeldr自己就在1M地址范围内并且内存管理在分配动态内存是也应该在在4M地址范围内分配这样这些地址在分页机制前后才能正常访问当然LoaderBlock也在这那个地址范围内了
从上面分析发现这些变量虽然地址数值没有改变但是它逻辑意义已经改变了分页前位物理地址分页后为虚拟地址
最新评论