[PATCH 1/2][RFC] PPC64 no-exec support for user space

Jake Moilanen moilanen at austin.ibm.com
Wed Oct 13 00:56:19 EST 2004


Here is no-exec support for user space.  This patch also includes base
no-exec support. 

Once again it requires Ben's signal trampoline in vdso piece.  

Thanks,
Jake

Signed-off-by: Jake Moilanen <moilanen at austin.ibm.com>

---


diff -puN arch/ppc64/kernel/head.S~nx-user-ppc64 arch/ppc64/kernel/head.S
--- linux-2.6-bk/arch/ppc64/kernel/head.S~nx-user-ppc64	Thu Oct  7 15:23:52 2004
+++ linux-2.6-bk-moilanen/arch/ppc64/kernel/head.S	Thu Oct  7 15:23:52 2004
@@ -35,6 +35,7 @@
 #include <asm/offsets.h>
 #include <asm/bug.h>
 #include <asm/cputable.h>
+#include <asm/pgtable.h>	
 #include <asm/setup.h>
 
 #ifdef CONFIG_PPC_ISERIES
@@ -879,6 +880,7 @@ InstructionAccess_common:
 	ld	r3,_NIP(r1)
 	andis.	r4,r12,0x5820
 	li	r5,0x400
+	ori	r4,r4,_PAGE_EXEC	
 	b	.do_hash_page		/* Try to handle as hpte fault */
 
 	.align	7
@@ -964,11 +966,10 @@ END_FTR_SECTION_IFCLR(CPU_FTR_SLB)
 	 * accessing a userspace segment (even from the kernel). We assume
 	 * kernel addresses always have the high bit set.
 	 */
-	rlwinm	r4,r4,32-23,29,29	/* DSISR_STORE -> _PAGE_RW */
+	rlwinm	r4,r4,32-25+9,31-9,31-9	/* DSISR_STORE -> _PAGE_RW */
 	rotldi	r0,r3,15		/* Move high bit into MSR_PR posn */
 	orc	r0,r12,r0		/* MSR_PR | ~high_bit */
 	rlwimi	r4,r0,32-13,30,30	/* becomes _PAGE_USER access bit */
-	ori	r4,r4,1			/* add _PAGE_PRESENT */
 
 	/*
 	 * On iSeries, we soft-disable interrupts here, then
diff -puN arch/ppc64/mm/fault.c~nx-user-ppc64 arch/ppc64/mm/fault.c
--- linux-2.6-bk/arch/ppc64/mm/fault.c~nx-user-ppc64	Thu Oct  7 15:23:52 2004
+++ linux-2.6-bk-moilanen/arch/ppc64/mm/fault.c	Thu Oct  7 15:23:52 2004
@@ -92,6 +92,7 @@ int do_page_fault(struct pt_regs *regs, 
 	unsigned long code = SEGV_MAPERR;
 	unsigned long is_write = error_code & 0x02000000;
 	unsigned long trap = TRAP(regs);
+ 	unsigned long is_exec = trap == 0x400;	
 
 	BUG_ON((trap == 0x380) || (trap == 0x480));
 
@@ -191,16 +192,19 @@ int do_page_fault(struct pt_regs *regs, 
 good_area:
 	code = SEGV_ACCERR;
 
+	if (is_exec) {
+		/* protection fault */
+		if (error_code & 0x08000000)
+			goto bad_area;
+		if (!(vma->vm_flags & VM_EXEC)) 
+			goto bad_area;
 	/* a write */
-	if (is_write) {
+	} else if (is_write) {
 		if (!(vma->vm_flags & VM_WRITE))
 			goto bad_area;
 	/* a read */
 	} else {
-		/* protection fault */
-		if (error_code & 0x08000000)
-			goto bad_area;
-		if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
+		if (!(vma->vm_flags & VM_READ))
 			goto bad_area;
 	}
 
diff -puN arch/ppc64/mm/hash_low.S~nx-user-ppc64 arch/ppc64/mm/hash_low.S
--- linux-2.6-bk/arch/ppc64/mm/hash_low.S~nx-user-ppc64	Thu Oct  7 15:23:52 2004
+++ linux-2.6-bk-moilanen/arch/ppc64/mm/hash_low.S	Thu Oct  7 15:23:52 2004
@@ -89,7 +89,7 @@ _GLOBAL(__hash_page)
 	/* Prepare new PTE value (turn access RW into DIRTY, then
 	 * add BUSY,HASHPTE and ACCESSED)
 	 */
