您的位置:首页 > 运维架构 > Linux

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

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

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)


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

转自:/article/5536066.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: