diff options
author | Russell King <rmk@dyn-67.arm.linux.org.uk> | 2007-04-21 05:16:48 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2007-04-21 15:35:52 -0400 |
commit | 4a56c1e41f19393577bdd5c774c289c199b7269d (patch) | |
tree | a2c36198e3b4d78ccc8e373c3748112bc0645b0e /arch/arm/mm/mmu.c | |
parent | d5c98176ef34b8b78645646593c17e10f62f53ff (diff) |
[ARM] mm 3: separate out supersection mappings, avoid for <4GB
Catalin Marinas at ARM Ltd says:
> The CPU architects in ARM intended supersections only as a way to map
> addresses >= 4GB. Supersections are not mandated by the architecture
> and there is no easy way to detect their hardware support at run-time
> (other than checking for a specific core). From the analysis done in
> ARM, there wasn't a clear performance gain by using supersections
> rather than sections (no significant improvement in the TLB misses).
Therefore, we should avoid using supersections unless there's a real
need (iow, we're mapping addresses >= 4GB).
This means that we can simplify create_mapping() a bit since we will
only use supersection mappings for addresses >= 4GB, which means that
the physical, virtual and length must be multiples of the supersection
mapping size.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/mm/mmu.c')
-rw-r--r-- | arch/arm/mm/mmu.c | 130 |
1 files changed, 61 insertions, 69 deletions
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index e359f3685433..32139800d939 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c | |||
@@ -398,21 +398,6 @@ alloc_init_section(unsigned long virt, unsigned long phys, int prot) | |||
398 | } | 398 | } |
399 | 399 | ||
400 | /* | 400 | /* |
401 | * Create a SUPER SECTION PGD between VIRT and PHYS with protection PROT | ||
402 | */ | ||
403 | static inline void | ||
404 | alloc_init_supersection(unsigned long virt, unsigned long phys, int prot) | ||
405 | { | ||
406 | int i; | ||
407 | |||
408 | for (i = 0; i < 16; i += 1) { | ||
409 | alloc_init_section(virt, phys, prot | PMD_SECT_SUPER); | ||
410 | |||
411 | virt += (PGDIR_SIZE / 2); | ||
412 | } | ||
413 | } | ||
414 | |||
415 | /* | ||
416 | * Add a PAGE mapping between VIRT and PHYS in domain | 401 | * Add a PAGE mapping between VIRT and PHYS in domain |
417 | * DOMAIN with protection PROT. Note that due to the | 402 | * DOMAIN with protection PROT. Note that due to the |
418 | * way we map the PTEs, we must allocate two PTE_SIZE'd | 403 | * way we map the PTEs, we must allocate two PTE_SIZE'd |
@@ -436,6 +421,64 @@ alloc_init_page(unsigned long virt, unsigned long phys, const struct mem_type *t | |||
436 | set_pte_ext(ptep, pfn_pte(phys >> PAGE_SHIFT, __pgprot(type->prot_pte)), 0); | 421 | set_pte_ext(ptep, pfn_pte(phys >> PAGE_SHIFT, __pgprot(type->prot_pte)), 0); |
437 | } | 422 | } |
438 | 423 | ||
424 | static void __init create_36bit_mapping(struct map_desc *md, | ||
425 | const struct mem_type *type) | ||
426 | { | ||
427 | unsigned long phys, addr, length, end; | ||
428 | pgd_t *pgd; | ||
429 | |||
430 | addr = md->virtual; | ||
431 | phys = (unsigned long)__pfn_to_phys(md->pfn); | ||
432 | length = PAGE_ALIGN(md->length); | ||
433 | |||
434 | if (!(cpu_architecture() >= CPU_ARCH_ARMv6 || cpu_is_xsc3())) { | ||
435 | printk(KERN_ERR "MM: CPU does not support supersection " | ||
436 | "mapping for 0x%08llx at 0x%08lx\n", | ||
437 | __pfn_to_phys((u64)md->pfn), addr); | ||
438 | return; | ||
439 | } | ||
440 | |||
441 | /* N.B. ARMv6 supersections are only defined to work with domain 0. | ||
442 | * Since domain assignments can in fact be arbitrary, the | ||
443 | * 'domain == 0' check below is required to insure that ARMv6 | ||
444 | * supersections are only allocated for domain 0 regardless | ||
445 | * of the actual domain assignments in use. | ||
446 | */ | ||
447 | if (type->domain) { | ||
448 | printk(KERN_ERR "MM: invalid domain in supersection " | ||
449 | "mapping for 0x%08llx at 0x%08lx\n", | ||
450 | __pfn_to_phys((u64)md->pfn), addr); | ||
451 | return; | ||
452 | } | ||
453 | |||
454 | if ((addr | length | __pfn_to_phys(md->pfn)) & ~SUPERSECTION_MASK) { | ||
455 | printk(KERN_ERR "MM: cannot create mapping for " | ||
456 | "0x%08llx at 0x%08lx invalid alignment\n", | ||
457 | __pfn_to_phys((u64)md->pfn), addr); | ||
458 | return; | ||
459 | } | ||
460 | |||
461 | /* | ||
462 | * Shift bits [35:32] of address into bits [23:20] of PMD | ||
463 | * (See ARMv6 spec). | ||
464 | */ | ||
465 | phys |= (((md->pfn >> (32 - PAGE_SHIFT)) & 0xF) << 20); | ||
466 | |||
467 | pgd = pgd_offset_k(addr); | ||
468 | end = addr + length; | ||
469 | do { | ||
470 | pmd_t *pmd = pmd_offset(pgd, addr); | ||
471 | int i; | ||
472 | |||
473 | for (i = 0; i < 16; i++) | ||
474 | *pmd++ = __pmd(phys | type->prot_sect | PMD_SECT_SUPER); | ||
475 | |||
476 | addr += SUPERSECTION_SIZE; | ||
477 | phys += SUPERSECTION_SIZE; | ||
478 | pgd += SUPERSECTION_SIZE >> PGDIR_SHIFT; | ||
479 | } while (addr != end); | ||
480 | } | ||
481 | |||
439 | /* | 482 | /* |
440 | * Create the page directory entries and any necessary | 483 | * Create the page directory entries and any necessary |
441 | * page tables for the mapping specified by `md'. We | 484 | * page tables for the mapping specified by `md'. We |
@@ -468,26 +511,9 @@ void __init create_mapping(struct map_desc *md) | |||
468 | /* | 511 | /* |
469 | * Catch 36-bit addresses | 512 | * Catch 36-bit addresses |
470 | */ | 513 | */ |
471 | if(md->pfn >= 0x100000) { | 514 | if (md->pfn >= 0x100000) { |
472 | if (type->domain) { | 515 | create_36bit_mapping(md, type); |
473 | printk(KERN_ERR "MM: invalid domain in supersection " | 516 | return; |
474 | "mapping for 0x%08llx at 0x%08lx\n", | ||
475 | __pfn_to_phys((u64)md->pfn), md->virtual); | ||
476 | return; | ||
477 | } | ||
478 | if((md->virtual | md->length | __pfn_to_phys(md->pfn)) | ||
479 | & ~SUPERSECTION_MASK) { | ||
480 | printk(KERN_ERR "MM: cannot create mapping for " | ||
481 | "0x%08llx at 0x%08lx invalid alignment\n", | ||
482 | __pfn_to_phys((u64)md->pfn), md->virtual); | ||
483 | return; | ||
484 | } | ||
485 | |||
486 | /* | ||
487 | * Shift bits [35:32] of address into bits [23:20] of PMD | ||
488 | * (See ARMv6 spec). | ||
489 | */ | ||
490 | off |= (((md->pfn >> (32 - PAGE_SHIFT)) & 0xF) << 20); | ||
491 | } | 517 | } |
492 | 518 | ||
493 | virt = md->virtual; | 519 | virt = md->virtual; |
@@ -509,40 +535,6 @@ void __init create_mapping(struct map_desc *md) | |||
509 | length -= PAGE_SIZE; | 535 | length -= PAGE_SIZE; |
510 | } | 536 | } |
511 | 537 | ||
512 | /* N.B. ARMv6 supersections are only defined to work with domain 0. | ||
513 | * Since domain assignments can in fact be arbitrary, the | ||
514 | * 'domain == 0' check below is required to insure that ARMv6 | ||
515 | * supersections are only allocated for domain 0 regardless | ||
516 | * of the actual domain assignments in use. | ||
517 | */ | ||
518 | if ((cpu_architecture() >= CPU_ARCH_ARMv6 || cpu_is_xsc3()) | ||
519 | && type->domain == 0) { | ||
520 | /* | ||
521 | * Align to supersection boundary if !high pages. | ||
522 | * High pages have already been checked for proper | ||
523 | * alignment above and they will fail the SUPSERSECTION_MASK | ||
524 | * check because of the way the address is encoded into | ||
525 | * offset. | ||
526 | */ | ||
527 | if (md->pfn <= 0x100000) { | ||
528 | while ((virt & ~SUPERSECTION_MASK || | ||
529 | (virt + off) & ~SUPERSECTION_MASK) && | ||
530 | length >= (PGDIR_SIZE / 2)) { | ||
531 | alloc_init_section(virt, virt + off, type->prot_sect); | ||
532 | |||
533 | virt += (PGDIR_SIZE / 2); | ||
534 | length -= (PGDIR_SIZE / 2); | ||
535 | } | ||
536 | } | ||
537 | |||
538 | while (length >= SUPERSECTION_SIZE) { | ||
539 | alloc_init_supersection(virt, virt + off, type->prot_sect); | ||
540 | |||
541 | virt += SUPERSECTION_SIZE; | ||
542 | length -= SUPERSECTION_SIZE; | ||
543 | } | ||
544 | } | ||
545 | |||
546 | /* | 538 | /* |
547 | * A section mapping covers half a "pgdir" entry. | 539 | * A section mapping covers half a "pgdir" entry. |
548 | */ | 540 | */ |