diff options
Diffstat (limited to 'arch/x86/mm/init_32.c')
-rw-r--r-- | arch/x86/mm/init_32.c | 106 |
1 files changed, 66 insertions, 40 deletions
diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index 745d66b843c8..b299724f6e34 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c | |||
@@ -53,25 +53,14 @@ | |||
53 | #include <asm/page_types.h> | 53 | #include <asm/page_types.h> |
54 | #include <asm/init.h> | 54 | #include <asm/init.h> |
55 | 55 | ||
56 | #include "mm_internal.h" | ||
57 | |||
56 | unsigned long highstart_pfn, highend_pfn; | 58 | unsigned long highstart_pfn, highend_pfn; |
57 | 59 | ||
58 | static noinline int do_test_wp_bit(void); | 60 | static noinline int do_test_wp_bit(void); |
59 | 61 | ||
60 | bool __read_mostly __vmalloc_start_set = false; | 62 | bool __read_mostly __vmalloc_start_set = false; |
61 | 63 | ||
62 | static __init void *alloc_low_page(void) | ||
63 | { | ||
64 | unsigned long pfn = pgt_buf_end++; | ||
65 | void *adr; | ||
66 | |||
67 | if (pfn >= pgt_buf_top) | ||
68 | panic("alloc_low_page: ran out of memory"); | ||
69 | |||
70 | adr = __va(pfn * PAGE_SIZE); | ||
71 | clear_page(adr); | ||
72 | return adr; | ||
73 | } | ||
74 | |||
75 | /* | 64 | /* |
76 | * Creates a middle page table and puts a pointer to it in the | 65 | * Creates a middle page table and puts a pointer to it in the |
77 | * given global directory entry. This only returns the gd entry | 66 | * given global directory entry. This only returns the gd entry |
@@ -84,10 +73,7 @@ static pmd_t * __init one_md_table_init(pgd_t *pgd) | |||
84 | 73 | ||
85 | #ifdef CONFIG_X86_PAE | 74 | #ifdef CONFIG_X86_PAE |
86 | if (!(pgd_val(*pgd) & _PAGE_PRESENT)) { | 75 | if (!(pgd_val(*pgd) & _PAGE_PRESENT)) { |
87 | if (after_bootmem) | 76 | pmd_table = (pmd_t *)alloc_low_page(); |
88 | pmd_table = (pmd_t *)alloc_bootmem_pages(PAGE_SIZE); | ||
89 | else | ||
90 | pmd_table = (pmd_t *)alloc_low_page(); | ||
91 | paravirt_alloc_pmd(&init_mm, __pa(pmd_table) >> PAGE_SHIFT); | 77 | paravirt_alloc_pmd(&init_mm, __pa(pmd_table) >> PAGE_SHIFT); |
92 | set_pgd(pgd, __pgd(__pa(pmd_table) | _PAGE_PRESENT)); | 78 | set_pgd(pgd, __pgd(__pa(pmd_table) | _PAGE_PRESENT)); |
93 | pud = pud_offset(pgd, 0); | 79 | pud = pud_offset(pgd, 0); |
@@ -109,17 +95,7 @@ static pmd_t * __init one_md_table_init(pgd_t *pgd) | |||
109 | static pte_t * __init one_page_table_init(pmd_t *pmd) | 95 | static pte_t * __init one_page_table_init(pmd_t *pmd) |
110 | { | 96 | { |
111 | if (!(pmd_val(*pmd) & _PAGE_PRESENT)) { | 97 | if (!(pmd_val(*pmd) & _PAGE_PRESENT)) { |
112 | pte_t *page_table = NULL; | 98 | pte_t *page_table = (pte_t *)alloc_low_page(); |
113 | |||
114 | if (after_bootmem) { | ||
115 | #if defined(CONFIG_DEBUG_PAGEALLOC) || defined(CONFIG_KMEMCHECK) | ||
116 | page_table = (pte_t *) alloc_bootmem_pages(PAGE_SIZE); | ||
117 | #endif | ||
118 | if (!page_table) | ||
119 | page_table = | ||
120 | (pte_t *)alloc_bootmem_pages(PAGE_SIZE); | ||
121 | } else | ||
122 | page_table = (pte_t *)alloc_low_page(); | ||
123 | 99 | ||
124 | paravirt_alloc_pte(&init_mm, __pa(page_table) >> PAGE_SHIFT); | 100 | paravirt_alloc_pte(&init_mm, __pa(page_table) >> PAGE_SHIFT); |
125 | set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_TABLE)); | 101 | set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_TABLE)); |
@@ -146,8 +122,39 @@ pte_t * __init populate_extra_pte(unsigned long vaddr) | |||
146 | return one_page_table_init(pmd) + pte_idx; | 122 | return one_page_table_init(pmd) + pte_idx; |
147 | } | 123 | } |
148 | 124 | ||
125 | static unsigned long __init | ||
126 | page_table_range_init_count(unsigned long start, unsigned long end) | ||
127 | { | ||
128 | unsigned long count = 0; | ||
129 | #ifdef CONFIG_HIGHMEM | ||
130 | int pmd_idx_kmap_begin = fix_to_virt(FIX_KMAP_END) >> PMD_SHIFT; | ||
131 | int pmd_idx_kmap_end = fix_to_virt(FIX_KMAP_BEGIN) >> PMD_SHIFT; | ||
132 | int pgd_idx, pmd_idx; | ||
133 | unsigned long vaddr; | ||
134 | |||
135 | if (pmd_idx_kmap_begin == pmd_idx_kmap_end) | ||
136 | return 0; | ||
137 | |||
138 | vaddr = start; | ||
139 | pgd_idx = pgd_index(vaddr); | ||
140 | |||
141 | for ( ; (pgd_idx < PTRS_PER_PGD) && (vaddr != end); pgd_idx++) { | ||
142 | for (; (pmd_idx < PTRS_PER_PMD) && (vaddr != end); | ||
143 | pmd_idx++) { | ||
144 | if ((vaddr >> PMD_SHIFT) >= pmd_idx_kmap_begin && | ||
145 | (vaddr >> PMD_SHIFT) <= pmd_idx_kmap_end) | ||
146 | count++; | ||
147 | vaddr += PMD_SIZE; | ||
148 | } | ||
149 | pmd_idx = 0; | ||
150 | } | ||
151 | #endif | ||
152 | return count; | ||
153 | } | ||
154 | |||
149 | static pte_t *__init page_table_kmap_check(pte_t *pte, pmd_t *pmd, | 155 | static pte_t *__init page_table_kmap_check(pte_t *pte, pmd_t *pmd, |
150 | unsigned long vaddr, pte_t *lastpte) | 156 | unsigned long vaddr, pte_t *lastpte, |
157 | void **adr) | ||
151 | { | 158 | { |
152 | #ifdef CONFIG_HIGHMEM | 159 | #ifdef CONFIG_HIGHMEM |
153 | /* | 160 | /* |
@@ -161,16 +168,15 @@ static pte_t *__init page_table_kmap_check(pte_t *pte, pmd_t *pmd, | |||
161 | 168 | ||
162 | if (pmd_idx_kmap_begin != pmd_idx_kmap_end | 169 | if (pmd_idx_kmap_begin != pmd_idx_kmap_end |
163 | && (vaddr >> PMD_SHIFT) >= pmd_idx_kmap_begin | 170 | && (vaddr >> PMD_SHIFT) >= pmd_idx_kmap_begin |
164 | && (vaddr >> PMD_SHIFT) <= pmd_idx_kmap_end | 171 | && (vaddr >> PMD_SHIFT) <= pmd_idx_kmap_end) { |
165 | && ((__pa(pte) >> PAGE_SHIFT) < pgt_buf_start | ||
166 | || (__pa(pte) >> PAGE_SHIFT) >= pgt_buf_end)) { | ||
167 | pte_t *newpte; | 172 | pte_t *newpte; |
168 | int i; | 173 | int i; |
169 | 174 | ||
170 | BUG_ON(after_bootmem); | 175 | BUG_ON(after_bootmem); |
171 | newpte = alloc_low_page(); | 176 | newpte = *adr; |
172 | for (i = 0; i < PTRS_PER_PTE; i++) | 177 | for (i = 0; i < PTRS_PER_PTE; i++) |
173 | set_pte(newpte + i, pte[i]); | 178 | set_pte(newpte + i, pte[i]); |
179 | *adr = (void *)(((unsigned long)(*adr)) + PAGE_SIZE); | ||
174 | 180 | ||
175 | paravirt_alloc_pte(&init_mm, __pa(newpte) >> PAGE_SHIFT); | 181 | paravirt_alloc_pte(&init_mm, __pa(newpte) >> PAGE_SHIFT); |
176 | set_pmd(pmd, __pmd(__pa(newpte)|_PAGE_TABLE)); | 182 | set_pmd(pmd, __pmd(__pa(newpte)|_PAGE_TABLE)); |
@@ -204,6 +210,11 @@ page_table_range_init(unsigned long start, unsigned long end, pgd_t *pgd_base) | |||
204 | pgd_t *pgd; | 210 | pgd_t *pgd; |
205 | pmd_t *pmd; | 211 | pmd_t *pmd; |
206 | pte_t *pte = NULL; | 212 | pte_t *pte = NULL; |
213 | unsigned long count = page_table_range_init_count(start, end); | ||
214 | void *adr = NULL; | ||
215 | |||
216 | if (count) | ||
217 | adr = alloc_low_pages(count); | ||
207 | 218 | ||
208 | vaddr = start; | 219 | vaddr = start; |
209 | pgd_idx = pgd_index(vaddr); | 220 | pgd_idx = pgd_index(vaddr); |
@@ -216,7 +227,7 @@ page_table_range_init(unsigned long start, unsigned long end, pgd_t *pgd_base) | |||
216 | for (; (pmd_idx < PTRS_PER_PMD) && (vaddr != end); | 227 | for (; (pmd_idx < PTRS_PER_PMD) && (vaddr != end); |
217 | pmd++, pmd_idx++) { | 228 | pmd++, pmd_idx++) { |
218 | pte = page_table_kmap_check(one_page_table_init(pmd), | 229 | pte = page_table_kmap_check(one_page_table_init(pmd), |
219 | pmd, vaddr, pte); | 230 | pmd, vaddr, pte, &adr); |
220 | 231 | ||
221 | vaddr += PMD_SIZE; | 232 | vaddr += PMD_SIZE; |
222 | } | 233 | } |
@@ -310,6 +321,7 @@ repeat: | |||
310 | __pgprot(PTE_IDENT_ATTR | | 321 | __pgprot(PTE_IDENT_ATTR | |
311 | _PAGE_PSE); | 322 | _PAGE_PSE); |
312 | 323 | ||
324 | pfn &= PMD_MASK >> PAGE_SHIFT; | ||
313 | addr2 = (pfn + PTRS_PER_PTE-1) * PAGE_SIZE + | 325 | addr2 = (pfn + PTRS_PER_PTE-1) * PAGE_SIZE + |
314 | PAGE_OFFSET + PAGE_SIZE-1; | 326 | PAGE_OFFSET + PAGE_SIZE-1; |
315 | 327 | ||
@@ -455,9 +467,14 @@ void __init native_pagetable_init(void) | |||
455 | 467 | ||
456 | /* | 468 | /* |
457 | * Remove any mappings which extend past the end of physical | 469 | * Remove any mappings which extend past the end of physical |
458 | * memory from the boot time page table: | 470 | * memory from the boot time page table. |
471 | * In virtual address space, we should have at least two pages | ||
472 | * from VMALLOC_END to pkmap or fixmap according to VMALLOC_END | ||
473 | * definition. And max_low_pfn is set to VMALLOC_END physical | ||
474 | * address. If initial memory mapping is doing right job, we | ||
475 | * should have pte used near max_low_pfn or one pmd is not present. | ||
459 | */ | 476 | */ |
460 | for (pfn = max_low_pfn + 1; pfn < 1<<(32-PAGE_SHIFT); pfn++) { | 477 | for (pfn = max_low_pfn; pfn < 1<<(32-PAGE_SHIFT); pfn++) { |
461 | va = PAGE_OFFSET + (pfn<<PAGE_SHIFT); | 478 | va = PAGE_OFFSET + (pfn<<PAGE_SHIFT); |
462 | pgd = base + pgd_index(va); | 479 | pgd = base + pgd_index(va); |
463 | if (!pgd_present(*pgd)) | 480 | if (!pgd_present(*pgd)) |
@@ -468,10 +485,19 @@ void __init native_pagetable_init(void) | |||
468 | if (!pmd_present(*pmd)) | 485 | if (!pmd_present(*pmd)) |
469 | break; | 486 | break; |
470 | 487 | ||
488 | /* should not be large page here */ | ||
489 | if (pmd_large(*pmd)) { | ||
490 | pr_warn("try to clear pte for ram above max_low_pfn: pfn: %lx pmd: %p pmd phys: %lx, but pmd is big page and is not using pte !\n", | ||
491 | pfn, pmd, __pa(pmd)); | ||
492 | BUG_ON(1); | ||
493 | } | ||
494 | |||
471 | pte = pte_offset_kernel(pmd, va); | 495 | pte = pte_offset_kernel(pmd, va); |
472 | if (!pte_present(*pte)) | 496 | if (!pte_present(*pte)) |
473 | break; | 497 | break; |
474 | 498 | ||
499 | printk(KERN_DEBUG "clearing pte for ram above max_low_pfn: pfn: %lx pmd: %p pmd phys: %lx pte: %p pte phys: %lx\n", | ||
500 | pfn, pmd, __pa(pmd), pte, __pa(pte)); | ||
475 | pte_clear(NULL, va, pte); | 501 | pte_clear(NULL, va, pte); |
476 | } | 502 | } |
477 | paravirt_alloc_pmd(&init_mm, __pa(base) >> PAGE_SHIFT); | 503 | paravirt_alloc_pmd(&init_mm, __pa(base) >> PAGE_SHIFT); |
@@ -550,7 +576,7 @@ early_param("highmem", parse_highmem); | |||
550 | * artificially via the highmem=x boot parameter then create | 576 | * artificially via the highmem=x boot parameter then create |
551 | * it: | 577 | * it: |
552 | */ | 578 | */ |
553 | void __init lowmem_pfn_init(void) | 579 | static void __init lowmem_pfn_init(void) |
554 | { | 580 | { |
555 | /* max_low_pfn is 0, we already have early_res support */ | 581 | /* max_low_pfn is 0, we already have early_res support */ |
556 | max_low_pfn = max_pfn; | 582 | max_low_pfn = max_pfn; |
@@ -586,7 +612,7 @@ void __init lowmem_pfn_init(void) | |||
586 | * We have more RAM than fits into lowmem - we try to put it into | 612 | * We have more RAM than fits into lowmem - we try to put it into |
587 | * highmem, also taking the highmem=x boot parameter into account: | 613 | * highmem, also taking the highmem=x boot parameter into account: |
588 | */ | 614 | */ |
589 | void __init highmem_pfn_init(void) | 615 | static void __init highmem_pfn_init(void) |
590 | { | 616 | { |
591 | max_low_pfn = MAXMEM_PFN; | 617 | max_low_pfn = MAXMEM_PFN; |
592 | 618 | ||
@@ -669,8 +695,6 @@ void __init setup_bootmem_allocator(void) | |||
669 | printk(KERN_INFO " mapped low ram: 0 - %08lx\n", | 695 | printk(KERN_INFO " mapped low ram: 0 - %08lx\n", |
670 | max_pfn_mapped<<PAGE_SHIFT); | 696 | max_pfn_mapped<<PAGE_SHIFT); |
671 | printk(KERN_INFO " low ram: 0 - %08lx\n", max_low_pfn<<PAGE_SHIFT); | 697 | printk(KERN_INFO " low ram: 0 - %08lx\n", max_low_pfn<<PAGE_SHIFT); |
672 | |||
673 | after_bootmem = 1; | ||
674 | } | 698 | } |
675 | 699 | ||
676 | /* | 700 | /* |
@@ -753,6 +777,8 @@ void __init mem_init(void) | |||
753 | if (page_is_ram(tmp) && PageReserved(pfn_to_page(tmp))) | 777 | if (page_is_ram(tmp) && PageReserved(pfn_to_page(tmp))) |
754 | reservedpages++; | 778 | reservedpages++; |
755 | 779 | ||
780 | after_bootmem = 1; | ||
781 | |||
756 | codesize = (unsigned long) &_etext - (unsigned long) &_text; | 782 | codesize = (unsigned long) &_etext - (unsigned long) &_text; |
757 | datasize = (unsigned long) &_edata - (unsigned long) &_etext; | 783 | datasize = (unsigned long) &_edata - (unsigned long) &_etext; |
758 | initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; | 784 | initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; |