diff options
-rw-r--r-- | arch/x86/mm/init_32.c | 65 | ||||
-rw-r--r-- | arch/x86/mm/init_64.c | 99 |
2 files changed, 144 insertions, 20 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 | /* |
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index d3746efb060d..1ba945eb6282 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c | |||
@@ -270,6 +270,8 @@ static __ref void unmap_low_page(void *adr) | |||
270 | early_iounmap(adr, PAGE_SIZE); | 270 | early_iounmap(adr, PAGE_SIZE); |
271 | } | 271 | } |
272 | 272 | ||
273 | static int physical_mapping_iter; | ||
274 | |||
273 | static unsigned long __meminit | 275 | static unsigned long __meminit |
274 | phys_pte_init(pte_t *pte_page, unsigned long addr, unsigned long end) | 276 | phys_pte_init(pte_t *pte_page, unsigned long addr, unsigned long end) |
275 | { | 277 | { |
@@ -290,16 +292,19 @@ phys_pte_init(pte_t *pte_page, unsigned long addr, unsigned long end) | |||
290 | } | 292 | } |
291 | 293 | ||
292 | if (pte_val(*pte)) | 294 | if (pte_val(*pte)) |
293 | continue; | 295 | goto repeat_set_pte; |
294 | 296 | ||
295 | if (0) | 297 | if (0) |
296 | printk(" pte=%p addr=%lx pte=%016lx\n", | 298 | printk(" pte=%p addr=%lx pte=%016lx\n", |
297 | pte, addr, pfn_pte(addr >> PAGE_SHIFT, PAGE_KERNEL).pte); | 299 | pte, addr, pfn_pte(addr >> PAGE_SHIFT, PAGE_KERNEL).pte); |
300 | pages++; | ||
301 | repeat_set_pte: | ||
298 | set_pte(pte, pfn_pte(addr >> PAGE_SHIFT, PAGE_KERNEL)); | 302 | set_pte(pte, pfn_pte(addr >> PAGE_SHIFT, PAGE_KERNEL)); |
299 | last_map_addr = (addr & PAGE_MASK) + PAGE_SIZE; | 303 | last_map_addr = (addr & PAGE_MASK) + PAGE_SIZE; |
300 | pages++; | ||
301 | } | 304 | } |
302 | update_page_count(PG_LEVEL_4K, pages); | 305 | |
306 | if (physical_mapping_iter == 1) | ||
307 | update_page_count(PG_LEVEL_4K, pages); | ||
303 | 308 | ||
304 | return last_map_addr; | 309 | return last_map_addr; |
305 | } | 310 | } |
@@ -318,7 +323,6 @@ phys_pmd_init(pmd_t *pmd_page, unsigned long address, unsigned long end, | |||
318 | { | 323 | { |
319 | unsigned long pages = 0; | 324 | unsigned long pages = 0; |
320 | unsigned long last_map_addr = end; | 325 | unsigned long last_map_addr = end; |
321 | unsigned long start = address; | ||
322 | 326 | ||
323 | int i = pmd_index(address); | 327 | int i = pmd_index(address); |
324 | 328 | ||
@@ -341,15 +345,14 @@ phys_pmd_init(pmd_t *pmd_page, unsigned long address, unsigned long end, | |||
341 | last_map_addr = phys_pte_update(pmd, address, | 345 | last_map_addr = phys_pte_update(pmd, address, |
342 | end); | 346 | end); |
343 | spin_unlock(&init_mm.page_table_lock); | 347 | spin_unlock(&init_mm.page_table_lock); |
348 | continue; | ||
344 | } | 349 | } |
345 | /* Count entries we're using from level2_ident_pgt */ | 350 | goto repeat_set_pte; |
346 | if (start == 0) | ||
347 | pages++; | ||
348 | continue; | ||
349 | } | 351 | } |
350 | 352 | ||
351 | if (page_size_mask & (1<<PG_LEVEL_2M)) { | 353 | if (page_size_mask & (1<<PG_LEVEL_2M)) { |
352 | pages++; | 354 | pages++; |
355 | repeat_set_pte: | ||
353 | spin_lock(&init_mm.page_table_lock); | 356 | spin_lock(&init_mm.page_table_lock); |
354 | set_pte((pte_t *)pmd, | 357 | set_pte((pte_t *)pmd, |
355 | pfn_pte(address >> PAGE_SHIFT, PAGE_KERNEL_LARGE)); | 358 | pfn_pte(address >> PAGE_SHIFT, PAGE_KERNEL_LARGE)); |
@@ -366,7 +369,8 @@ phys_pmd_init(pmd_t *pmd_page, unsigned long address, unsigned long end, | |||
366 | pmd_populate_kernel(&init_mm, pmd, __va(pte_phys)); | 369 | pmd_populate_kernel(&init_mm, pmd, __va(pte_phys)); |
367 | spin_unlock(&init_mm.page_table_lock); | 370 | spin_unlock(&init_mm.page_table_lock); |
368 | } | 371 | } |
369 | update_page_count(PG_LEVEL_2M, pages); | 372 | if (physical_mapping_iter == 1) |
373 | update_page_count(PG_LEVEL_2M, pages); | ||
370 | return last_map_addr; | 374 | return last_map_addr; |
371 | } | 375 | } |
372 | 376 | ||
@@ -405,14 +409,18 @@ phys_pud_init(pud_t *pud_page, unsigned long addr, unsigned long end, | |||
405 | } | 409 | } |
406 | 410 | ||
407 | if (pud_val(*pud)) { | 411 | if (pud_val(*pud)) { |
408 | if (!pud_large(*pud)) | 412 | if (!pud_large(*pud)) { |
409 | last_map_addr = phys_pmd_update(pud, addr, end, | 413 | last_map_addr = phys_pmd_update(pud, addr, end, |
410 | page_size_mask); | 414 | page_size_mask); |
411 | continue; | 415 | continue; |
416 | } | ||
417 | |||
418 | goto repeat_set_pte; | ||
412 | } | 419 | } |
413 | 420 | ||
414 | if (page_size_mask & (1<<PG_LEVEL_1G)) { | 421 | if (page_size_mask & (1<<PG_LEVEL_1G)) { |
415 | pages++; | 422 | pages++; |
423 | repeat_set_pte: | ||
416 | spin_lock(&init_mm.page_table_lock); | 424 | spin_lock(&init_mm.page_table_lock); |
417 | set_pte((pte_t *)pud, | 425 | set_pte((pte_t *)pud, |
418 | pfn_pte(addr >> PAGE_SHIFT, PAGE_KERNEL_LARGE)); | 426 | pfn_pte(addr >> PAGE_SHIFT, PAGE_KERNEL_LARGE)); |
@@ -430,7 +438,9 @@ phys_pud_init(pud_t *pud_page, unsigned long addr, unsigned long end, | |||
430 | spin_unlock(&init_mm.page_table_lock); | 438 | spin_unlock(&init_mm.page_table_lock); |
431 | } | 439 | } |
432 | __flush_tlb_all(); | 440 | __flush_tlb_all(); |
433 | update_page_count(PG_LEVEL_1G, pages); | 441 | |
442 | if (physical_mapping_iter == 1) | ||
443 | update_page_count(PG_LEVEL_1G, pages); | ||
434 | 444 | ||
435 | return last_map_addr; | 445 | return last_map_addr; |
436 | } | 446 | } |
@@ -494,15 +504,54 @@ static void __init init_gbpages(void) | |||
494 | direct_gbpages = 0; | 504 | direct_gbpages = 0; |
495 | } | 505 | } |
496 | 506 | ||
507 | static int is_kernel(unsigned long pfn) | ||
508 | { | ||
509 | unsigned long pg_addresss = pfn << PAGE_SHIFT; | ||
510 | |||
511 | if (pg_addresss >= (unsigned long) __pa(_text) && | ||
512 | pg_addresss <= (unsigned long) __pa(_end)) | ||
513 | return 1; | ||
514 | |||
515 | return 0; | ||
516 | } | ||
517 | |||
497 | static unsigned long __init kernel_physical_mapping_init(unsigned long start, | 518 | static unsigned long __init kernel_physical_mapping_init(unsigned long start, |
498 | unsigned long end, | 519 | unsigned long end, |
499 | unsigned long page_size_mask) | 520 | unsigned long page_size_mask) |
500 | { | 521 | { |
501 | 522 | ||
502 | unsigned long next, last_map_addr = end; | 523 | unsigned long next, last_map_addr; |
524 | u64 cached_supported_pte_mask = __supported_pte_mask; | ||
525 | unsigned long cache_start = start; | ||
526 | unsigned long cache_end = end; | ||
527 | |||
528 | /* | ||
529 | * First iteration will setup identity mapping using large/small pages | ||
530 | * based on page_size_mask, with other attributes same as set by | ||
531 | * the early code in head_64.S | ||
532 | * | ||
533 | * Second iteration will setup the appropriate attributes | ||
534 | * as desired for the kernel identity mapping. | ||
535 | * | ||
536 | * This two pass mechanism conforms to the TLB app note which says: | ||
537 | * | ||
538 | * "Software should not write to a paging-structure entry in a way | ||
539 | * that would change, for any linear address, both the page size | ||
540 | * and either the page frame or attributes." | ||
541 | * | ||
542 | * For now, only difference between very early PTE attributes used in | ||
543 | * head_64.S and here is _PAGE_NX. | ||
544 | */ | ||
545 | BUILD_BUG_ON((__PAGE_KERNEL_LARGE & ~__PAGE_KERNEL_IDENT_LARGE_EXEC) | ||
546 | != _PAGE_NX); | ||
547 | __supported_pte_mask &= ~(_PAGE_NX); | ||
548 | physical_mapping_iter = 1; | ||
503 | 549 | ||
504 | start = (unsigned long)__va(start); | 550 | repeat: |
505 | end = (unsigned long)__va(end); | 551 | last_map_addr = cache_end; |
552 | |||
553 | start = (unsigned long)__va(cache_start); | ||
554 | end = (unsigned long)__va(cache_end); | ||
506 | 555 | ||
507 | for (; start < end; start = next) { | 556 | for (; start < end; start = next) { |
508 | pgd_t *pgd = pgd_offset_k(start); | 557 | pgd_t *pgd = pgd_offset_k(start); |
@@ -514,11 +563,21 @@ static unsigned long __init kernel_physical_mapping_init(unsigned long start, | |||
514 | next = end; | 563 | next = end; |
515 | 564 | ||
516 | if (pgd_val(*pgd)) { | 565 | if (pgd_val(*pgd)) { |
566 | /* | ||
567 | * Static identity mappings will be overwritten | ||
568 | * with run-time mappings. For example, this allows | ||
569 | * the static 0-1GB identity mapping to be mapped | ||
570 | * non-executable with this. | ||
571 | */ | ||
572 | if (is_kernel(pte_pfn(*((pte_t *) pgd)))) | ||
573 | goto realloc; | ||
574 | |||
517 | last_map_addr = phys_pud_update(pgd, __pa(start), | 575 | last_map_addr = phys_pud_update(pgd, __pa(start), |
518 | __pa(end), page_size_mask); | 576 | __pa(end), page_size_mask); |
519 | continue; | 577 | continue; |
520 | } | 578 | } |
521 | 579 | ||
580 | realloc: | ||
522 | pud = alloc_low_page(&pud_phys); | 581 | pud = alloc_low_page(&pud_phys); |
523 | last_map_addr = phys_pud_init(pud, __pa(start), __pa(next), | 582 | last_map_addr = phys_pud_init(pud, __pa(start), __pa(next), |
524 | page_size_mask); | 583 | page_size_mask); |
@@ -528,6 +587,16 @@ static unsigned long __init kernel_physical_mapping_init(unsigned long start, | |||
528 | pgd_populate(&init_mm, pgd, __va(pud_phys)); | 587 | pgd_populate(&init_mm, pgd, __va(pud_phys)); |
529 | spin_unlock(&init_mm.page_table_lock); | 588 | spin_unlock(&init_mm.page_table_lock); |
530 | } | 589 | } |
590 | __flush_tlb_all(); | ||
591 | |||
592 | if (physical_mapping_iter == 1) { | ||
593 | physical_mapping_iter = 2; | ||
594 | /* | ||
595 | * Second iteration will set the actual desired PTE attributes. | ||
596 | */ | ||
597 | __supported_pte_mask = cached_supported_pte_mask; | ||
598 | goto repeat; | ||
599 | } | ||
531 | 600 | ||
532 | return last_map_addr; | 601 | return last_map_addr; |
533 | } | 602 | } |