diff options
Diffstat (limited to 'arch/x86/mm/init_32.c')
-rw-r--r-- | arch/x86/mm/init_32.c | 65 |
1 files changed, 60 insertions, 5 deletions
diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index d37f29376b0c..9b5f7d7049d0 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c | |||
@@ -194,11 +194,30 @@ static void __init kernel_physical_mapping_init(pgd_t *pgd_base, | |||
194 | pgd_t *pgd; | 194 | pgd_t *pgd; |
195 | pmd_t *pmd; | 195 | pmd_t *pmd; |
196 | pte_t *pte; | 196 | pte_t *pte; |
197 | unsigned pages_2m = 0, pages_4k = 0; | 197 | unsigned pages_2m, pages_4k; |
198 | int mapping_iter; | ||
199 | |||
200 | /* | ||
201 | * First iteration will setup identity mapping using large/small pages | ||
202 | * based on use_pse, with other attributes same as set by | ||
203 | * the early code in head_32.S | ||
204 | * | ||
205 | * Second iteration will setup the appropriate attributes (NX, GLOBAL..) | ||
206 | * as desired for the kernel identity mapping. | ||
207 | * | ||
208 | * This two pass mechanism conforms to the TLB app note which says: | ||
209 | * | ||
210 | * "Software should not write to a paging-structure entry in a way | ||
211 | * that would change, for any linear address, both the page size | ||
212 | * and either the page frame or attributes." | ||
213 | */ | ||
214 | mapping_iter = 1; | ||
198 | 215 | ||
199 | if (!cpu_has_pse) | 216 | if (!cpu_has_pse) |
200 | use_pse = 0; | 217 | use_pse = 0; |
201 | 218 | ||
219 | repeat: | ||
220 | pages_2m = pages_4k = 0; | ||
202 | pfn = start_pfn; | 221 | pfn = start_pfn; |
203 | pgd_idx = pgd_index((pfn<<PAGE_SHIFT) + PAGE_OFFSET); | 222 | pgd_idx = pgd_index((pfn<<PAGE_SHIFT) + PAGE_OFFSET); |
204 | pgd = pgd_base + pgd_idx; | 223 | pgd = pgd_base + pgd_idx; |
@@ -224,6 +243,13 @@ static void __init kernel_physical_mapping_init(pgd_t *pgd_base, | |||
224 | if (use_pse) { | 243 | if (use_pse) { |
225 | unsigned int addr2; | 244 | unsigned int addr2; |
226 | pgprot_t prot = PAGE_KERNEL_LARGE; | 245 | pgprot_t prot = PAGE_KERNEL_LARGE; |
246 | /* | ||
247 | * first pass will use the same initial | ||
248 | * identity mapping attribute + _PAGE_PSE. | ||
249 | */ | ||
250 | pgprot_t init_prot = | ||
251 | __pgprot(PTE_IDENT_ATTR | | ||
252 | _PAGE_PSE); | ||
227 | 253 | ||
228 | addr2 = (pfn + PTRS_PER_PTE-1) * PAGE_SIZE + | 254 | addr2 = (pfn + PTRS_PER_PTE-1) * PAGE_SIZE + |
229 | PAGE_OFFSET + PAGE_SIZE-1; | 255 | PAGE_OFFSET + PAGE_SIZE-1; |
@@ -233,7 +259,10 @@ static void __init kernel_physical_mapping_init(pgd_t *pgd_base, | |||
233 | prot = PAGE_KERNEL_LARGE_EXEC; | 259 | prot = PAGE_KERNEL_LARGE_EXEC; |
234 | 260 | ||
235 | pages_2m++; | 261 | pages_2m++; |
236 | set_pmd(pmd, pfn_pmd(pfn, prot)); | 262 | if (mapping_iter == 1) |
263 | set_pmd(pmd, pfn_pmd(pfn, init_prot)); | ||
264 | else | ||
265 | set_pmd(pmd, pfn_pmd(pfn, prot)); | ||
237 | 266 | ||
238 | pfn += PTRS_PER_PTE; | 267 | pfn += PTRS_PER_PTE; |
239 | continue; | 268 | continue; |
@@ -245,17 +274,43 @@ static void __init kernel_physical_mapping_init(pgd_t *pgd_base, | |||
245 | for (; pte_ofs < PTRS_PER_PTE && pfn < end_pfn; | 274 | for (; pte_ofs < PTRS_PER_PTE && pfn < end_pfn; |
246 | pte++, pfn++, pte_ofs++, addr += PAGE_SIZE) { | 275 | pte++, pfn++, pte_ofs++, addr += PAGE_SIZE) { |
247 | pgprot_t prot = PAGE_KERNEL; | 276 | pgprot_t prot = PAGE_KERNEL; |
277 | /* | ||
278 | * first pass will use the same initial | ||
279 | * identity mapping attribute. | ||
280 | */ | ||
281 | pgprot_t init_prot = __pgprot(PTE_IDENT_ATTR); | ||
248 | 282 | ||
249 | if (is_kernel_text(addr)) | 283 | if (is_kernel_text(addr)) |
250 | prot = PAGE_KERNEL_EXEC; | 284 | prot = PAGE_KERNEL_EXEC; |
251 | 285 | ||
252 | pages_4k++; | 286 | pages_4k++; |
253 | set_pte(pte, pfn_pte(pfn, prot)); | 287 | if (mapping_iter == 1) |
288 | set_pte(pte, pfn_pte(pfn, init_prot)); | ||
289 | else | ||
290 | set_pte(pte, pfn_pte(pfn, prot)); | ||
254 | } | 291 | } |
255 | } | 292 | } |
256 | } | 293 | } |
257 | update_page_count(PG_LEVEL_2M, pages_2m); | 294 | if (mapping_iter == 1) { |
258 | update_page_count(PG_LEVEL_4K, pages_4k); | 295 | /* |
296 | * update direct mapping page count only in the first | ||
297 | * iteration. | ||
298 | */ | ||
299 | update_page_count(PG_LEVEL_2M, pages_2m); | ||
300 | update_page_count(PG_LEVEL_4K, pages_4k); | ||
301 | |||
302 | /* | ||
303 | * local global flush tlb, which will flush the previous | ||
304 | * mappings present in both small and large page TLB's. | ||
305 | */ | ||
306 | __flush_tlb_all(); | ||
307 | |||
308 | /* | ||
309 | * Second iteration will set the actual desired PTE attributes. | ||
310 | */ | ||
311 | mapping_iter = 2; | ||
312 | goto repeat; | ||
313 | } | ||
259 | } | 314 | } |
260 | 315 | ||
261 | /* | 316 | /* |