内核页表的初始化分析 (转载)
时间:2006-05-13 来源:ooike
-------------------- 在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 }
相关阅读 更多 +