diff options
author | Paul Mackerras <paulus@samba.org> | 2007-04-03 07:24:02 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2007-04-12 13:55:18 -0400 |
commit | 721151d004dcf01a71b12bb6b893f9160284cf6e (patch) | |
tree | 16105646cae11ad6f785a5756d526b01922bcd7c | |
parent | 1a38147ed0737a9c01dbf5f2ca47fd2a0aa5cb55 (diff) |
[POWERPC] Allow drivers to map individual 4k pages to userspace
Some drivers have resources that they want to be able to map into
userspace that are 4k in size. On a kernel configured with 64k pages
we currently end up mapping the 4k we want plus another 60k of
physical address space, which could contain anything. This can
introduce security problems, for example in the case of an infiniband
adaptor where the other 60k could contain registers that some other
program is using for its communications.
This patch adds a new function, remap_4k_pfn, which drivers can use to
map a single 4k page to userspace regardless of whether the kernel is
using a 4k or a 64k page size. Like remap_pfn_range, it would
typically be called in a driver's mmap function. It only maps a
single 4k page, which on a 64k page kernel appears replicated 16 times
throughout a 64k page. On a 4k page kernel it reduces to a call to
remap_pfn_range.
The way this works on a 64k kernel is that a new bit, _PAGE_4K_PFN,
gets set on the linux PTE. This alters the way that __hash_page_4K
computes the real address to put in the HPTE. The RPN field of the
linux PTE becomes the 4k RPN directly rather than being interpreted as
a 64k RPN. Since the RPN field is 32 bits, this means that physical
addresses being mapped with remap_4k_pfn have to be below 2^44,
i.e. 0x100000000000.
The patch also factors out the code in arch/powerpc/mm/hash_utils_64.c
that deals with demoting a process to use 4k pages into one function
that gets called in the various different places where we need to do
that. There were some discrepancies between exactly what was done in
the various places, such as a call to spu_flush_all_slbs in one case
but not in others.
Signed-off-by: Paul Mackerras <paulus@samba.org>
-rw-r--r-- | arch/powerpc/mm/hash_low_64.S | 5 | ||||
-rw-r--r-- | arch/powerpc/mm/hash_utils_64.c | 43 | ||||
-rw-r--r-- | include/asm-powerpc/pgtable-4k.h | 3 | ||||
-rw-r--r-- | include/asm-powerpc/pgtable-64k.h | 5 |
4 files changed, 42 insertions, 14 deletions
diff --git a/arch/powerpc/mm/hash_low_64.S b/arch/powerpc/mm/hash_low_64.S index 9bc0a9c2b9bc..e64ce3eec36e 100644 --- a/arch/powerpc/mm/hash_low_64.S +++ b/arch/powerpc/mm/hash_low_64.S | |||
@@ -445,9 +445,12 @@ END_FTR_SECTION(CPU_FTR_NOEXECUTE|CPU_FTR_COHERENT_ICACHE, CPU_FTR_NOEXECUTE) | |||
445 | 445 | ||
446 | htab_insert_pte: | 446 | htab_insert_pte: |
447 | /* real page number in r5, PTE RPN value + index */ | 447 | /* real page number in r5, PTE RPN value + index */ |
448 | rldicl r5,r31,64-PTE_RPN_SHIFT,PTE_RPN_SHIFT | 448 | andis. r0,r31,_PAGE_4K_PFN@h |
449 | srdi r5,r31,PTE_RPN_SHIFT | ||
450 | bne- htab_special_pfn | ||
449 | sldi r5,r5,PAGE_SHIFT-HW_PAGE_SHIFT | 451 | sldi r5,r5,PAGE_SHIFT-HW_PAGE_SHIFT |
450 | add r5,r5,r25 | 452 | add r5,r5,r25 |
453 | htab_special_pfn: | ||
451 | sldi r5,r5,HW_PAGE_SHIFT | 454 | sldi r5,r5,HW_PAGE_SHIFT |
452 | 455 | ||
453 | /* Calculate primary group hash */ | 456 | /* Calculate primary group hash */ |
diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c index 3c7fe2c65b5a..aae085317018 100644 --- a/arch/powerpc/mm/hash_utils_64.c +++ b/arch/powerpc/mm/hash_utils_64.c | |||
@@ -573,6 +573,27 @@ unsigned int hash_page_do_lazy_icache(unsigned int pp, pte_t pte, int trap) | |||
573 | return pp; | 573 | return pp; |
574 | } | 574 | } |
575 | 575 | ||
576 | /* | ||
577 | * Demote a segment to using 4k pages. | ||
578 | * For now this makes the whole process use 4k pages. | ||
579 | */ | ||
580 | void demote_segment_4k(struct mm_struct *mm, unsigned long addr) | ||
581 | { | ||
582 | #ifdef CONFIG_PPC_64K_PAGES | ||
583 | if (mm->context.user_psize == MMU_PAGE_4K) | ||
584 | return; | ||
585 | mm->context.user_psize = MMU_PAGE_4K; | ||
586 | mm->context.sllp = SLB_VSID_USER | mmu_psize_defs[MMU_PAGE_4K].sllp; | ||
587 | get_paca()->context = mm->context; | ||
588 | slb_flush_and_rebolt(); | ||
589 | #ifdef CONFIG_SPE_BASE | ||
590 | spu_flush_all_slbs(mm); | ||
591 | #endif | ||
592 | #endif | ||
593 | } | ||
594 | |||
595 | EXPORT_SYMBOL_GPL(demote_segment_4k); | ||
596 | |||
576 | /* Result code is: | 597 | /* Result code is: |
577 | * 0 - handled | 598 | * 0 - handled |
578 | * 1 - normal page fault | 599 | * 1 - normal page fault |
@@ -665,15 +686,19 @@ int hash_page(unsigned long ea, unsigned long access, unsigned long trap) | |||
665 | #ifndef CONFIG_PPC_64K_PAGES | 686 | #ifndef CONFIG_PPC_64K_PAGES |
666 | rc = __hash_page_4K(ea, access, vsid, ptep, trap, local); | 687 | rc = __hash_page_4K(ea, access, vsid, ptep, trap, local); |
667 | #else | 688 | #else |
689 | /* If _PAGE_4K_PFN is set, make sure this is a 4k segment */ | ||
690 | if (pte_val(*ptep) & _PAGE_4K_PFN) { | ||
691 | demote_segment_4k(mm, ea); | ||
692 | psize = MMU_PAGE_4K; | ||
693 | } | ||
694 | |||
668 | if (mmu_ci_restrictions) { | 695 | if (mmu_ci_restrictions) { |
669 | /* If this PTE is non-cacheable, switch to 4k */ | 696 | /* If this PTE is non-cacheable, switch to 4k */ |
670 | if (psize == MMU_PAGE_64K && | 697 | if (psize == MMU_PAGE_64K && |
671 | (pte_val(*ptep) & _PAGE_NO_CACHE)) { | 698 | (pte_val(*ptep) & _PAGE_NO_CACHE)) { |
672 | if (user_region) { | 699 | if (user_region) { |
700 | demote_segment_4k(mm, ea); | ||
673 | psize = MMU_PAGE_4K; | 701 | psize = MMU_PAGE_4K; |
674 | mm->context.user_psize = MMU_PAGE_4K; | ||
675 | mm->context.sllp = SLB_VSID_USER | | ||
676 | mmu_psize_defs[MMU_PAGE_4K].sllp; | ||
677 | } else if (ea < VMALLOC_END) { | 702 | } else if (ea < VMALLOC_END) { |
678 | /* | 703 | /* |
679 | * some driver did a non-cacheable mapping | 704 | * some driver did a non-cacheable mapping |
@@ -756,16 +781,8 @@ void hash_preload(struct mm_struct *mm, unsigned long ea, | |||
756 | if (mmu_ci_restrictions) { | 781 | if (mmu_ci_restrictions) { |
757 | /* If this PTE is non-cacheable, switch to 4k */ | 782 | /* If this PTE is non-cacheable, switch to 4k */ |
758 | if (mm->context.user_psize == MMU_PAGE_64K && | 783 | if (mm->context.user_psize == MMU_PAGE_64K && |
759 | (pte_val(*ptep) & _PAGE_NO_CACHE)) { | 784 | (pte_val(*ptep) & _PAGE_NO_CACHE)) |
760 | mm->context.user_psize = MMU_PAGE_4K; | 785 | demote_segment_4k(mm, ea); |
761 | mm->context.sllp = SLB_VSID_USER | | ||
762 | mmu_psize_defs[MMU_PAGE_4K].sllp; | ||
763 | get_paca()->context = mm->context; | ||
764 | slb_flush_and_rebolt(); | ||
765 | #ifdef CONFIG_SPE_BASE | ||
766 | spu_flush_all_slbs(mm); | ||
767 | #endif | ||
768 | } | ||
769 | } | 786 | } |
770 | if (mm->context.user_psize == MMU_PAGE_64K) | 787 | if (mm->context.user_psize == MMU_PAGE_64K) |
771 | __hash_page_64K(ea, access, vsid, ptep, trap, local); | 788 | __hash_page_64K(ea, access, vsid, ptep, trap, local); |
diff --git a/include/asm-powerpc/pgtable-4k.h b/include/asm-powerpc/pgtable-4k.h index 345d9b07b3e2..a28fa8bc01da 100644 --- a/include/asm-powerpc/pgtable-4k.h +++ b/include/asm-powerpc/pgtable-4k.h | |||
@@ -97,3 +97,6 @@ | |||
97 | 97 | ||
98 | #define pud_ERROR(e) \ | 98 | #define pud_ERROR(e) \ |
99 | printk("%s:%d: bad pud %08lx.\n", __FILE__, __LINE__, pud_val(e)) | 99 | printk("%s:%d: bad pud %08lx.\n", __FILE__, __LINE__, pud_val(e)) |
100 | |||
101 | #define remap_4k_pfn(vma, addr, pfn, prot) \ | ||
102 | remap_pfn_range((vma), (addr), (pfn), PAGE_SIZE, (prot)) | ||
diff --git a/include/asm-powerpc/pgtable-64k.h b/include/asm-powerpc/pgtable-64k.h index 4b7126c53f37..5e84f070eaf7 100644 --- a/include/asm-powerpc/pgtable-64k.h +++ b/include/asm-powerpc/pgtable-64k.h | |||
@@ -35,6 +35,7 @@ | |||
35 | #define _PAGE_HPTE_SUB 0x0ffff000 /* combo only: sub pages HPTE bits */ | 35 | #define _PAGE_HPTE_SUB 0x0ffff000 /* combo only: sub pages HPTE bits */ |
36 | #define _PAGE_HPTE_SUB0 0x08000000 /* combo only: first sub page */ | 36 | #define _PAGE_HPTE_SUB0 0x08000000 /* combo only: first sub page */ |
37 | #define _PAGE_COMBO 0x10000000 /* this is a combo 4k page */ | 37 | #define _PAGE_COMBO 0x10000000 /* this is a combo 4k page */ |
38 | #define _PAGE_4K_PFN 0x20000000 /* PFN is for a single 4k page */ | ||
38 | #define _PAGE_F_SECOND 0x00008000 /* full page: hidx bits */ | 39 | #define _PAGE_F_SECOND 0x00008000 /* full page: hidx bits */ |
39 | #define _PAGE_F_GIX 0x00007000 /* full page: hidx bits */ | 40 | #define _PAGE_F_GIX 0x00007000 /* full page: hidx bits */ |
40 | 41 | ||
@@ -93,6 +94,10 @@ | |||
93 | #define pte_pagesize_index(pte) \ | 94 | #define pte_pagesize_index(pte) \ |
94 | (((pte) & _PAGE_COMBO)? MMU_PAGE_4K: MMU_PAGE_64K) | 95 | (((pte) & _PAGE_COMBO)? MMU_PAGE_4K: MMU_PAGE_64K) |
95 | 96 | ||
97 | #define remap_4k_pfn(vma, addr, pfn, prot) \ | ||
98 | remap_pfn_range((vma), (addr), (pfn), PAGE_SIZE, \ | ||
99 | __pgprot(pgprot_val((prot)) | _PAGE_4K_PFN)) | ||
100 | |||
96 | #endif /* __ASSEMBLY__ */ | 101 | #endif /* __ASSEMBLY__ */ |
97 | #endif /* __KERNEL__ */ | 102 | #endif /* __KERNEL__ */ |
98 | #endif /* _ASM_POWERPC_PGTABLE_64K_H */ | 103 | #endif /* _ASM_POWERPC_PGTABLE_64K_H */ |