diff options
Diffstat (limited to 'arch/sparc64/mm/init.c')
-rw-r--r-- | arch/sparc64/mm/init.c | 187 |
1 files changed, 74 insertions, 113 deletions
diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c index 0d2e967c7200..1e44ee26cee8 100644 --- a/arch/sparc64/mm/init.c +++ b/arch/sparc64/mm/init.c | |||
@@ -105,7 +105,7 @@ static void __init read_obp_memory(const char *property, | |||
105 | regs[i].phys_addr = base; | 105 | regs[i].phys_addr = base; |
106 | regs[i].reg_size = size; | 106 | regs[i].reg_size = size; |
107 | } | 107 | } |
108 | sort(regs, ents, sizeof(struct linux_prom64_registers), | 108 | sort(regs, ents, sizeof(struct linux_prom64_registers), |
109 | cmp_p64, NULL); | 109 | cmp_p64, NULL); |
110 | } | 110 | } |
111 | 111 | ||
@@ -367,8 +367,11 @@ struct linux_prom_translation { | |||
367 | unsigned long size; | 367 | unsigned long size; |
368 | unsigned long data; | 368 | unsigned long data; |
369 | }; | 369 | }; |
370 | static struct linux_prom_translation prom_trans[512] __initdata; | 370 | |
371 | static unsigned int prom_trans_ents __initdata; | 371 | /* Exported for kernel TLB miss handling in ktlb.S */ |
372 | struct linux_prom_translation prom_trans[512] __read_mostly; | ||
373 | unsigned int prom_trans_ents __read_mostly; | ||
374 | unsigned int swapper_pgd_zero __read_mostly; | ||
372 | 375 | ||
373 | extern unsigned long prom_boot_page; | 376 | extern unsigned long prom_boot_page; |
374 | extern void prom_remap(unsigned long physpage, unsigned long virtpage, int mmu_ihandle); | 377 | extern void prom_remap(unsigned long physpage, unsigned long virtpage, int mmu_ihandle); |
@@ -378,122 +381,57 @@ extern void register_prom_callbacks(void); | |||
378 | /* Exported for SMP bootup purposes. */ | 381 | /* Exported for SMP bootup purposes. */ |
379 | unsigned long kern_locked_tte_data; | 382 | unsigned long kern_locked_tte_data; |
380 | 383 | ||
381 | /* Exported for kernel TLB miss handling in ktlb.S */ | ||
382 | unsigned long prom_pmd_phys __read_mostly; | ||
383 | unsigned int swapper_pgd_zero __read_mostly; | ||
384 | |||
385 | static pmd_t *prompmd __read_mostly; | ||
386 | |||
387 | #define BASE_PAGE_SIZE 8192 | ||
388 | |||
389 | /* | 384 | /* |
390 | * Translate PROM's mapping we capture at boot time into physical address. | 385 | * Translate PROM's mapping we capture at boot time into physical address. |
391 | * The second parameter is only set from prom_callback() invocations. | 386 | * The second parameter is only set from prom_callback() invocations. |
392 | */ | 387 | */ |
393 | unsigned long prom_virt_to_phys(unsigned long promva, int *error) | 388 | unsigned long prom_virt_to_phys(unsigned long promva, int *error) |
394 | { | 389 | { |
395 | pmd_t *pmdp = prompmd + ((promva >> 23) & 0x7ff); | 390 | int i; |
396 | pte_t *ptep; | ||
397 | unsigned long base; | ||
398 | |||
399 | if (pmd_none(*pmdp)) { | ||
400 | if (error) | ||
401 | *error = 1; | ||
402 | return 0; | ||
403 | } | ||
404 | ptep = (pte_t *)__pmd_page(*pmdp) + ((promva >> 13) & 0x3ff); | ||
405 | if (!pte_present(*ptep)) { | ||
406 | if (error) | ||
407 | *error = 1; | ||
408 | return 0; | ||
409 | } | ||
410 | if (error) { | ||
411 | *error = 0; | ||
412 | return pte_val(*ptep); | ||
413 | } | ||
414 | base = pte_val(*ptep) & _PAGE_PADDR; | ||
415 | |||
416 | return base + (promva & (BASE_PAGE_SIZE - 1)); | ||
417 | } | ||
418 | 391 | ||
419 | /* The obp translations are saved based on 8k pagesize, since obp can | 392 | for (i = 0; i < prom_trans_ents; i++) { |
420 | * use a mixture of pagesizes. Misses to the LOW_OBP_ADDRESS -> | 393 | struct linux_prom_translation *p = &prom_trans[i]; |
421 | * HI_OBP_ADDRESS range are handled in entry.S and do not use the vpte | ||
422 | * scheme (also, see rant in inherit_locked_prom_mappings()). | ||
423 | */ | ||
424 | static void __init build_obp_range(unsigned long start, unsigned long end, unsigned long data) | ||
425 | { | ||
426 | unsigned long vaddr; | ||
427 | 394 | ||
428 | for (vaddr = start; vaddr < end; vaddr += BASE_PAGE_SIZE) { | 395 | if (promva >= p->virt && |
429 | unsigned long val; | 396 | promva < (p->virt + p->size)) { |
430 | pmd_t *pmd; | 397 | unsigned long base = p->data & _PAGE_PADDR; |
431 | pte_t *pte; | ||
432 | 398 | ||
433 | pmd = prompmd + ((vaddr >> 23) & 0x7ff); | 399 | if (error) |
434 | if (pmd_none(*pmd)) { | 400 | *error = 0; |
435 | pte = __alloc_bootmem(BASE_PAGE_SIZE, BASE_PAGE_SIZE, | 401 | return base + (promva & (8192 - 1)); |
436 | PAGE_SIZE); | ||
437 | if (!pte) | ||
438 | prom_halt(); | ||
439 | memset(pte, 0, BASE_PAGE_SIZE); | ||
440 | pmd_set(pmd, pte); | ||
441 | } | 402 | } |
442 | pte = (pte_t *) __pmd_page(*pmd) + ((vaddr >> 13) & 0x3ff); | ||
443 | |||
444 | val = data; | ||
445 | |||
446 | /* Clear diag TTE bits. */ | ||
447 | if (tlb_type == spitfire) | ||
448 | val &= ~0x0003fe0000000000UL; | ||
449 | |||
450 | set_pte_at(&init_mm, vaddr, pte, | ||
451 | __pte(val | _PAGE_MODIFIED)); | ||
452 | |||
453 | data += BASE_PAGE_SIZE; | ||
454 | } | 403 | } |
404 | if (error) | ||
405 | *error = 1; | ||
406 | return 0UL; | ||
455 | } | 407 | } |
456 | 408 | ||
409 | /* The obp translations are saved based on 8k pagesize, since obp can | ||
410 | * use a mixture of pagesizes. Misses to the LOW_OBP_ADDRESS -> | ||
411 | * HI_OBP_ADDRESS range are handled in ktlb.S and do not use the vpte | ||
412 | * scheme (also, see rant in inherit_locked_prom_mappings()). | ||
413 | */ | ||
457 | static inline int in_obp_range(unsigned long vaddr) | 414 | static inline int in_obp_range(unsigned long vaddr) |
458 | { | 415 | { |
459 | return (vaddr >= LOW_OBP_ADDRESS && | 416 | return (vaddr >= LOW_OBP_ADDRESS && |
460 | vaddr < HI_OBP_ADDRESS); | 417 | vaddr < HI_OBP_ADDRESS); |
461 | } | 418 | } |
462 | 419 | ||
463 | #define OBP_PMD_SIZE 2048 | 420 | static int cmp_ptrans(const void *a, const void *b) |
464 | static void __init build_obp_pgtable(void) | ||
465 | { | 421 | { |
466 | unsigned long i; | 422 | const struct linux_prom_translation *x = a, *y = b; |
467 | |||
468 | prompmd = __alloc_bootmem(OBP_PMD_SIZE, OBP_PMD_SIZE, PAGE_SIZE); | ||
469 | if (!prompmd) | ||
470 | prom_halt(); | ||
471 | |||
472 | memset(prompmd, 0, OBP_PMD_SIZE); | ||
473 | |||
474 | prom_pmd_phys = __pa(prompmd); | ||
475 | |||
476 | for (i = 0; i < prom_trans_ents; i++) { | ||
477 | unsigned long start, end; | ||
478 | |||
479 | if (!in_obp_range(prom_trans[i].virt)) | ||
480 | continue; | ||
481 | 423 | ||
482 | start = prom_trans[i].virt; | 424 | if (x->virt > y->virt) |
483 | end = start + prom_trans[i].size; | 425 | return 1; |
484 | if (end > HI_OBP_ADDRESS) | 426 | if (x->virt < y->virt) |
485 | end = HI_OBP_ADDRESS; | 427 | return -1; |
486 | 428 | return 0; | |
487 | build_obp_range(start, end, prom_trans[i].data); | ||
488 | } | ||
489 | } | 429 | } |
490 | 430 | ||
491 | /* Read OBP translations property into 'prom_trans[]'. | 431 | /* Read OBP translations property into 'prom_trans[]'. */ |
492 | * Return the number of entries. | ||
493 | */ | ||
494 | static void __init read_obp_translations(void) | 432 | static void __init read_obp_translations(void) |
495 | { | 433 | { |
496 | int n, node; | 434 | int n, node, ents, first, last, i; |
497 | 435 | ||
498 | node = prom_finddevice("/virtual-memory"); | 436 | node = prom_finddevice("/virtual-memory"); |
499 | n = prom_getproplen(node, "translations"); | 437 | n = prom_getproplen(node, "translations"); |
@@ -515,7 +453,41 @@ static void __init read_obp_translations(void) | |||
515 | 453 | ||
516 | n = n / sizeof(struct linux_prom_translation); | 454 | n = n / sizeof(struct linux_prom_translation); |
517 | 455 | ||
518 | prom_trans_ents = n; | 456 | ents = n; |
457 | |||
458 | sort(prom_trans, ents, sizeof(struct linux_prom_translation), | ||
459 | cmp_ptrans, NULL); | ||
460 | |||
461 | /* Now kick out all the non-OBP entries. */ | ||
462 | for (i = 0; i < ents; i++) { | ||
463 | if (in_obp_range(prom_trans[i].virt)) | ||
464 | break; | ||
465 | } | ||
466 | first = i; | ||
467 | for (; i < ents; i++) { | ||
468 | if (!in_obp_range(prom_trans[i].virt)) | ||
469 | break; | ||
470 | } | ||
471 | last = i; | ||
472 | |||
473 | for (i = 0; i < (last - first); i++) { | ||
474 | struct linux_prom_translation *src = &prom_trans[i + first]; | ||
475 | struct linux_prom_translation *dest = &prom_trans[i]; | ||
476 | |||
477 | *dest = *src; | ||
478 | } | ||
479 | for (; i < ents; i++) { | ||
480 | struct linux_prom_translation *dest = &prom_trans[i]; | ||
481 | dest->virt = dest->size = dest->data = 0x0UL; | ||
482 | } | ||
483 | |||
484 | prom_trans_ents = last - first; | ||
485 | |||
486 | if (tlb_type == spitfire) { | ||
487 | /* Clear diag TTE bits. */ | ||
488 | for (i = 0; i < prom_trans_ents; i++) | ||
489 | prom_trans[i].data &= ~0x0003fe0000000000UL; | ||
490 | } | ||
519 | } | 491 | } |
520 | 492 | ||
521 | static void __init remap_kernel(void) | 493 | static void __init remap_kernel(void) |
@@ -553,21 +525,18 @@ static void __init remap_kernel(void) | |||
553 | } | 525 | } |
554 | 526 | ||
555 | 527 | ||
556 | static void __init inherit_prom_mappings_pre(void) | 528 | static void __init inherit_prom_mappings(void) |
557 | { | 529 | { |
558 | read_obp_translations(); | 530 | read_obp_translations(); |
559 | 531 | ||
560 | /* Now fixup OBP's idea about where we really are mapped. */ | 532 | /* Now fixup OBP's idea about where we really are mapped. */ |
561 | prom_printf("Remapping the kernel... "); | 533 | prom_printf("Remapping the kernel... "); |
562 | remap_kernel(); | 534 | remap_kernel(); |
563 | |||
564 | prom_printf("done.\n"); | 535 | prom_printf("done.\n"); |
565 | } | ||
566 | 536 | ||
567 | static void __init inherit_prom_mappings_post(void) | 537 | prom_printf("Registering callbacks... "); |
568 | { | ||
569 | build_obp_pgtable(); | ||
570 | register_prom_callbacks(); | 538 | register_prom_callbacks(); |
539 | prom_printf("done.\n"); | ||
571 | } | 540 | } |
572 | 541 | ||
573 | /* The OBP specifications for sun4u mark 0xfffffffc00000000 and | 542 | /* The OBP specifications for sun4u mark 0xfffffffc00000000 and |
@@ -1519,7 +1488,7 @@ void __init paging_init(void) | |||
1519 | 1488 | ||
1520 | swapper_pgd_zero = pgd_val(swapper_pg_dir[0]); | 1489 | swapper_pgd_zero = pgd_val(swapper_pg_dir[0]); |
1521 | 1490 | ||
1522 | inherit_prom_mappings_pre(); | 1491 | inherit_prom_mappings(); |
1523 | 1492 | ||
1524 | /* Ok, we can use our TLB miss and window trap handlers safely. | 1493 | /* Ok, we can use our TLB miss and window trap handlers safely. |
1525 | * We need to do a quick peek here to see if we are on StarFire | 1494 | * We need to do a quick peek here to see if we are on StarFire |
@@ -1530,23 +1499,15 @@ void __init paging_init(void) | |||
1530 | extern void setup_tba(int); | 1499 | extern void setup_tba(int); |
1531 | setup_tba(this_is_starfire); | 1500 | setup_tba(this_is_starfire); |
1532 | } | 1501 | } |
1533 | __flush_tlb_all(); | ||
1534 | 1502 | ||
1535 | /* Everything from this point forward, until we are done with | 1503 | inherit_locked_prom_mappings(1); |
1536 | * inherit_prom_mappings_post(), must complete successfully | 1504 | |
1537 | * without calling into the firmware. The firwmare page tables | 1505 | __flush_tlb_all(); |
1538 | * have not been built, but we are running on the Linux kernel's | ||
1539 | * trap table. | ||
1540 | */ | ||
1541 | 1506 | ||
1542 | /* Setup bootmem... */ | 1507 | /* Setup bootmem... */ |
1543 | pages_avail = 0; | 1508 | pages_avail = 0; |
1544 | last_valid_pfn = end_pfn = bootmem_init(&pages_avail); | 1509 | last_valid_pfn = end_pfn = bootmem_init(&pages_avail); |
1545 | 1510 | ||
1546 | inherit_prom_mappings_post(); | ||
1547 | |||
1548 | inherit_locked_prom_mappings(1); | ||
1549 | |||
1550 | #ifdef CONFIG_DEBUG_PAGEALLOC | 1511 | #ifdef CONFIG_DEBUG_PAGEALLOC |
1551 | kernel_physical_mapping_init(); | 1512 | kernel_physical_mapping_init(); |
1552 | #endif | 1513 | #endif |