-	rlwinm	r30,r4,5,24,24	/* _PAGE_RW -> _PAGE_DIRTY */
+	rlwinm	r30,r4,32-9+7,31-7,31-7	/* _PAGE_RW -> _PAGE_DIRTY */
 	or	r30,r30,r31
 	ori	r30,r30,_PAGE_BUSY | _PAGE_ACCESSED | _PAGE_HASHPTE
 	/* Write the linux PTE atomically (setting busy) */
@@ -112,11 +112,11 @@ _GLOBAL(__hash_page)
 	rldicl	r5,r5,0,25		/* vsid & 0x0000007fffffffff */
 	rldicl	r0,r3,64-12,48		/* (ea >> 12) & 0xffff */
 	xor	r28,r5,r0
-	
-	/* Convert linux PTE bits into HW equivalents
-	 */
-	andi.	r3,r30,0x1fa		/* Get basic set of flags */
-	rlwinm	r0,r30,32-2+1,30,30	/* _PAGE_RW -> _PAGE_USER (r0) */
+
+	/* Convert linux PTE bits into HW equivalents */
+	andi.	r3,r30,0x1fe		/* Get basic set of flags */
+	xori	r3,r3,HW_NO_EXEC	/* _PAGE_EXEC -> NOEXEC */
+	rlwinm	r0,r30,32-9+1,30,30	/* _PAGE_RW -> _PAGE_USER (r0) */
 	rlwinm	r4,r30,32-7+1,30,30	/* _PAGE_DIRTY -> _PAGE_USER (r4) */
 	and	r0,r0,r4		/* _PAGE_RW & _PAGE_DIRTY -> r0 bit 30 */
 	andc	r0,r30,r0		/* r0 = pte & ~r0 */
