diff options
| author | David S. Miller <davem@sunset.davemloft.net> | 2005-10-05 18:12:00 -0400 |
|---|---|---|
| committer | David S. Miller <davem@sunset.davemloft.net> | 2005-10-05 18:12:00 -0400 |
| commit | 9ad98c5b4461e7dfa3754963200993a68825eab4 (patch) | |
| tree | b7456cf49eaf034740fa7cb8b75e5b6ee01c9666 | |
| parent | 782c3fd470abddf2525e34cf3131215a8f95e834 (diff) | |
[SPARC64]: Fix initrd when net booting.
By allocating early memory for the firmware page tables, we
can write over the beginning of the initrd image.
So what we do now is:
1) Read in firmware translations table while still on the
firmware's trap table.
2) Switch to Linux trap table.
3) Init bootmem.
4) Build firmware page tables using __alloc_bootmem().
And this keeps the initrd from being clobbered.
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | arch/sparc64/mm/init.c | 156 |
1 files changed, 56 insertions, 100 deletions
diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c index 4e2f71e0abc8..0d2e967c7200 100644 --- a/arch/sparc64/mm/init.c +++ b/arch/sparc64/mm/init.c | |||
| @@ -368,6 +368,7 @@ struct linux_prom_translation { | |||
| 368 | unsigned long data; | 368 | unsigned long data; |
| 369 | }; | 369 | }; |
| 370 | static struct linux_prom_translation prom_trans[512] __initdata; | 370 | static struct linux_prom_translation prom_trans[512] __initdata; |
| 371 | static unsigned int prom_trans_ents __initdata; | ||
| 371 | 372 | ||
| 372 | extern unsigned long prom_boot_page; | 373 | extern unsigned long prom_boot_page; |
| 373 | extern void prom_remap(unsigned long physpage, unsigned long virtpage, int mmu_ihandle); | 374 | extern void prom_remap(unsigned long physpage, unsigned long virtpage, int mmu_ihandle); |
| @@ -381,57 +382,7 @@ unsigned long kern_locked_tte_data; | |||
| 381 | unsigned long prom_pmd_phys __read_mostly; | 382 | unsigned long prom_pmd_phys __read_mostly; |
| 382 | unsigned int swapper_pgd_zero __read_mostly; | 383 | unsigned int swapper_pgd_zero __read_mostly; |
| 383 | 384 | ||
| 384 | /* Allocate power-of-2 aligned chunks from the end of the | 385 | static pmd_t *prompmd __read_mostly; |
| 385 | * kernel image. Return physical address. | ||
| 386 | */ | ||
| 387 | static inline unsigned long early_alloc_phys(unsigned long size) | ||
| 388 | { | ||
| 389 | unsigned long base; | ||
| 390 | |||
| 391 | BUILD_BUG_ON(size & (size - 1)); | ||
| 392 | |||
| 393 | kern_size = (kern_size + (size - 1)) & ~(size - 1); | ||
| 394 | base = kern_base + kern_size; | ||
| 395 | kern_size += size; | ||
| 396 | |||
| 397 | return base; | ||
| 398 | } | ||
| 399 | |||
| 400 | static inline unsigned long load_phys32(unsigned long pa) | ||
| 401 | { | ||
| 402 | unsigned long val; | ||
| 403 | |||
| 404 | __asm__ __volatile__("lduwa [%1] %2, %0" | ||
| 405 | : "=&r" (val) | ||
| 406 | : "r" (pa), "i" (ASI_PHYS_USE_EC)); | ||
| 407 | |||
| 408 | return val; | ||
| 409 | } | ||
| 410 | |||
| 411 | static inline unsigned long load_phys64(unsigned long pa) | ||
| 412 | { | ||
| 413 | unsigned long val; | ||
| 414 | |||
| 415 | __asm__ __volatile__("ldxa [%1] %2, %0" | ||
| 416 | : "=&r" (val) | ||
| 417 | : "r" (pa), "i" (ASI_PHYS_USE_EC)); | ||
| 418 | |||
| 419 | return val; | ||
| 420 | } | ||
| 421 | |||
| 422 | static inline void store_phys32(unsigned long pa, unsigned long val) | ||
| 423 | { | ||
| 424 | __asm__ __volatile__("stwa %0, [%1] %2" | ||
| 425 | : /* no outputs */ | ||
| 426 | : "r" (val), "r" (pa), "i" (ASI_PHYS_USE_EC)); | ||
| 427 | } | ||
| 428 | |||
| 429 | static inline void store_phys64(unsigned long pa, unsigned long val) | ||
| 430 | { | ||
| 431 | __asm__ __volatile__("stxa %0, [%1] %2" | ||
| 432 | : /* no outputs */ | ||
| 433 | : "r" (val), "r" (pa), "i" (ASI_PHYS_USE_EC)); | ||
| 434 | } | ||
| 435 | 386 | ||
| 436 | #define BASE_PAGE_SIZE 8192 | 387 | #define BASE_PAGE_SIZE 8192 |
| 437 | 388 | ||
| @@ -441,34 +392,28 @@ static inline void store_phys64(unsigned long pa, unsigned long val) | |||
| 441 | */ | 392 | */ |
| 442 | unsigned long prom_virt_to_phys(unsigned long promva, int *error) | 393 | unsigned long prom_virt_to_phys(unsigned long promva, int *error) |
| 443 | { | 394 | { |
| 444 | unsigned long pmd_phys = (prom_pmd_phys + | 395 | pmd_t *pmdp = prompmd + ((promva >> 23) & 0x7ff); |
| 445 | ((promva >> 23) & 0x7ff) * sizeof(pmd_t)); | 396 | pte_t *ptep; |
| 446 | unsigned long pte_phys; | ||
| 447 | pmd_t pmd_ent; | ||
| 448 | pte_t pte_ent; | ||
| 449 | unsigned long base; | 397 | unsigned long base; |
| 450 | 398 | ||
| 451 | pmd_val(pmd_ent) = load_phys32(pmd_phys); | 399 | if (pmd_none(*pmdp)) { |
| 452 | if (pmd_none(pmd_ent)) { | ||
| 453 | if (error) | 400 | if (error) |
| 454 | *error = 1; | 401 | *error = 1; |
| 455 | return 0; | 402 | return 0; |
| 456 | } | 403 | } |
| 457 | 404 | ptep = (pte_t *)__pmd_page(*pmdp) + ((promva >> 13) & 0x3ff); | |
| 458 | pte_phys = (unsigned long)pmd_val(pmd_ent) << 11UL; | 405 | if (!pte_present(*ptep)) { |
| 459 | pte_phys += ((promva >> 13) & 0x3ff) * sizeof(pte_t); | ||
| 460 | pte_val(pte_ent) = load_phys64(pte_phys); | ||
| 461 | if (!pte_present(pte_ent)) { | ||
| 462 | if (error) | 406 | if (error) |
| 463 | *error = 1; | 407 | *error = 1; |
| 464 | return 0; | 408 | return 0; |
| 465 | } | 409 | } |
| 466 | if (error) { | 410 | if (error) { |
| 467 | *error = 0; | 411 | *error = 0; |
| 468 | return pte_val(pte_ent); | 412 | return pte_val(*ptep); |
| 469 | } | 413 | } |
| 470 | base = pte_val(pte_ent) & _PAGE_PADDR; | 414 | base = pte_val(*ptep) & _PAGE_PADDR; |
| 471 | return (base + (promva & (BASE_PAGE_SIZE - 1))); | 415 | |
| 416 | return base + (promva & (BASE_PAGE_SIZE - 1)); | ||
| 472 | } | 417 | } |
| 473 | 418 | ||
| 474 | /* The obp translations are saved based on 8k pagesize, since obp can | 419 | /* The obp translations are saved based on 8k pagesize, since obp can |
| @@ -481,25 +426,20 @@ static void __init build_obp_range(unsigned long start, unsigned long end, unsig | |||
| 481 | unsigned long vaddr; | 426 | unsigned long vaddr; |
| 482 | 427 | ||
| 483 | for (vaddr = start; vaddr < end; vaddr += BASE_PAGE_SIZE) { | 428 | for (vaddr = start; vaddr < end; vaddr += BASE_PAGE_SIZE) { |
| 484 | unsigned long val, pte_phys, pmd_phys; | 429 | unsigned long val; |
| 485 | pmd_t pmd_ent; | 430 | pmd_t *pmd; |
| 486 | int i; | 431 | pte_t *pte; |
| 487 | |||
| 488 | pmd_phys = (prom_pmd_phys + | ||
| 489 | (((vaddr >> 23) & 0x7ff) * sizeof(pmd_t))); | ||
| 490 | pmd_val(pmd_ent) = load_phys32(pmd_phys); | ||
| 491 | if (pmd_none(pmd_ent)) { | ||
| 492 | pte_phys = early_alloc_phys(BASE_PAGE_SIZE); | ||
| 493 | |||
| 494 | for (i = 0; i < BASE_PAGE_SIZE / sizeof(pte_t); i++) | ||
| 495 | store_phys64(pte_phys+i*sizeof(pte_t),0); | ||
| 496 | 432 | ||
| 497 | pmd_val(pmd_ent) = pte_phys >> 11UL; | 433 | pmd = prompmd + ((vaddr >> 23) & 0x7ff); |
| 498 | store_phys32(pmd_phys, pmd_val(pmd_ent)); | 434 | if (pmd_none(*pmd)) { |
| 435 | pte = __alloc_bootmem(BASE_PAGE_SIZE, BASE_PAGE_SIZE, | ||
| 436 | PAGE_SIZE); | ||
| 437 | if (!pte) | ||
| 438 | prom_halt(); | ||
| 439 | memset(pte, 0, BASE_PAGE_SIZE); | ||
| 440 | pmd_set(pmd, pte); | ||
| 499 | } | 441 | } |
| 500 | 442 | pte = (pte_t *) __pmd_page(*pmd) + ((vaddr >> 13) & 0x3ff); | |
| 501 | pte_phys = (unsigned long)pmd_val(pmd_ent) << 11UL; | ||
| 502 | pte_phys += (((vaddr >> 13) & 0x3ff) * sizeof(pte_t)); | ||
| 503 | 443 | ||
| 504 | val = data; | 444 | val = data; |
| 505 | 445 | ||
| @@ -507,7 +447,8 @@ static void __init build_obp_range(unsigned long start, unsigned long end, unsig | |||
| 507 | if (tlb_type == spitfire) | 447 | if (tlb_type == spitfire) |
| 508 | val &= ~0x0003fe0000000000UL; | 448 | val &= ~0x0003fe0000000000UL; |
| 509 | 449 | ||
| 510 | store_phys64(pte_phys, val | _PAGE_MODIFIED); | 450 | set_pte_at(&init_mm, vaddr, pte, |
| 451 | __pte(val | _PAGE_MODIFIED)); | ||
| 511 | 452 | ||
| 512 | data += BASE_PAGE_SIZE; | 453 | data += BASE_PAGE_SIZE; |
| 513 | } | 454 | } |
| @@ -520,13 +461,17 @@ static inline int in_obp_range(unsigned long vaddr) | |||
| 520 | } | 461 | } |
| 521 | 462 | ||
| 522 | #define OBP_PMD_SIZE 2048 | 463 | #define OBP_PMD_SIZE 2048 |
| 523 | static void __init build_obp_pgtable(int prom_trans_ents) | 464 | static void __init build_obp_pgtable(void) |
| 524 | { | 465 | { |
| 525 | unsigned long i; | 466 | unsigned long i; |
| 526 | 467 | ||
| 527 | prom_pmd_phys = early_alloc_phys(OBP_PMD_SIZE); | 468 | prompmd = __alloc_bootmem(OBP_PMD_SIZE, OBP_PMD_SIZE, PAGE_SIZE); |
| 528 | for (i = 0; i < OBP_PMD_SIZE; i += 4) | 469 | if (!prompmd) |
| 529 | store_phys32(prom_pmd_phys + i, 0); | 470 | prom_halt(); |
| 471 | |||
| 472 | memset(prompmd, 0, OBP_PMD_SIZE); | ||
| 473 | |||
| 474 | prom_pmd_phys = __pa(prompmd); | ||
| 530 | 475 | ||
| 531 | for (i = 0; i < prom_trans_ents; i++) { | 476 | for (i = 0; i < prom_trans_ents; i++) { |
| 532 | unsigned long start, end; | 477 | unsigned long start, end; |
| @@ -546,7 +491,7 @@ static void __init build_obp_pgtable(int prom_trans_ents) | |||
| 546 | /* Read OBP translations property into 'prom_trans[]'. | 491 | /* Read OBP translations property into 'prom_trans[]'. |
| 547 | * Return the number of entries. | 492 | * Return the number of entries. |
| 548 | */ | 493 | */ |
| 549 | static int __init read_obp_translations(void) | 494 | static void __init read_obp_translations(void) |
| 550 | { | 495 | { |
| 551 | int n, node; | 496 | int n, node; |
| 552 | 497 | ||
| @@ -567,8 +512,10 @@ static int __init read_obp_translations(void) | |||
| 567 | prom_printf("prom_mappings: Couldn't get property.\n"); | 512 | prom_printf("prom_mappings: Couldn't get property.\n"); |
| 568 | prom_halt(); | 513 | prom_halt(); |
| 569 | } | 514 | } |
| 515 | |||
| 570 | n = n / sizeof(struct linux_prom_translation); | 516 | n = n / sizeof(struct linux_prom_translation); |
| 571 | return n; | 517 | |
| 518 | prom_trans_ents = n; | ||
| 572 | } | 519 | } |
| 573 | 520 | ||
| 574 | static void __init remap_kernel(void) | 521 | static void __init remap_kernel(void) |
| @@ -605,19 +552,21 @@ static void __init remap_kernel(void) | |||
| 605 | } | 552 | } |
| 606 | } | 553 | } |
| 607 | 554 | ||
| 608 | static void __init inherit_prom_mappings(void) | ||
| 609 | { | ||
| 610 | int n; | ||
| 611 | 555 | ||
| 612 | n = read_obp_translations(); | 556 | static void __init inherit_prom_mappings_pre(void) |
| 613 | build_obp_pgtable(n); | 557 | { |
| 558 | read_obp_translations(); | ||
| 614 | 559 | ||
| 615 | /* Now fixup OBP's idea about where we really are mapped. */ | 560 | /* Now fixup OBP's idea about where we really are mapped. */ |
| 616 | prom_printf("Remapping the kernel... "); | 561 | prom_printf("Remapping the kernel... "); |
| 617 | remap_kernel(); | 562 | remap_kernel(); |
| 618 | 563 | ||
| 619 | prom_printf("done.\n"); | 564 | prom_printf("done.\n"); |
| 565 | } | ||
| 620 | 566 | ||
| 567 | static void __init inherit_prom_mappings_post(void) | ||
| 568 | { | ||
| 569 | build_obp_pgtable(); | ||
| 621 | register_prom_callbacks(); | 570 | register_prom_callbacks(); |
| 622 | } | 571 | } |
| 623 | 572 | ||
| @@ -1570,8 +1519,7 @@ void __init paging_init(void) | |||
| 1570 | 1519 | ||
| 1571 | swapper_pgd_zero = pgd_val(swapper_pg_dir[0]); | 1520 | swapper_pgd_zero = pgd_val(swapper_pg_dir[0]); |
| 1572 | 1521 | ||
| 1573 | /* Inherit non-locked OBP mappings. */ | 1522 | inherit_prom_mappings_pre(); |
| 1574 | inherit_prom_mappings(); | ||
| 1575 | 1523 | ||
| 1576 | /* Ok, we can use our TLB miss and window trap handlers safely. | 1524 | /* Ok, we can use our TLB miss and window trap handlers safely. |
| 1577 | * We need to do a quick peek here to see if we are on StarFire | 1525 | * We need to do a quick peek here to see if we are on StarFire |
| @@ -1582,15 +1530,23 @@ void __init paging_init(void) | |||
| 1582 | extern void setup_tba(int); | 1530 | extern void setup_tba(int); |
| 1583 | setup_tba(this_is_starfire); | 1531 | setup_tba(this_is_starfire); |
| 1584 | } | 1532 | } |
| 1585 | |||
| 1586 | inherit_locked_prom_mappings(1); | ||
| 1587 | |||
| 1588 | __flush_tlb_all(); | 1533 | __flush_tlb_all(); |
| 1589 | 1534 | ||
| 1535 | /* Everything from this point forward, until we are done with | ||
| 1536 | * inherit_prom_mappings_post(), must complete successfully | ||
| 1537 | * without calling into the firmware. The firwmare page tables | ||
| 1538 | * have not been built, but we are running on the Linux kernel's | ||
| 1539 | * trap table. | ||
| 1540 | */ | ||
| 1541 | |||
| 1590 | /* Setup bootmem... */ | 1542 | /* Setup bootmem... */ |
| 1591 | pages_avail = 0; | 1543 | pages_avail = 0; |
| 1592 | last_valid_pfn = end_pfn = bootmem_init(&pages_avail); | 1544 | last_valid_pfn = end_pfn = bootmem_init(&pages_avail); |
| 1593 | 1545 | ||
| 1546 | inherit_prom_mappings_post(); | ||
| 1547 | |||
| 1548 | inherit_locked_prom_mappings(1); | ||
| 1549 | |||
| 1594 | #ifdef CONFIG_DEBUG_PAGEALLOC | 1550 | #ifdef CONFIG_DEBUG_PAGEALLOC |
| 1595 | kernel_physical_mapping_init(); | 1551 | kernel_physical_mapping_init(); |
| 1596 | #endif | 1552 | #endif |
