-------------------- 在head.s中进行第一次初始化 -----------------------
内核页目录swapper_pg_dir是在head.s中静态分配的,位于物理内存0x101000处,虚拟地址3G+0x101000处。
/*
* Initialize page tables
*/
movl $pg0-__PAGE_OFFSET,%edi /* initialize page tables */ // 2个页表pg0和pg1初始化,分别映射到物理内存0~4M和4~8M
movl $007,%eax /* "007" doesn't mean with right to kill, but
PRESENT+RW+USER */ //设置存在、可读写、用户态的标志
2: stosl //注意:edi中存放的都是pg0,pg1页表项的物理地址(因为页机制还没开启)
add $0x1000,%eax
cmp $empty_zero_page-__PAGE_OFFSET,%edi //循环初始化,一次一个页表项,每次eax的值增加4096,作为下一次的赋值
jne 2b //当设置完pg1的最后一项,循环结束
/*
* Enable paging
*/
3:
movl $swapper_pg_dir-__PAGE_OFFSET,%eax
movl %eax,%cr3 /* set the page table pointer.. */ //将页目录表swapper_pg_dir的物理地址赋给%cr3。
movl %cr0,%eax
orl $0x80000000,%eax
movl %eax,%cr0 /* ..and set paging (PG) bit */ //启用页机制
jmp 1f /* flush the prefetch-queue */
1:
movl $1f,%eax
jmp *%eax /* make sure eip is relocated */ //长跳转将IP指令计数器中的指令指针转为虚拟指针(内核自3G+1M开始的)
1: //此前一直是用的物理地址(内核从1M开始)
/* Set up the stack pointer */
lss stack_start,%esp
。。。。。。。。。。。。
.org 0x1000
ENTRY(swapper_pg_dir) //页目录表,有4项已预先赋好值了。在paging_init()中将重新初始化
.long 0x00102007 //页目录表中第1、2和第769、770项都指向pg0和pg1,使得虚拟地址0~8M和3G~3g+8M的空间都映射到了物理内存0~8M。
.long 0x00103007
.fill BOOT_USER_PGD_PTRS-2,4,0
/* default: 766 entries */
.long 0x00102007
.long 0x00103007
/* default: 254 entries */
.fill BOOT_KERNEL_PGD_PTRS-2,4,0
/*
* The page tables are initialized to only 8MB here - the final page
* tables are set up later depending on memory size.
*/
.org 0x2000 //页表0
ENTRY(pg0)
.org 0x3000 //页表1
ENTRY(pg1)
/*
* empty_zero_page must immediately follow the page tables ! (The
* initialization loop counts until empty_zero_page)
*/
.org 0x4000
ENTRY(empty_zero_page)
-------------------- 在start_kernel()->paging_init()->pagetable_init()中进行第二次初始化 ---------
该函数实现将从3G到end的虚拟地址空间都建立到物理地址的一对一映射。max_low_pfn是物理内存的最大页数(初始化阶段检测得到)
将它乘以页尺寸再转化为虚拟地址就得到end。页目录表起始地址为swapper_pg_dir(虚拟地址),是在head.s中静态分配的。
static void __init pagetable_init (void)
{
unsigned long vaddr, end;
pgd_t *pgd, *pgd_base;
int i, j, k;
pmd_t *pmd;
pte_t *pte;
/*
* This can be zero as well - no problem, in that case we exit
* the loops anyway due to the PTRS_PER_* conditions.
*/
end = (unsigned long)__va(max_low_pfn*PAGE_SIZE); //初始化建立虚拟地址空间中从3G到end的映射(max_low_pfn是物理内存的页数)
pgd_base = swapper_pg_dir;
#if CONFIG_X86_PAE
for (i = 0; i < PTRS_PER_PGD; i++) {
pgd = pgd_base + i;
__pgd_clear(pgd);
}
#endif
i = __pgd_offset(PAGE_OFFSET);
pgd = pgd_base + i; //初始化的起始页目录项指针
for (; i < PTRS_PER_PGD; pgd++, i++) { //从3G开始,每次循环解决一个页目录项的设置(包括下面的中间目录表的初始化)
vaddr = i*PGDIR_SIZE;
if (end && (vaddr >= end))
break;
#if CONFIG_X86_PAE
pmd = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE); //若使用三级映射,建立中间目录表
set_pgd(pgd, __pgd(__pa(pmd) + 0x1));
#else
pmd = (pmd_t *)pgd; //使用二级映射机制(页目录、页表),则中间目录项指针就直接指向页目录项
#endif
if (pmd != pmd_offset(pgd, 0))
BUG();
for (j = 0; j < PTRS_PER_PMD; pmd++, j++) { //每次循环解决一个中间目录项的设置,包括下面的页表的初始化
vaddr = i*PGDIR_SIZE + j*PMD_SIZE; //对于二级映射来说,PTRS_PER_PMD=1,pmd = (pmd_t *)pgd
if (end && (vaddr >= end)) //所以只循环一次,且设置的就是页目录项
break; //若是三级映射,则循环 PTRS_PER_PMD次,Intel CPU的中间目录项数为512
if (cpu_has_pse) { //若CPU支持4M的页,则无需页表了,直接初始化中间目录项就可以
unsigned long __pe; //对二级映射来说,即页目录项
set_in_cr4(X86_CR4_PSE);
boot_cpu_data.wp_works_ok = 1;
__pe = _KERNPG_TABLE + _PAGE_PSE + __pa(vaddr);
/* Make it "global" too if supported */
if (cpu_has_pge) {
set_in_cr4(X86_CR4_PGE);
__pe += _PAGE_GLOBAL;
}
set_pmd(pmd, __pmd(__pe)); //对二级映射来说,是设置页目录项的值
continue; //直接进行下一轮循环,无需建立页表
}
pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE); //CPU不支持4M页,还得建立页表(分配并置值)
set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(pte)));
if (pte != pte_offset(pmd, 0))
BUG();
for (k = 0; k < PTRS_PER_PTE; pte++, k++) { //设置各页表项的值(循环1024次)
vaddr = i*PGDIR_SIZE + j*PMD_SIZE + k*PAGE_SIZE;
if (end && (vaddr >= end))
break;
*pte = mk_pte_phys(__pa(vaddr), PAGE_KERNEL);
}
}
}
/*
* Fixed mappings, only the page table structure has to be
* created - mappings will be set by set_fixmap():
*/
vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK;
fixrange_init(vaddr, 0, pgd_base);
#if CONFIG_HIGHMEM
/*
* Permanent kmaps:
*/
vaddr = PKMAP_BASE;
fixrange_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, pgd_base);
pgd = swapper_pg_dir + __pgd_offset(vaddr);
pmd = pmd_offset(pgd, vaddr);
pte = pte_offset(pmd, vaddr);
pkmap_page_table = pte;
#endif
#if CONFIG_X86_PAE
/*
* Add low memory identity-mappings - SMP needs it when
* starting up on an AP from real-mode. In the non-PAE
* case we already have these mappings through head.S.
* All user-space mappings are explicitly cleared after
* SMP startup.
*/
pgd_base[0] = pgd_base[USER_PTRS_PER_PGD];
#endif
}