diff -puN fs/binfmt_elf.c~nx-user-ppc64 fs/binfmt_elf.c
--- linux-2.6-bk/fs/binfmt_elf.c~nx-user-ppc64	Thu Oct  7 15:23:52 2004
+++ linux-2.6-bk-moilanen/fs/binfmt_elf.c	Thu Oct  7 15:23:52 2004
@@ -89,8 +89,11 @@ static int set_brk(unsigned long start, 
 	end = ELF_PAGEALIGN(end);
 	if (end > start) {
 		unsigned long addr = do_brk(start, end - start);
+
 		if (BAD_ADDR(addr))
 			return addr;
+
+ 		sys_mprotect(start, end-start, PROT_READ|PROT_WRITE|PROT_EXEC);
 	}
 	current->mm->start_brk = current->mm->brk = end;
 	return 0;
diff -puN include/asm-ppc64/elf.h~nx-user-ppc64 include/asm-ppc64/elf.h
--- linux-2.6-bk/include/asm-ppc64/elf.h~nx-user-ppc64	Thu Oct  7 15:23:52 2004
+++ linux-2.6-bk-moilanen/include/asm-ppc64/elf.h	Thu Oct  7 15:23:52 2004
@@ -226,6 +226,13 @@ do {								\
 	else if (current->personality != PER_LINUX32)		\
 		set_personality(PER_LINUX);			\
 } while (0)
+
+/*
+ * An executable for which elf_read_implies_exec() returns TRUE will
+ * have the READ_IMPLIES_EXEC personality flag set automatically.
+ */
+#define elf_read_implies_exec_binary(ex, have_pt_gnu_stack)	(!(have_pt_gnu_stack))
+
 #endif
 
 /*
diff -puN include/asm-ppc64/page.h~nx-user-ppc64 include/asm-ppc64/page.h
--- linux-2.6-bk/include/asm-ppc64/page.h~nx-user-ppc64	Thu Oct  7 15:23:52 2004
+++ linux-2.6-bk-moilanen/include/asm-ppc64/page.h	Thu Oct  7 15:23:52 2004
@@ -233,8 +233,25 @@ extern int page_is_ram(unsigned long pfn
 
 #define virt_addr_valid(kaddr)	pfn_valid(__pa(kaddr) >> PAGE_SHIFT)
 
-#define VM_DATA_DEFAULT_FLAGS	(VM_READ | VM_WRITE | VM_EXEC | \
+#define VM_DATA_DEFAULT_FLAGS32	(VM_READ | VM_WRITE | \
 				 VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
+
+#define VM_STACK_DEFAULT_FLAGS32 (VM_READ | VM_WRITE | VM_EXEC | \
+				 VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
+
+#define VM_DATA_DEFAULT_FLAGS64	(VM_READ | VM_WRITE | \
+				 VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
+
+#define VM_STACK_DEFAULT_FLAGS64 (VM_READ | VM_WRITE | VM_EXEC | \
+				 VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
+
+#define VM_DATA_DEFAULT_FLAGS \
+	(test_thread_flag(TIF_32BIT) ? \
+	 VM_DATA_DEFAULT_FLAGS32 : VM_DATA_DEFAULT_FLAGS64)
+
+#define VM_STACK_DEFAULT_FLAGS \
+	(test_thread_flag(TIF_32BIT) ? \
+	 VM_STACK_DEFAULT_FLAGS32 : VM_STACK_DEFAULT_FLAGS64)
 
 #endif /* __KERNEL__ */
 #endif /* _PPC64_PAGE_H */
diff -puN include/asm-ppc64/pgtable.h~nx-user-ppc64 include/asm-ppc64/pgtable.h
--- linux-2.6-bk/include/asm-ppc64/pgtable.h~nx-user-ppc64	Thu Oct  7 15:23:52 2004
+++ linux-2.6-bk-moilanen/include/asm-ppc64/pgtable.h	Thu Oct  7 15:23:52 2004
@@ -86,24 +86,25 @@
 #define _PAGE_PRESENT	0x0001 /* software: pte contains a translation */
 #define _PAGE_USER	0x0002 /* matches one of the PP bits */
 #define _PAGE_FILE	0x0002 /* (!present only) software: pte holds file offset */
-#define _PAGE_RW	0x0004 /* software: user write access allowed */
+#define _PAGE_EXEC	0x0004 /* No execute on POWER4 and newer (we invert) */
 #define _PAGE_GUARDED	0x0008
 #define _PAGE_COHERENT	0x0010 /* M: enforce memory coherence (SMP systems) */
 #define _PAGE_NO_CACHE	0x0020 /* I: cache inhibit */
 #define _PAGE_WRITETHRU	0x0040 /* W: cache write-through */
 #define _PAGE_DIRTY	0x0080 /* C: page changed */
 #define _PAGE_ACCESSED	0x0100 /* R: page referenced */
-#define _PAGE_EXEC	0x0200 /* software: i-cache coherence required */
+#define _PAGE_RW	0x0200 /* software: user write access allowed */
 #define _PAGE_HASHPTE	0x0400 /* software: pte has an associated HPTE */
 #define _PAGE_BUSY	0x0800 /* software: PTE & hash are busy */ 
 #define _PAGE_SECONDARY 0x8000 /* software: HPTE is in secondary group */
 #define _PAGE_GROUP_IX  0x7000 /* software: HPTE index within group */
 /* Bits 0x7000 identify the index within an HPT Group */
 #define _PAGE_HPTEFLAGS (_PAGE_BUSY | _PAGE_HASHPTE | _PAGE_SECONDARY | _PAGE_GROUP_IX)
+
 /* PAGE_MASK gives the right answer below, but only by accident */
 /* It should be preserving the high 48 bits and then specifically */
 /* preserving _PAGE_SECONDARY | _PAGE_GROUP_IX */
-#define _PAGE_CHG_MASK	(PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_HPTEFLAGS)
+#define _PAGE_CHG_MASK (_PAGE_GUARDED | _PAGE_COHERENT | _PAGE_NO_CACHE | _PAGE_WRITETHRU | _PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_HPTEFLAGS | PAGE_MASK)
 
 #define _PAGE_BASE	(_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_COHERENT)
 
@@ -119,31 +120,32 @@
 #define PAGE_READONLY	__pgprot(_PAGE_BASE | _PAGE_USER)
 #define PAGE_READONLY_X	__pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_EXEC)
 #define PAGE_KERNEL	__pgprot(_PAGE_BASE | _PAGE_WRENABLE)
-#define PAGE_KERNEL_CI	__pgprot(_PAGE_PRESENT | _PAGE_ACCESSED | \
-			       _PAGE_WRENABLE | _PAGE_NO_CACHE | _PAGE_GUARDED)
 
 /*
- * The PowerPC can only do execute protection on a segment (256MB) basis,
- * not on a page basis.  So we consider execute permission the same as read.
+ * POWER4 and newer have per page execute protection, older chips can only
+ * do this on a segment (256MB) basis.
+ *
  * Also, write permissions imply read permissions.
  * This is the closest we can get..
+ *
+ * Note due to the way vm flags are laid out, the bits are XWR
  */
 #define __P000	PAGE_NONE
