diff options
Diffstat (limited to 'arch/sparc64/mm/init.c')
-rw-r--r-- | arch/sparc64/mm/init.c | 255 |
1 files changed, 93 insertions, 162 deletions
diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c index 5db50524f20d..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 | ||
@@ -133,6 +133,12 @@ extern unsigned int sparc_ramdisk_size; | |||
133 | 133 | ||
134 | struct page *mem_map_zero __read_mostly; | 134 | struct page *mem_map_zero __read_mostly; |
135 | 135 | ||
136 | unsigned int sparc64_highest_unlocked_tlb_ent __read_mostly; | ||
137 | |||
138 | unsigned long sparc64_kern_pri_context __read_mostly; | ||
139 | unsigned long sparc64_kern_pri_nuc_bits __read_mostly; | ||
140 | unsigned long sparc64_kern_sec_context __read_mostly; | ||
141 | |||
136 | int bigkernel = 0; | 142 | int bigkernel = 0; |
137 | 143 | ||
138 | /* XXX Tune this... */ | 144 | /* XXX Tune this... */ |
@@ -361,7 +367,11 @@ struct linux_prom_translation { | |||
361 | unsigned long size; | 367 | unsigned long size; |
362 | unsigned long data; | 368 | unsigned long data; |
363 | }; | 369 | }; |
364 | static struct linux_prom_translation prom_trans[512] __initdata; | 370 | |
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; | ||
365 | 375 | ||
366 | extern unsigned long prom_boot_page; | 376 | extern unsigned long prom_boot_page; |
367 | 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); |
@@ -371,178 +381,57 @@ extern void register_prom_callbacks(void); | |||
371 | /* Exported for SMP bootup purposes. */ | 381 | /* Exported for SMP bootup purposes. */ |
372 | unsigned long kern_locked_tte_data; | 382 | unsigned long kern_locked_tte_data; |
373 | 383 | ||
374 | /* Exported for kernel TLB miss handling in ktlb.S */ | ||
375 | unsigned long prom_pmd_phys __read_mostly; | ||
376 | unsigned int swapper_pgd_zero __read_mostly; | ||
377 | |||
378 | /* Allocate power-of-2 aligned chunks from the end of the | ||
379 | * kernel image. Return physical address. | ||
380 | */ | ||
381 | static inline unsigned long early_alloc_phys(unsigned long size) | ||
382 | { | ||
383 | unsigned long base; | ||
384 | |||
385 | BUILD_BUG_ON(size & (size - 1)); | ||
386 | |||
387 | kern_size = (kern_size + (size - 1)) & ~(size - 1); | ||
388 | base = kern_base + kern_size; | ||
389 | kern_size += size; | ||
390 | |||
391 | return base; | ||
392 | } | ||
393 | |||
394 | static inline unsigned long load_phys32(unsigned long pa) | ||
395 | { | ||
396 | unsigned long val; | ||
397 | |||
398 | __asm__ __volatile__("lduwa [%1] %2, %0" | ||
399 | : "=&r" (val) | ||
400 | : "r" (pa), "i" (ASI_PHYS_USE_EC)); | ||
401 | |||
402 | return val; | ||
403 | } | ||
404 | |||
405 | static inline unsigned long load_phys64(unsigned long pa) | ||
406 | { | ||
407 | unsigned long val; | ||
408 | |||
409 | __asm__ __volatile__("ldxa [%1] %2, %0" | ||
410 | : "=&r" (val) | ||
411 | : "r" (pa), "i" (ASI_PHYS_USE_EC)); | ||
412 | |||
413 | return val; | ||
414 | } | ||
415 | |||
416 | static inline void store_phys32(unsigned long pa, unsigned long val) | ||
417 | { | ||
418 | __asm__ __volatile__("stwa %0, [%1] %2" | ||
419 | : /* no outputs */ | ||
420 | : "r" (val), "r" (pa), "i" (ASI_PHYS_USE_EC)); | ||
421 | } | ||
422 | |||
423 | static inline void store_phys64(unsigned long pa, unsigned long val) | ||
424 | { | ||
425 | __asm__ __volatile__("stxa %0, [%1] %2" | ||
426 | : /* no outputs */ | ||
427 | : "r" (val), "r" (pa), "i" (ASI_PHYS_USE_EC)); | ||
428 | } | ||
429 | |||
430 | #define BASE_PAGE_SIZE 8192 | ||
431 | |||
432 | /* | 384 | /* |
433 | * 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. |
434 | * The second parameter is only set from prom_callback() invocations. | 386 | * The second parameter is only set from prom_callback() invocations. |
435 | */ | 387 | */ |
436 | unsigned long prom_virt_to_phys(unsigned long promva, int *error) | 388 | unsigned long prom_virt_to_phys(unsigned long promva, int *error) |
437 | { | 389 | { |
438 | unsigned long pmd_phys = (prom_pmd_phys + | 390 | int i; |
439 | ((promva >> 23) & 0x7ff) * sizeof(pmd_t)); | ||
440 | unsigned long pte_phys; | ||
441 | pmd_t pmd_ent; | ||
442 | pte_t pte_ent; | ||
443 | unsigned long base; | ||
444 | |||
445 | pmd_val(pmd_ent) = load_phys32(pmd_phys); | ||
446 | if (pmd_none(pmd_ent)) { | ||
447 | if (error) | ||
448 | *error = 1; | ||
449 | return 0; | ||
450 | } | ||
451 | 391 | ||
452 | pte_phys = (unsigned long)pmd_val(pmd_ent) << 11UL; | 392 | for (i = 0; i < prom_trans_ents; i++) { |
453 | pte_phys += ((promva >> 13) & 0x3ff) * sizeof(pte_t); | 393 | struct linux_prom_translation *p = &prom_trans[i]; |
454 | pte_val(pte_ent) = load_phys64(pte_phys); | 394 | |
455 | if (!pte_present(pte_ent)) { | 395 | if (promva >= p->virt && |
456 | if (error) | 396 | promva < (p->virt + p->size)) { |
457 | *error = 1; | 397 | unsigned long base = p->data & _PAGE_PADDR; |
458 | return 0; | 398 | |
459 | } | 399 | if (error) |
460 | if (error) { | 400 | *error = 0; |
461 | *error = 0; | 401 | return base + (promva & (8192 - 1)); |
462 | return pte_val(pte_ent); | 402 | } |
463 | } | 403 | } |
464 | base = pte_val(pte_ent) & _PAGE_PADDR; | 404 | if (error) |
465 | return (base + (promva & (BASE_PAGE_SIZE - 1))); | 405 | *error = 1; |
406 | return 0UL; | ||
466 | } | 407 | } |
467 | 408 | ||
468 | /* The obp translations are saved based on 8k pagesize, since obp can | 409 | /* The obp translations are saved based on 8k pagesize, since obp can |
469 | * use a mixture of pagesizes. Misses to the LOW_OBP_ADDRESS -> | 410 | * use a mixture of pagesizes. Misses to the LOW_OBP_ADDRESS -> |
470 | * HI_OBP_ADDRESS range are handled in entry.S and do not use the vpte | 411 | * HI_OBP_ADDRESS range are handled in ktlb.S and do not use the vpte |
471 | * scheme (also, see rant in inherit_locked_prom_mappings()). | 412 | * scheme (also, see rant in inherit_locked_prom_mappings()). |
472 | */ | 413 | */ |
473 | static void __init build_obp_range(unsigned long start, unsigned long end, unsigned long data) | ||
474 | { | ||
475 | unsigned long vaddr; | ||
476 | |||
477 | for (vaddr = start; vaddr < end; vaddr += BASE_PAGE_SIZE) { | ||
478 | unsigned long val, pte_phys, pmd_phys; | ||
479 | pmd_t pmd_ent; | ||
480 | int i; | ||
481 | |||
482 | pmd_phys = (prom_pmd_phys + | ||
483 | (((vaddr >> 23) & 0x7ff) * sizeof(pmd_t))); | ||
484 | pmd_val(pmd_ent) = load_phys32(pmd_phys); | ||
485 | if (pmd_none(pmd_ent)) { | ||
486 | pte_phys = early_alloc_phys(BASE_PAGE_SIZE); | ||
487 | |||
488 | for (i = 0; i < BASE_PAGE_SIZE / sizeof(pte_t); i++) | ||
489 | store_phys64(pte_phys+i*sizeof(pte_t),0); | ||
490 | |||
491 | pmd_val(pmd_ent) = pte_phys >> 11UL; | ||
492 | store_phys32(pmd_phys, pmd_val(pmd_ent)); | ||
493 | } | ||
494 | |||
495 | pte_phys = (unsigned long)pmd_val(pmd_ent) << 11UL; | ||
496 | pte_phys += (((vaddr >> 13) & 0x3ff) * sizeof(pte_t)); | ||
497 | |||
498 | val = data; | ||
499 | |||
500 | /* Clear diag TTE bits. */ | ||
501 | if (tlb_type == spitfire) | ||
502 | val &= ~0x0003fe0000000000UL; | ||
503 | |||
504 | store_phys64(pte_phys, val | _PAGE_MODIFIED); | ||
505 | |||
506 | data += BASE_PAGE_SIZE; | ||
507 | } | ||
508 | } | ||
509 | |||
510 | static inline int in_obp_range(unsigned long vaddr) | 414 | static inline int in_obp_range(unsigned long vaddr) |
511 | { | 415 | { |
512 | return (vaddr >= LOW_OBP_ADDRESS && | 416 | return (vaddr >= LOW_OBP_ADDRESS && |
513 | vaddr < HI_OBP_ADDRESS); | 417 | vaddr < HI_OBP_ADDRESS); |
514 | } | 418 | } |
515 | 419 | ||
516 | #define OBP_PMD_SIZE 2048 | 420 | static int cmp_ptrans(const void *a, const void *b) |
517 | static void __init build_obp_pgtable(int prom_trans_ents) | ||
518 | { | 421 | { |
519 | unsigned long i; | 422 | const struct linux_prom_translation *x = a, *y = b; |
520 | |||
521 | prom_pmd_phys = early_alloc_phys(OBP_PMD_SIZE); | ||
522 | for (i = 0; i < OBP_PMD_SIZE; i += 4) | ||
523 | store_phys32(prom_pmd_phys + i, 0); | ||
524 | 423 | ||
525 | for (i = 0; i < prom_trans_ents; i++) { | 424 | if (x->virt > y->virt) |
526 | unsigned long start, end; | 425 | return 1; |
527 | 426 | if (x->virt < y->virt) | |
528 | if (!in_obp_range(prom_trans[i].virt)) | 427 | return -1; |
529 | continue; | 428 | return 0; |
530 | |||
531 | start = prom_trans[i].virt; | ||
532 | end = start + prom_trans[i].size; | ||
533 | if (end > HI_OBP_ADDRESS) | ||
534 | end = HI_OBP_ADDRESS; | ||
535 | |||
536 | build_obp_range(start, end, prom_trans[i].data); | ||
537 | } | ||
538 | } | 429 | } |
539 | 430 | ||
540 | /* Read OBP translations property into 'prom_trans[]'. | 431 | /* Read OBP translations property into 'prom_trans[]'. */ |
541 | * Return the number of entries. | 432 | static void __init read_obp_translations(void) |
542 | */ | ||
543 | static int __init read_obp_translations(void) | ||
544 | { | 433 | { |
545 | int n, node; | 434 | int n, node, ents, first, last, i; |
546 | 435 | ||
547 | node = prom_finddevice("/virtual-memory"); | 436 | node = prom_finddevice("/virtual-memory"); |
548 | n = prom_getproplen(node, "translations"); | 437 | n = prom_getproplen(node, "translations"); |
@@ -561,8 +450,44 @@ static int __init read_obp_translations(void) | |||
561 | prom_printf("prom_mappings: Couldn't get property.\n"); | 450 | prom_printf("prom_mappings: Couldn't get property.\n"); |
562 | prom_halt(); | 451 | prom_halt(); |
563 | } | 452 | } |
453 | |||
564 | n = n / sizeof(struct linux_prom_translation); | 454 | n = n / sizeof(struct linux_prom_translation); |
565 | return n; | 455 | |
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 | } | ||
566 | } | 491 | } |
567 | 492 | ||
568 | static void __init remap_kernel(void) | 493 | static void __init remap_kernel(void) |
@@ -582,29 +507,36 @@ static void __init remap_kernel(void) | |||
582 | prom_dtlb_load(tlb_ent, tte_data, tte_vaddr); | 507 | prom_dtlb_load(tlb_ent, tte_data, tte_vaddr); |
583 | prom_itlb_load(tlb_ent, tte_data, tte_vaddr); | 508 | prom_itlb_load(tlb_ent, tte_data, tte_vaddr); |
584 | if (bigkernel) { | 509 | if (bigkernel) { |
585 | prom_dtlb_load(tlb_ent - 1, | 510 | tlb_ent -= 1; |
511 | prom_dtlb_load(tlb_ent, | ||
586 | tte_data + 0x400000, | 512 | tte_data + 0x400000, |
587 | tte_vaddr + 0x400000); | 513 | tte_vaddr + 0x400000); |
588 | prom_itlb_load(tlb_ent - 1, | 514 | prom_itlb_load(tlb_ent, |
589 | tte_data + 0x400000, | 515 | tte_data + 0x400000, |
590 | tte_vaddr + 0x400000); | 516 | tte_vaddr + 0x400000); |
591 | } | 517 | } |
518 | sparc64_highest_unlocked_tlb_ent = tlb_ent - 1; | ||
519 | if (tlb_type == cheetah_plus) { | ||
520 | sparc64_kern_pri_context = (CTX_CHEETAH_PLUS_CTX0 | | ||
521 | CTX_CHEETAH_PLUS_NUC); | ||
522 | sparc64_kern_pri_nuc_bits = CTX_CHEETAH_PLUS_NUC; | ||
523 | sparc64_kern_sec_context = CTX_CHEETAH_PLUS_CTX0; | ||
524 | } | ||
592 | } | 525 | } |
593 | 526 | ||
527 | |||
594 | static void __init inherit_prom_mappings(void) | 528 | static void __init inherit_prom_mappings(void) |
595 | { | 529 | { |
596 | int n; | 530 | read_obp_translations(); |
597 | |||
598 | n = read_obp_translations(); | ||
599 | build_obp_pgtable(n); | ||
600 | 531 | ||
601 | /* Now fixup OBP's idea about where we really are mapped. */ | 532 | /* Now fixup OBP's idea about where we really are mapped. */ |
602 | prom_printf("Remapping the kernel... "); | 533 | prom_printf("Remapping the kernel... "); |
603 | remap_kernel(); | 534 | remap_kernel(); |
604 | |||
605 | prom_printf("done.\n"); | 535 | prom_printf("done.\n"); |
606 | 536 | ||
537 | prom_printf("Registering callbacks... "); | ||
607 | register_prom_callbacks(); | 538 | register_prom_callbacks(); |
539 | prom_printf("done.\n"); | ||
608 | } | 540 | } |
609 | 541 | ||
610 | /* The OBP specifications for sun4u mark 0xfffffffc00000000 and | 542 | /* The OBP specifications for sun4u mark 0xfffffffc00000000 and |
@@ -788,8 +720,8 @@ void inherit_locked_prom_mappings(int save_p) | |||
788 | } | 720 | } |
789 | } | 721 | } |
790 | if (tlb_type == spitfire) { | 722 | if (tlb_type == spitfire) { |
791 | int high = SPITFIRE_HIGHEST_LOCKED_TLBENT - bigkernel; | 723 | int high = sparc64_highest_unlocked_tlb_ent; |
792 | for (i = 0; i < high; i++) { | 724 | for (i = 0; i <= high; i++) { |
793 | unsigned long data; | 725 | unsigned long data; |
794 | 726 | ||
795 | /* Spitfire Errata #32 workaround */ | 727 | /* Spitfire Errata #32 workaround */ |
@@ -877,9 +809,9 @@ void inherit_locked_prom_mappings(int save_p) | |||
877 | } | 809 | } |
878 | } | 810 | } |
879 | } else if (tlb_type == cheetah || tlb_type == cheetah_plus) { | 811 | } else if (tlb_type == cheetah || tlb_type == cheetah_plus) { |
880 | int high = CHEETAH_HIGHEST_LOCKED_TLBENT - bigkernel; | 812 | int high = sparc64_highest_unlocked_tlb_ent; |
881 | 813 | ||
882 | for (i = 0; i < high; i++) { | 814 | for (i = 0; i <= high; i++) { |
883 | unsigned long data; | 815 | unsigned long data; |
884 | 816 | ||
885 | data = cheetah_get_ldtlb_data(i); | 817 | data = cheetah_get_ldtlb_data(i); |
@@ -1556,7 +1488,6 @@ void __init paging_init(void) | |||
1556 | 1488 | ||
1557 | swapper_pgd_zero = pgd_val(swapper_pg_dir[0]); | 1489 | swapper_pgd_zero = pgd_val(swapper_pg_dir[0]); |
1558 | 1490 | ||
1559 | /* Inherit non-locked OBP mappings. */ | ||
1560 | inherit_prom_mappings(); | 1491 | inherit_prom_mappings(); |
1561 | 1492 | ||
1562 | /* 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. |