之前建立了临时页表,现在要建立最终内核页表,内核必须首先要建立一个完整的页表才能继续运行,因为内存寻址是内核继续运行的前提。下面就对主要函数kernel_physical_mapping_init(),进行分析。这个函数的掉用关系为:head.S->start_kernel()->setup_arch()->paging_init()->pagetable_init()->kernel_physical_mapping_init.


linux内核学习笔记【二】最终内核页表 Final kernel Page Table_EXEC



1 static void __init kernel_physical_mapping_init(pgd_t *pgd_base)
2 {
3 unsigned long pfn;
4 pgd_t *pgd;
5 pmd_t *pmd;
6 pte_t *pte;
7 int pgd_idx, pmd_idx, pte_ofs;
8
9 pgd_idx = pgd_index(PAGE_OFFSET);
10 //#define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD-1)) in inculde/pagetable.h
11 //so pgd_index = (0xc0000000 >> 22) & (1024 - 1) = 0x300 & 0x3ff = 0x300 = 768
12 pgd = pgd_base + pgd_idx;
13 //pgd_t *pgd_base = swapper_pg_dir;
14 //pgd = swapper_pg_dir[0x300]
15 pfn = 0;
16
17 //for(pgd_idx = 768; pgd_idx < 1024; pgd++,pgd_indx++)
18 for (; pgd_idx < PTRS_PER_PGD; pgd++, pgd_idx++) {
19 pmd = one_md_table_init(pgd);
20 //pmd = pgd
21 if (pfn >= max_low_pfn) //max_low_pfn = 1024 * 1024?
22 continue;
23 for (pmd_idx = 0; pmd_idx < PTRS_PER_PMD && pfn < max_low_pfn; pmd++, pmd_idx++) {
24 //only have 2 level directory, so PTRS_PER_PMD = 1
25 unsigned int address = pfn * PAGE_SIZE + PAGE_OFFSET;
26 //address = 0 * 4096 + 0xc0000000 = 0xc0000000
27 // 1 * 4096 = 0xc0001000
28 // ...
29
30 /* Map with big pages if possible, otherwise create normal page tables. */
31 if (cpu_has_pse) {
32 unsigned int address2 = (pfn + PTRS_PER_PTE - 1) * PAGE_SIZE + PAGE_OFFSET + PAGE_SIZE-1;
33
34 if (is_kernel_text(address) || is_kernel_text(address2))
35 set_pmd(pmd, pfn_pmd(pfn, PAGE_KERNEL_LARGE_EXEC));
36 else
37 set_pmd(pmd, pfn_pmd(pfn, PAGE_KERNEL_LARGE));
38 pfn += PTRS_PER_PTE;
39 } else {
40 pte = one_page_table_init(pmd);
41 //then we have mapped one page table
42
43 for (pte_ofs = 0; pte_ofs < PTRS_PER_PTE && pfn < max_low_pfn; pte++, pfn++, pte_ofs++) {
44 if (is_kernel_text(address))
45 set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC));
46 else
47 set_pte(pte, pfn_pte(pfn, PAGE_KERNEL));
48 //set every page table entry PAGE_KERNEL_EXEC or PAGE_KERNEL depend onthe address
49 }
50 }
51 }
52 }
53 }
54
55 static pmd_t * __init one_md_table_init(pgd_t *pgd)
56 {
57 pud_t *pud;
58 pmd_t *pmd_table;
59
60 #ifdef CONFIG_X86_PAE
61 //...
62 #else
63 pud = pud_offset(pgd, 0);
64 //#define pud_offset(pgd, start) (pgd) ;in include/generic/4level-fixup.h
65 //so pud = pgd;
66 pmd_table = pmd_offset(pud, 0);
67 //#define pmd_offset(pud, address) ((pmd_t *) pud_page(*(pud)) + pmd_index(address)) ; in /include/asm-i386/pgtable-3level.h
68 //so pmd_table = pgd
69 #endif
70
71 return pmd_table;
72 }
73
74 /*
75 * Create a page table and place a pointer to it in a middle page
76 * directory entry.
77 */
78 static pte_t * __init one_page_table_init(pmd_t *pmd)
79 {
80 if (pmd_none(*pmd)) {
81 //check if the pmd so as pgd has value
82 pte_t *page_table = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);
83 //alloc physical memory, size = 4096
84 set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_TABLE));
85 //set parameters
86 if (page_table != pte_offset_kernel(pmd, 0))
87 BUG();
88
89 return page_table;
90 }
91
92 return pte_offset_kernel(pmd, 0);
93 }
94
95 static inline int is_kernel_text(unsigned long addr)
96 {
97 if (addr >= (unsigned long)_stext && addr <= (unsigned long)__init_end)
98 return 1;
99 return 0;
100 }
101
102 //_stext, __init_end是个内核符号, 在内核链接的时候生成的, 分别表示内核代码段的开始和终止地址.
103 //如果address属于内核代码段, 那么在设置页表项的时候就要加个PAGE_KERNEL_EXEC属性,如果不是,则加个PAGE_KERNEL属性.
104 //#define _PAGE_KERNEL_EXEC \
105 // (_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED)
106
107 //#define _PAGE_KERNEL \
108 // (_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_NX)



linux内核学习笔记【二】最终内核页表 Final kernel Page Table_EXEC


这样,内核空间的线性地址0xc0000000-0xffffffff已经都映射了整块物理内存。即pgd[0x300]-pgd[0x400]都有了值,同时下面的每个pte[0-1024]也都有了值,不知道理解的对不对,希望高手指点。还有一个疑问就是,用户空间的线性地址0x00000000 - 0xbfffffff这段地址是在什么时候映射的,可能还没有看到吧,继续学习。。。