在二级引导器中建立MMU页表数据,目的就是要在内核加载运行之初开启长模式。
为了简化编程,使用长模式下的2MB分页方式。
#define KINITPAGE_PHYADR 0x1000000
void init_bstartpages(machbstart_t *mbsp)
{
//顶级页目录
u64_t *p = (u64_t *)(KINITPAGE_PHYADR); //16MB地址处
//页目录指针
u64_t *pdpte = (u64_t *)(KINITPAGE_PHYADR + 0x1000);
//页目录
u64_t *pde = (u64_t *)(KINITPAGE_PHYADR + 0x2000);
//物理地址从0开始
u64_t adr = 0;
if (1 > move_krlimg(mbsp, (u64_t)(KINITPAGE_PHYADR), (0x1000 * 16 + 0x2000)))
{
kerror("move_krling err");
}
//将顶级页目录、页目录指针的空间清0
for (uint_t mi = 0; mi < PGENTY_SIZE; mi++)
{
p[mi] = 0;
pdpte[mi] = 0;
}
//映射
for (uint_t pdei = 0; pdei < 16; pdei++)
{
pdpte[pdei] = (u64_t)((u32_t)pde | KPDPTE_RW | KPDPTE_P);
for (uint_t pdeii = 0; pdeii < PGENTY_SIZE; pdeii++)
{
//大页KPDE_PS 2MB, 可读写KPDE_RW, 存在KPDE_P
pde[pdeii] = 0 | adr | KPDE_PS | KPDE_RW | KPDE_P;
adr += 0x200000;
}
pde = (u64_t *)((u32_t)pde + 0x1000);
}
//让顶级页目录中第0项和第((KRNL_VIRTUAL_ADDRESS_START) >> KPML4_SHIFT)&0x1ff项,指向同一个页目录指针页
p[((KRNL_VIRTUAL_ADDRESS_START)>>KPML_4_SHIFT)&0x1ff] = (u64_t)((u32_t)pdpte | KPML4_RW | KPML4_P);
p[0] = (u64_t)((u32_t)pdpte | KPML4_RW | KPML4_P);
//把页表首地址保存在机器信息结构中。
mbsp->mb_pml4padr = (u64_t)(KINITPAGE_PHYADR);
mbsp->mb_subpageslen = (u64_t)(0x1000 * 16 + 0x2000);
mbsp->mb_kpmapphymemsz = (u64_t)(0x400000000);
return;
}