-#define __P001	PAGE_READONLY_X
+#define __P001	PAGE_READONLY
 #define __P010	PAGE_COPY
-#define __P011	PAGE_COPY_X
-#define __P100	PAGE_READONLY
+#define __P011	PAGE_COPY
+#define __P100	PAGE_READONLY_X
 #define __P101	PAGE_READONLY_X
-#define __P110	PAGE_COPY
+#define __P110	PAGE_COPY_X
 #define __P111	PAGE_COPY_X
 
 #define __S000	PAGE_NONE
-#define __S001	PAGE_READONLY_X
+#define __S001	PAGE_READONLY
 #define __S010	PAGE_SHARED
-#define __S011	PAGE_SHARED_X
-#define __S100	PAGE_READONLY
+#define __S011	PAGE_SHARED
+#define __S100	PAGE_READONLY_X
 #define __S101	PAGE_READONLY_X
-#define __S110	PAGE_SHARED
+#define __S110	PAGE_SHARED_X
 #define __S111	PAGE_SHARED_X
 
 #ifndef __ASSEMBLY__
@@ -200,7 +202,8 @@ int hash_huge_page(struct mm_struct *mm,
 })
 
 #define pte_modify(_pte, newprot) \
-  (__pte((pte_val(_pte) & _PAGE_CHG_MASK) | pgprot_val(newprot)))
+	(__pte((pte_val(_pte) & _PAGE_CHG_MASK) | \
+	       (pgprot_val(newprot) & ~_PAGE_CHG_MASK)))
 
 #define pte_none(pte)		((pte_val(pte) & ~_PAGE_HPTEFLAGS) == 0)
 #define pte_present(pte)	(pte_val(pte) & _PAGE_PRESENT)
