diff options
| -rw-r--r-- | arch/sparc64/mm/init.c | 301 |
1 files changed, 160 insertions, 141 deletions
diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c index 141d4cc5fc53..11d2187990d4 100644 --- a/arch/sparc64/mm/init.c +++ b/arch/sparc64/mm/init.c | |||
| @@ -362,84 +362,107 @@ unsigned long prom_virt_to_phys(unsigned long promva, int *error) | |||
| 362 | return(base + (promva & (BASE_PAGE_SIZE - 1))); | 362 | return(base + (promva & (BASE_PAGE_SIZE - 1))); |
| 363 | } | 363 | } |
| 364 | 364 | ||
| 365 | static void inherit_prom_mappings(void) | 365 | static inline int in_obp_range(unsigned long vaddr) |
| 366 | { | 366 | { |
| 367 | unsigned long phys_page, tte_vaddr, tte_data; | 367 | return (vaddr >= LOW_OBP_ADDRESS && |
| 368 | void (*remap_func)(unsigned long, unsigned long, int); | 368 | vaddr < HI_OBP_ADDRESS); |
| 369 | pmd_t *pmdp; | 369 | } |
| 370 | pte_t *ptep; | ||
| 371 | int node, n, i, tsz; | ||
| 372 | 370 | ||
| 373 | node = prom_finddevice("/virtual-memory"); | 371 | /* The obp translations are saved based on 8k pagesize, since obp can |
| 374 | n = prom_getproplen(node, "translations"); | 372 | * use a mixture of pagesizes. Misses to the LOW_OBP_ADDRESS -> |
| 375 | if (n == 0 || n == -1) { | 373 | * HI_OBP_ADDRESS range are handled in entry.S and do not use the vpte |
| 376 | prom_printf("prom_mappings: Couldn't get size.\n"); | 374 | * scheme (also, see rant in inherit_locked_prom_mappings()). |
| 377 | prom_halt(); | 375 | */ |
| 378 | } | 376 | static void build_obp_range(unsigned long start, unsigned long end, unsigned long data) |
| 379 | n += 24 * sizeof(struct linux_prom_translation); | 377 | { |
| 380 | if (n > sizeof(prom_trans)) { | 378 | unsigned long vaddr; |
| 381 | prom_printf("prom_mappings: prom_trans too small, " | 379 | |
| 382 | "need %Zd bytes\n", n); | 380 | for (vaddr = start; vaddr < end; vaddr += BASE_PAGE_SIZE) { |
| 383 | prom_halt(); | 381 | unsigned long val; |
| 384 | } | 382 | pmd_t *pmdp; |
| 385 | tsz = n; | 383 | pte_t *ptep; |
| 386 | if ((n = prom_getproperty(node, "translations", | 384 | |
| 387 | (char *)&prom_trans[0], tsz)) == -1) { | 385 | pmdp = prompmd + ((vaddr >> 23) & 0x7ff); |
| 388 | prom_printf("prom_mappings: Couldn't get property.\n"); | 386 | if (pmd_none(*pmdp)) { |
| 389 | prom_halt(); | 387 | ptep = __alloc_bootmem(BASE_PAGE_SIZE, |
| 388 | BASE_PAGE_SIZE, | ||
| 389 | bootmap_base); | ||
| 390 | if (ptep == NULL) | ||
| 391 | early_pgtable_allocfail("pte"); | ||
| 392 | memset(ptep, 0, BASE_PAGE_SIZE); | ||
| 393 | pmd_set(pmdp, ptep); | ||
| 394 | } | ||
| 395 | ptep = (pte_t *)__pmd_page(*pmdp) + | ||
| 396 | ((vaddr >> 13) & 0x3ff); | ||
| 397 | |||
| 398 | val = data; | ||
| 399 | |||
| 400 | /* Clear diag TTE bits. */ | ||
| 401 | if (tlb_type == spitfire) | ||
| 402 | val &= ~0x0003fe0000000000UL; | ||
| 403 | |||
| 404 | set_pte_at(&init_mm, vaddr, | ||
| 405 | ptep, __pte(val | _PAGE_MODIFIED)); | ||
| 406 | data += BASE_PAGE_SIZE; | ||
| 390 | } | 407 | } |
| 391 | n = n / sizeof(struct linux_prom_translation); | 408 | } |
| 392 | 409 | ||
| 393 | /* The obp translations are saved based on 8k pagesize, since obp | ||
| 394 | * can use a mixture of pagesizes. Misses to the 0xf0000000 -> | ||
| 395 | * 0x100000000, ie obp range, are handled in entry.S and do not | ||
| 396 | * use the vpte scheme (see rant: inherit_locked_prom_mappings). | ||
| 397 | */ | ||
| 398 | #define OBP_PMD_SIZE 2048 | 410 | #define OBP_PMD_SIZE 2048 |
| 399 | prompmd = __alloc_bootmem(OBP_PMD_SIZE, OBP_PMD_SIZE, bootmap_base); | 411 | static void build_obp_pgtable(int prom_trans_ents) |
| 412 | { | ||
| 413 | int i; | ||
| 414 | |||
| 415 | prompmd = __alloc_bootmem(OBP_PMD_SIZE, OBP_PMD_SIZE, | ||
| 416 | bootmap_base); | ||
| 400 | if (prompmd == NULL) | 417 | if (prompmd == NULL) |
| 401 | early_pgtable_allocfail("pmd"); | 418 | early_pgtable_allocfail("pmd"); |
| 402 | memset(prompmd, 0, OBP_PMD_SIZE); | 419 | memset(prompmd, 0, OBP_PMD_SIZE); |
| 403 | for (i = 0; i < n; i++) { | 420 | for (i = 0; i < prom_trans_ents; i++) { |
| 404 | unsigned long vaddr; | 421 | unsigned long start, end; |
| 405 | |||
| 406 | if (prom_trans[i].virt >= LOW_OBP_ADDRESS && prom_trans[i].virt < HI_OBP_ADDRESS) { | ||
| 407 | for (vaddr = prom_trans[i].virt; | ||
| 408 | ((vaddr < prom_trans[i].virt + prom_trans[i].size) && | ||
| 409 | (vaddr < HI_OBP_ADDRESS)); | ||
| 410 | vaddr += BASE_PAGE_SIZE) { | ||
| 411 | unsigned long val; | ||
| 412 | |||
| 413 | pmdp = prompmd + ((vaddr >> 23) & 0x7ff); | ||
| 414 | if (pmd_none(*pmdp)) { | ||
| 415 | ptep = __alloc_bootmem(BASE_PAGE_SIZE, | ||
| 416 | BASE_PAGE_SIZE, | ||
| 417 | bootmap_base); | ||
| 418 | if (ptep == NULL) | ||
| 419 | early_pgtable_allocfail("pte"); | ||
| 420 | memset(ptep, 0, BASE_PAGE_SIZE); | ||
| 421 | pmd_set(pmdp, ptep); | ||
| 422 | } | ||
| 423 | ptep = (pte_t *)__pmd_page(*pmdp) + | ||
| 424 | ((vaddr >> 13) & 0x3ff); | ||
| 425 | 422 | ||
| 426 | val = prom_trans[i].data; | 423 | if (!in_obp_range(prom_trans[i].virt)) |
| 424 | continue; | ||
| 427 | 425 | ||
| 428 | /* Clear diag TTE bits. */ | 426 | start = prom_trans[i].virt; |
| 429 | if (tlb_type == spitfire) | 427 | end = start + prom_trans[i].size; |
| 430 | val &= ~0x0003fe0000000000UL; | 428 | if (end > HI_OBP_ADDRESS) |
| 429 | end = HI_OBP_ADDRESS; | ||
| 431 | 430 | ||
| 432 | set_pte_at(&init_mm, vaddr, | 431 | build_obp_range(start, end, prom_trans[i].data); |
| 433 | ptep, __pte(val | _PAGE_MODIFIED)); | ||
| 434 | prom_trans[i].data += BASE_PAGE_SIZE; | ||
| 435 | } | ||
| 436 | } | ||
| 437 | } | 432 | } |
| 438 | prom_pmd_phys = __pa(prompmd); | 433 | prom_pmd_phys = __pa(prompmd); |
| 434 | } | ||
| 439 | 435 | ||
| 440 | /* Now fixup OBP's idea about where we really are mapped. */ | 436 | /* Read OBP translations property into 'prom_trans[]'. |
| 441 | prom_printf("Remapping the kernel... "); | 437 | * Return the number of entries. |
| 438 | */ | ||
| 439 | static int read_obp_translations(void) | ||
| 440 | { | ||
| 441 | int n, node; | ||
| 442 | 442 | ||
| 443 | node = prom_finddevice("/virtual-memory"); | ||
| 444 | n = prom_getproplen(node, "translations"); | ||
| 445 | if (unlikely(n == 0 || n == -1)) { | ||
| 446 | prom_printf("prom_mappings: Couldn't get size.\n"); | ||
| 447 | prom_halt(); | ||
| 448 | } | ||
| 449 | if (unlikely(n > sizeof(prom_trans))) { | ||
| 450 | prom_printf("prom_mappings: Size %Zd is too big.\n", n); | ||
| 451 | prom_halt(); | ||
| 452 | } | ||
| 453 | |||
| 454 | if ((n = prom_getproperty(node, "translations", | ||
| 455 | (char *)&prom_trans[0], | ||
| 456 | sizeof(prom_trans))) == -1) { | ||
| 457 | prom_printf("prom_mappings: Couldn't get property.\n"); | ||
| 458 | prom_halt(); | ||
| 459 | } | ||
| 460 | n = n / sizeof(struct linux_prom_translation); | ||
| 461 | return n; | ||
| 462 | } | ||
| 463 | |||
| 464 | static inline void early_spitfire_errata32(void) | ||
| 465 | { | ||
| 443 | /* Spitfire Errata #32 workaround */ | 466 | /* Spitfire Errata #32 workaround */ |
| 444 | /* NOTE: Using plain zero for the context value is | 467 | /* NOTE: Using plain zero for the context value is |
| 445 | * correct here, we are not using the Linux trap | 468 | * correct here, we are not using the Linux trap |
| @@ -449,23 +472,13 @@ static void inherit_prom_mappings(void) | |||
| 449 | __asm__ __volatile__("stxa %0, [%1] %2\n\t" | 472 | __asm__ __volatile__("stxa %0, [%1] %2\n\t" |
| 450 | "flush %%g6" | 473 | "flush %%g6" |
| 451 | : /* No outputs */ | 474 | : /* No outputs */ |
| 452 | : "r" (0), "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU)); | 475 | : "r" (0), "r" (PRIMARY_CONTEXT), |
| 453 | 476 | "i" (ASI_DMMU)); | |
| 454 | switch (tlb_type) { | 477 | } |
| 455 | default: | ||
| 456 | case spitfire: | ||
| 457 | phys_page = spitfire_get_dtlb_data(sparc64_highest_locked_tlbent()); | ||
| 458 | break; | ||
| 459 | |||
| 460 | case cheetah: | ||
| 461 | case cheetah_plus: | ||
| 462 | phys_page = cheetah_get_litlb_data(sparc64_highest_locked_tlbent()); | ||
| 463 | break; | ||
| 464 | }; | ||
| 465 | 478 | ||
| 466 | phys_page &= _PAGE_PADDR; | 479 | static void lock_remap_func_page(unsigned long phys_page) |
| 467 | phys_page += ((unsigned long)&prom_boot_page - | 480 | { |
| 468 | (unsigned long)KERNBASE); | 481 | unsigned long tte_data = (phys_page | pgprot_val(PAGE_KERNEL)); |
| 469 | 482 | ||
| 470 | if (tlb_type == spitfire) { | 483 | if (tlb_type == spitfire) { |
| 471 | /* Lock this into i/d tlb entry 59 */ | 484 | /* Lock this into i/d tlb entry 59 */ |
| @@ -478,13 +491,12 @@ static void inherit_prom_mappings(void) | |||
| 478 | "stxa %0, [%1] %6\n\t" | 491 | "stxa %0, [%1] %6\n\t" |
| 479 | "membar #Sync\n\t" | 492 | "membar #Sync\n\t" |
| 480 | "flush %%g6" | 493 | "flush %%g6" |
| 481 | : : "r" (phys_page | _PAGE_VALID | _PAGE_SZ8K | _PAGE_CP | | 494 | : /* no outputs */ |
| 482 | _PAGE_CV | _PAGE_P | _PAGE_L | _PAGE_W), | 495 | : "r" (tte_data), "r" (59 << 3), "r" (TLB_TAG_ACCESS), |
| 483 | "r" (59 << 3), "r" (TLB_TAG_ACCESS), | 496 | "i" (ASI_DMMU), "i" (ASI_DTLB_DATA_ACCESS), |
| 484 | "i" (ASI_DMMU), "i" (ASI_DTLB_DATA_ACCESS), | 497 | "i" (ASI_IMMU), "i" (ASI_ITLB_DATA_ACCESS) |
| 485 | "i" (ASI_IMMU), "i" (ASI_ITLB_DATA_ACCESS) | 498 | : "memory"); |
| 486 | : "memory"); | 499 | } else { |
| 487 | } else if (tlb_type == cheetah || tlb_type == cheetah_plus) { | ||
| 488 | /* Lock this into i/d tlb-0 entry 11 */ | 500 | /* Lock this into i/d tlb-0 entry 11 */ |
| 489 | __asm__ __volatile__( | 501 | __asm__ __volatile__( |
| 490 | "stxa %%g0, [%2] %3\n\t" | 502 | "stxa %%g0, [%2] %3\n\t" |
| @@ -495,87 +507,80 @@ static void inherit_prom_mappings(void) | |||
| 495 | "stxa %0, [%1] %6\n\t" | 507 | "stxa %0, [%1] %6\n\t" |
| 496 | "membar #Sync\n\t" | 508 | "membar #Sync\n\t" |
| 497 | "flush %%g6" | 509 | "flush %%g6" |
| 498 | : : "r" (phys_page | _PAGE_VALID | _PAGE_SZ8K | _PAGE_CP | | 510 | : /* no outputs */ |
| 499 | _PAGE_CV | _PAGE_P | _PAGE_L | _PAGE_W), | 511 | : "r" (tte_data), "r" ((0 << 16) | (11 << 3)), |
| 500 | "r" ((0 << 16) | (11 << 3)), "r" (TLB_TAG_ACCESS), | 512 | "r" (TLB_TAG_ACCESS), "i" (ASI_DMMU), |
| 501 | "i" (ASI_DMMU), "i" (ASI_DTLB_DATA_ACCESS), | 513 | "i" (ASI_DTLB_DATA_ACCESS), "i" (ASI_IMMU), |
| 502 | "i" (ASI_IMMU), "i" (ASI_ITLB_DATA_ACCESS) | 514 | "i" (ASI_ITLB_DATA_ACCESS) |
| 503 | : "memory"); | 515 | : "memory"); |
| 504 | } else { | ||
| 505 | /* Implement me :-) */ | ||
| 506 | BUG(); | ||
| 507 | } | 516 | } |
| 517 | } | ||
| 518 | |||
| 519 | static void remap_kernel(void) | ||
| 520 | { | ||
| 521 | unsigned long phys_page, tte_vaddr, tte_data; | ||
| 522 | void (*remap_func)(unsigned long, unsigned long, int); | ||
| 523 | int tlb_ent = sparc64_highest_locked_tlbent(); | ||
| 524 | |||
| 525 | early_spitfire_errata32(); | ||
| 526 | |||
| 527 | if (tlb_type == spitfire) | ||
| 528 | phys_page = spitfire_get_dtlb_data(tlb_ent); | ||
| 529 | else | ||
| 530 | phys_page = cheetah_get_ldtlb_data(tlb_ent); | ||
| 531 | |||
| 532 | phys_page &= _PAGE_PADDR; | ||
| 533 | phys_page += ((unsigned long)&prom_boot_page - | ||
| 534 | (unsigned long)KERNBASE); | ||
| 535 | |||
| 536 | lock_remap_func_page(phys_page); | ||
| 508 | 537 | ||
| 509 | tte_vaddr = (unsigned long) KERNBASE; | 538 | tte_vaddr = (unsigned long) KERNBASE; |
| 510 | 539 | ||
| 511 | /* Spitfire Errata #32 workaround */ | 540 | early_spitfire_errata32(); |
| 512 | /* NOTE: Using plain zero for the context value is | ||
| 513 | * correct here, we are not using the Linux trap | ||
| 514 | * tables yet so we should not use the special | ||
| 515 | * UltraSPARC-III+ page size encodings yet. | ||
| 516 | */ | ||
| 517 | __asm__ __volatile__("stxa %0, [%1] %2\n\t" | ||
| 518 | "flush %%g6" | ||
| 519 | : /* No outputs */ | ||
| 520 | : "r" (0), | ||
| 521 | "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU)); | ||
| 522 | 541 | ||
| 523 | if (tlb_type == spitfire) | 542 | if (tlb_type == spitfire) |
| 524 | tte_data = spitfire_get_dtlb_data(sparc64_highest_locked_tlbent()); | 543 | tte_data = spitfire_get_dtlb_data(tlb_ent); |
| 525 | else | 544 | else |
| 526 | tte_data = cheetah_get_ldtlb_data(sparc64_highest_locked_tlbent()); | 545 | tte_data = cheetah_get_ldtlb_data(tlb_ent); |
| 527 | 546 | ||
| 528 | kern_locked_tte_data = tte_data; | 547 | kern_locked_tte_data = tte_data; |
| 529 | 548 | ||
| 530 | remap_func = (void *) ((unsigned long) &prom_remap - | 549 | remap_func = (void *) ((unsigned long) &prom_remap - |
| 531 | (unsigned long) &prom_boot_page); | 550 | (unsigned long) &prom_boot_page); |
| 532 | 551 | ||
| 552 | early_spitfire_errata32(); | ||
| 533 | 553 | ||
| 534 | /* Spitfire Errata #32 workaround */ | 554 | phys_page = tte_data & _PAGE_PADDR; |
| 535 | /* NOTE: Using plain zero for the context value is | 555 | remap_func(phys_page, KERNBASE, prom_get_mmu_ihandle()); |
| 536 | * correct here, we are not using the Linux trap | ||
| 537 | * tables yet so we should not use the special | ||
| 538 | * UltraSPARC-III+ page size encodings yet. | ||
| 539 | */ | ||
| 540 | __asm__ __volatile__("stxa %0, [%1] %2\n\t" | ||
| 541 | "flush %%g6" | ||
| 542 | : /* No outputs */ | ||
| 543 | : "r" (0), | ||
| 544 | "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU)); | ||
| 545 | |||
| 546 | remap_func((tlb_type == spitfire ? | ||
| 547 | (spitfire_get_dtlb_data(sparc64_highest_locked_tlbent()) & _PAGE_PADDR) : | ||
| 548 | (cheetah_get_litlb_data(sparc64_highest_locked_tlbent()) & _PAGE_PADDR)), | ||
| 549 | (unsigned long) KERNBASE, | ||
| 550 | prom_get_mmu_ihandle()); | ||
| 551 | |||
| 552 | if (bigkernel) | 556 | if (bigkernel) |
| 553 | remap_func(((tte_data + 0x400000) & _PAGE_PADDR), | 557 | remap_func(phys_page + 0x400000, |
| 554 | (unsigned long) KERNBASE + 0x400000, prom_get_mmu_ihandle()); | 558 | KERNBASE + 0x400000, |
| 559 | prom_get_mmu_ihandle()); | ||
| 555 | 560 | ||
| 556 | /* Flush out that temporary mapping. */ | 561 | /* Flush out that temporary mapping. */ |
| 557 | spitfire_flush_dtlb_nucleus_page(0x0); | 562 | spitfire_flush_dtlb_nucleus_page(0x0); |
| 558 | spitfire_flush_itlb_nucleus_page(0x0); | 563 | spitfire_flush_itlb_nucleus_page(0x0); |
| 559 | 564 | ||
| 560 | /* Now lock us back into the TLBs via OBP. */ | 565 | /* Now lock us back into the TLBs via OBP. */ |
| 561 | prom_dtlb_load(sparc64_highest_locked_tlbent(), tte_data, tte_vaddr); | 566 | prom_dtlb_load(tlb_ent, tte_data, tte_vaddr); |
| 562 | prom_itlb_load(sparc64_highest_locked_tlbent(), tte_data, tte_vaddr); | 567 | prom_itlb_load(tlb_ent, tte_data, tte_vaddr); |
| 563 | if (bigkernel) { | 568 | if (bigkernel) { |
| 564 | prom_dtlb_load(sparc64_highest_locked_tlbent()-1, tte_data + 0x400000, | 569 | prom_dtlb_load(tlb_ent - 1, |
| 565 | tte_vaddr + 0x400000); | 570 | tte_data + 0x400000, |
| 566 | prom_itlb_load(sparc64_highest_locked_tlbent()-1, tte_data + 0x400000, | 571 | tte_vaddr + 0x400000); |
| 567 | tte_vaddr + 0x400000); | 572 | prom_itlb_load(tlb_ent - 1, |
| 573 | tte_data + 0x400000, | ||
| 574 | tte_vaddr + 0x400000); | ||
| 568 | } | 575 | } |
| 576 | } | ||
| 569 | 577 | ||
| 570 | /* Re-read translations property. */ | 578 | static void readjust_prom_translations(void) |
| 571 | if ((n = prom_getproperty(node, "translations", | 579 | { |
| 572 | (char *)&prom_trans[0], tsz)) == -1) { | 580 | int nents, i; |
| 573 | prom_printf("prom_mappings: Can't reread prom_trans.\n"); | ||
| 574 | prom_halt(); | ||
| 575 | } | ||
| 576 | n = n / sizeof(struct linux_prom_translation); | ||
| 577 | 581 | ||
| 578 | for (i = 0; i < n; i++) { | 582 | nents = read_obp_translations(); |
| 583 | for (i = 0; i < nents; i++) { | ||
| 579 | unsigned long vaddr = prom_trans[i].virt; | 584 | unsigned long vaddr = prom_trans[i].virt; |
| 580 | unsigned long size = prom_trans[i].size; | 585 | unsigned long size = prom_trans[i].size; |
| 581 | 586 | ||
| @@ -601,6 +606,20 @@ static void inherit_prom_mappings(void) | |||
| 601 | } | 606 | } |
| 602 | } | 607 | } |
| 603 | } | 608 | } |
| 609 | } | ||
| 610 | |||
| 611 | static void inherit_prom_mappings(void) | ||
| 612 | { | ||
| 613 | int n; | ||
| 614 | |||
| 615 | n = read_obp_translations(); | ||
| 616 | build_obp_pgtable(n); | ||
| 617 | |||
| 618 | /* Now fixup OBP's idea about where we really are mapped. */ | ||
| 619 | prom_printf("Remapping the kernel... "); | ||
| 620 | remap_kernel(); | ||
| 621 | |||
| 622 | readjust_prom_translations(); | ||
| 604 | 623 | ||
| 605 | prom_printf("done.\n"); | 624 | prom_printf("done.\n"); |
| 606 | 625 | ||
