aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/mm
diff options
context:
space:
mode:
authorPaul Mackerras <paulus@samba.org>2007-04-03 07:24:02 -0400
committerPaul Mackerras <paulus@samba.org>2007-04-12 13:55:18 -0400
commit721151d004dcf01a71b12bb6b893f9160284cf6e (patch)
tree16105646cae11ad6f785a5756d526b01922bcd7c /arch/powerpc/mm
parent1a38147ed0737a9c01dbf5f2ca47fd2a0aa5cb55 (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>
Diffstat (limited to 'arch/powerpc/mm')
-rw-r--r--arch/powerpc/mm/hash_low_64.S5
-rw-r--r--arch/powerpc/mm/hash_utils_64.c43
2 files changed, 34 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
446htab_insert_pte: 446htab_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
453htab_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 */
580void 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
595EXPORT_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);