@@ -270,9 +273,6 @@ static inline int pte_dirty(pte_t pte) {
 static inline int pte_young(pte_t pte) { return pte_val(pte) & _PAGE_ACCESSED;}
 static inline int pte_file(pte_t pte) { return pte_val(pte) & _PAGE_FILE;}
 
-static inline void pte_uncache(pte_t pte) { pte_val(pte) |= _PAGE_NO_CACHE; }
-static inline void pte_cache(pte_t pte)   { pte_val(pte) &= ~_PAGE_NO_CACHE; }
-
 static inline pte_t pte_rdprotect(pte_t pte) {
 	pte_val(pte) &= ~_PAGE_USER; return pte; }
 static inline pte_t pte_exprotect(pte_t pte) {
@@ -420,7 +420,7 @@ static inline void set_pte(pte_t *ptep, 
 static inline void __ptep_set_access_flags(pte_t *ptep, pte_t entry, int dirty)
 {
 	unsigned long bits = pte_val(entry) &
-		(_PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_RW);
+		(_PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_RW | _PAGE_EXEC);
 	unsigned long old, tmp;
 
 	__asm__ __volatile__(
diff -puN arch/ppc64/mm/hugetlbpage.c~nx-user-ppc64 arch/ppc64/mm/hugetlbpage.c
--- linux-2.6-bk/arch/ppc64/mm/hugetlbpage.c~nx-user-ppc64	Thu Oct  7 15:23:52 2004
+++ linux-2.6-bk-moilanen/arch/ppc64/mm/hugetlbpage.c	Thu Oct  7 15:23:52 2004
@@ -29,8 +29,8 @@
 
 /* HugePTE layout:
  *
- * 31 30 ... 15 14 13 12 10 9  8  7   6    5    4    3    2    1    0
- * PFN>>12..... -  -  -  -  -  -  HASH_IX....   2ND  HASH RW   -    HG=1
+ * 31 30 ... 15 14 13 12 10 9  8  7   6    5    4    3    2    	1    0
+ * PFN>>12..... -  -  -  -  -  -  HASH_IX....   2ND  HASH !EXEC	RW   HG=1
  */
 
 #define HUGEPTE_SHIFT	15
@@ -41,7 +41,8 @@
 #define _HUGEPAGE_GROUP_IX	0x000000e0
 #define _HUGEPAGE_HPTEFLAGS	(_HUGEPAGE_HASHPTE | _HUGEPAGE_SECONDARY | \
 				 _HUGEPAGE_GROUP_IX)
-#define _HUGEPAGE_RW		0x00000004
+#define _HUGEPAGE_RW		0x00000002
+#define _HUGEPAGE_EXEC		0x00000004	/* this is inverted */
 
 typedef struct {unsigned int val;} hugepte_t;
 #define hugepte_val(hugepte)	((hugepte).val)
@@ -722,6 +723,7 @@ int hash_huge_page(struct mm_struct *mm,
 	hugepte_t *ptep;
 	unsigned long va, vpn;
 	int is_write;
+	int is_exec;
 	hugepte_t old_pte, new_pte;
 	unsigned long hpteflags, prpn, flags;
 	long slot;
@@ -752,6 +754,10 @@ int hash_huge_page(struct mm_struct *mm,
 	if (unlikely(is_write && !(hugepte_val(*ptep) & _HUGEPAGE_RW)))
 		return 1;
 
+	is_exec = access & _PAGE_EXEC;
+	if (unlikely(is_exec && !(hugepte_val(*ptep) & _HUGEPAGE_EXEC)))
+		return 1;
+
 	/*
 	 * At this point, we have a pte (old_pte) which can be used to build
 	 * or update an HPTE. There are 2 cases:
@@ -769,7 +775,10 @@ int hash_huge_page(struct mm_struct *mm,
 	old_pte = *ptep;
 	new_pte = old_pte;
 
-	hpteflags = 0x2 | (! (hugepte_val(new_pte) & _HUGEPAGE_RW));
+	/* _HUGEPAGE_EXEC -> HW_NO_EXEC since it's inverted */
+	hpteflags = (hugepte_val(new_pte) & _HUGEPAGE_RW) |
+		(hugepte_val(new_pte) ^ HW_NO_EXEC) |
+		(!(hugepte_val(new_pte) & _HUGEPAGE_RW));
 
 	/* Check if pte already has an hpte (case 2) */
 	if (unlikely(hugepte_val(old_pte) & _HUGEPAGE_HASHPTE)) {
diff -L arch/ppc64/kernel/pSeries_htab.c -puN /dev/null /dev/null
diff -puN arch/ppc64/kernel/pSeries_lpar.c~nx-user-ppc64 arch/ppc64/kernel/pSeries_lpar.c
--- linux-2.6-bk/arch/ppc64/kernel/pSeries_lpar.c~nx-user-ppc64	Thu Oct  7 15:23:52 2004
+++ linux-2.6-bk-moilanen/arch/ppc64/kernel/pSeries_lpar.c	Thu Oct  7 15:23:52 2004
@@ -384,7 +384,7 @@ static void pSeries_lpar_hpte_updatebolt
 	slot = pSeries_lpar_hpte_find(vpn);
 	BUG_ON(slot == -1);
 
-	flags = newpp & 3;
+	flags = newpp & 7;
 	lpar_rc = plpar_pte_protect(flags, slot, 0);
 
 	BUG_ON(lpar_rc != H_Success);
diff -puN arch/ppc64/kernel/iSeries_htab.c~nx-user-ppc64 arch/ppc64/kernel/iSeries_htab.c
--- linux-2.6-bk/arch/ppc64/kernel/iSeries_htab.c~nx-user-ppc64	Thu Oct  7 15:23:52 2004
+++ linux-2.6-bk-moilanen/arch/ppc64/kernel/iSeries_htab.c	Thu Oct  7 15:23:52 2004
@@ -144,6 +144,10 @@ static long iSeries_hpte_updatepp(unsign
 
 	HvCallHpt_get(&hpte, slot);
 	if ((hpte.dw0.dw0.avpn == avpn) && (hpte.dw0.dw0.v)) {
+		/*
+		 * Hypervisor expects bit's as NPPP, which is
+		 * different from how they are mapped in our PP.
+		 */
 		HvCallHpt_setPp(slot, (newpp & 0x3) | ((newpp & 0x4) << 1));
 		iSeries_hunlock(slot);
 		return 0;

_



More information about the Linuxppc64-dev mailing list