[PATCH/RFC] powerpc/mm: Cleanup handling of execute permission

Becky Bruce beckyb at kernel.crashing.org
Sat Aug 15 08:39:42 EST 2009


Ben,

This breaks the boot on 8572.  I don't know why yet (and I'm probably  
not going to figure it out before I go home, because, frankly, it's  
late on a Friday afternoon and I need a glass of wine or, perhaps, a  
beer).

Kumar and I will poke into this more and let you know what we find out  
- in the meantime, if you have any brilliant flashes, pony up!

-Becky

On Jul 28, 2009, at 2:32 AM, Benjamin Herrenschmidt wrote:

> This is an attempt at cleaning up a bit the way we handle execute
> permission on powerpc (again !).
>
> _PAGE_HWEXEC is gone, _PAGE_EXEC is now only defined by CPUs that
> can do something with it, and the myriad of #ifdef's in the I$/D$
> coherency code is reduced to 2 cases that hopefully should cover
> everything.
>
> The logic on BookE is a little bit different than what it was though
> not by much. Since now, _PAGE_EXEC will be set by the generic code
> for executable pages, we need to filter out if they are unclean and
> recover it. However, I don't expect the code to be more bloated than
> it already was in that area due to that change.
>
> Signed-off-by: Benjamin Herrenschmidt <benh at kernel.crashing.org>
> ---
>
> I could boast that this brings proper enforcing of per-page execute
> permissions to all BookE and 40x but in fact, we've had that now for
> some time as a side effect of my previous rework in that area (and
> I didn't even know it :-) We would only enable execute permission if
> the page was cache clean and we would only cache clean it if we took
> and exec fault. Since we now enforce that the later only work if
> VM_EXEC is part of the VMA flags, we de-fact already enforce per-page
> execute permissions... Unless I missed something
>
> Kumar, Becky, I could really use some review here :-) I tested on
> 440 and that's about it, I'll do more testing tomorrow. Basically, I
> _think_ we already enforce execute permission fully on all BookE today
> (test case welcome) but at least after that patch it becomes more
> obvious what is happening in the code.
>
> arch/powerpc/include/asm/pgtable-ppc32.h |    7 +-
> arch/powerpc/include/asm/pgtable-ppc64.h |    3 +-
> arch/powerpc/include/asm/pte-40x.h       |    2 +-
> arch/powerpc/include/asm/pte-44x.h       |    2 +-
> arch/powerpc/include/asm/pte-8xx.h       |    1 -
> arch/powerpc/include/asm/pte-book3e.h    |   13 ++-
> arch/powerpc/include/asm/pte-common.h    |   22 ++--
> arch/powerpc/include/asm/pte-fsl-booke.h |    2 +-
> arch/powerpc/include/asm/pte-hash32.h    |    1 -
> arch/powerpc/kernel/head_44x.S           |    2 +-
> arch/powerpc/kernel/head_fsl_booke.S     |    4 +-
> arch/powerpc/mm/40x_mmu.c                |    4 +-
> arch/powerpc/mm/pgtable.c                |  148 +++++++++++++++++++ 
> +----------
> arch/powerpc/mm/pgtable_32.c             |    2 +-
> arch/powerpc/mm/tlb_low_64e.S            |    4 +-
> 15 files changed, 132 insertions(+), 85 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/pgtable-ppc32.h b/arch/powerpc/ 
> include/asm/pgtable-ppc32.h
> index c9ff9d7..f2c52e2 100644
> --- a/arch/powerpc/include/asm/pgtable-ppc32.h
> +++ b/arch/powerpc/include/asm/pgtable-ppc32.h
> @@ -186,7 +186,7 @@ static inline unsigned long pte_update(pte_t *p,
> #endif /* !PTE_ATOMIC_UPDATES */
>
> #ifdef CONFIG_44x
> -	if ((old & _PAGE_USER) && (old & _PAGE_HWEXEC))
> +	if ((old & _PAGE_USER) && (old & _PAGE_EXEC))
> 		icache_44x_need_flush = 1;
> #endif
> 	return old;
> @@ -217,7 +217,7 @@ static inline unsigned long long  
> pte_update(pte_t *p,
> #endif /* !PTE_ATOMIC_UPDATES */
>
> #ifdef CONFIG_44x
> -	if ((old & _PAGE_USER) && (old & _PAGE_HWEXEC))
> +	if ((old & _PAGE_USER) && (old & _PAGE_EXEC))
> 		icache_44x_need_flush = 1;
> #endif
> 	return old;
> @@ -267,8 +267,7 @@ static inline void  
> huge_ptep_set_wrprotect(struct mm_struct *mm,
> static inline void __ptep_set_access_flags(pte_t *ptep, pte_t entry)
> {
> 	unsigned long bits = pte_val(entry) &
> -		(_PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_RW |
> -		 _PAGE_HWEXEC | _PAGE_EXEC);
> +		(_PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_RW | _PAGE_EXEC);
> 	pte_update(ptep, 0, bits);
> }
>
> diff --git a/arch/powerpc/include/asm/pgtable-ppc64.h b/arch/powerpc/ 
> include/asm/pgtable-ppc64.h
> index 200ec2d..806abe7 100644
> --- a/arch/powerpc/include/asm/pgtable-ppc64.h
> +++ b/arch/powerpc/include/asm/pgtable-ppc64.h
> @@ -313,8 +313,7 @@ static inline void pte_clear(struct mm_struct  
> *mm, unsigned long addr,
> static inline void __ptep_set_access_flags(pte_t *ptep, pte_t entry)
> {
> 	unsigned long bits = pte_val(entry) &
> -		(_PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_RW |
> -		 _PAGE_EXEC | _PAGE_HWEXEC);
> +		(_PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_RW | _PAGE_EXEC);
>
> #ifdef PTE_ATOMIC_UPDATES
> 	unsigned long old, tmp;
> diff --git a/arch/powerpc/include/asm/pte-40x.h b/arch/powerpc/ 
> include/asm/pte-40x.h
> index 07630fa..6c3e1f4 100644
> --- a/arch/powerpc/include/asm/pte-40x.h
> +++ b/arch/powerpc/include/asm/pte-40x.h
> @@ -46,7 +46,7 @@
> #define	_PAGE_RW	0x040	/* software: Writes permitted */
> #define	_PAGE_DIRTY	0x080	/* software: dirty page */
> #define _PAGE_HWWRITE	0x100	/* hardware: Dirty & RW, set in  
> exception */
> -#define _PAGE_HWEXEC	0x200	/* hardware: EX permission */
> +#define _PAGE_EXEC	0x200	/* hardware: EX permission */
> #define _PAGE_ACCESSED	0x400	/* software: R: page referenced */
>
> #define _PMD_PRESENT	0x400	/* PMD points to page of PTEs */
> diff --git a/arch/powerpc/include/asm/pte-44x.h b/arch/powerpc/ 
> include/asm/pte-44x.h
> index 37e98bc..4192b9b 100644
> --- a/arch/powerpc/include/asm/pte-44x.h
> +++ b/arch/powerpc/include/asm/pte-44x.h
> @@ -78,7 +78,7 @@
> #define _PAGE_PRESENT	0x00000001		/* S: PTE valid */
> #define _PAGE_RW	0x00000002		/* S: Write permission */
> #define _PAGE_FILE	0x00000004		/* S: nonlinear file mapping */
> -#define _PAGE_HWEXEC	0x00000004		/* H: Execute permission */
> +#define _PAGE_EXEC	0x00000004		/* H: Execute permission */
> #define _PAGE_ACCESSED	0x00000008		/* S: Page referenced */
> #define _PAGE_DIRTY	0x00000010		/* S: Page dirty */
> #define _PAGE_SPECIAL	0x00000020		/* S: Special page */
> diff --git a/arch/powerpc/include/asm/pte-8xx.h b/arch/powerpc/ 
> include/asm/pte-8xx.h
> index 8c6e312..94e9797 100644
> --- a/arch/powerpc/include/asm/pte-8xx.h
> +++ b/arch/powerpc/include/asm/pte-8xx.h
> @@ -36,7 +36,6 @@
> /* These five software bits must be masked out when the entry is  
> loaded
>  * into the TLB.
>  */
> -#define _PAGE_EXEC	0x0008	/* software: i-cache coherency required */
> #define _PAGE_GUARDED	0x0010	/* software: guarded access */
> #define _PAGE_DIRTY	0x0020	/* software: page changed */
> #define _PAGE_RW	0x0040	/* software: user write access allowed */
> diff --git a/arch/powerpc/include/asm/pte-book3e.h b/arch/powerpc/ 
> include/asm/pte-book3e.h
> index 1d27c77..9800565 100644
> --- a/arch/powerpc/include/asm/pte-book3e.h
> +++ b/arch/powerpc/include/asm/pte-book3e.h
> @@ -37,12 +37,13 @@
> #define _PAGE_WRITETHRU	0x800000 /* W: cache write-through */
>
> /* "Higher level" linux bit combinations */
> -#define _PAGE_EXEC	_PAGE_BAP_SX /* Can be executed from potentially  
> */
> -#define _PAGE_HWEXEC	_PAGE_BAP_UX /* .. and was cache cleaned */
> -#define _PAGE_RW	(_PAGE_BAP_SW | _PAGE_BAP_UW) /* User write  
> permission */
> -#define _PAGE_KERNEL_RW	(_PAGE_BAP_SW | _PAGE_BAP_SR | _PAGE_DIRTY)
> -#define _PAGE_KERNEL_RO	(_PAGE_BAP_SR)
> -#define _PAGE_USER	(_PAGE_BAP_UR | _PAGE_BAP_SR) /* Can be read */
> +#define _PAGE_EXEC		_PAGE_BAP_UX /* .. and was cache cleaned */
> +#define _PAGE_RW		(_PAGE_BAP_SW | _PAGE_BAP_UW) /* User write  
> permission */
> +#define _PAGE_KERNEL_RW		(_PAGE_BAP_SW | _PAGE_BAP_SR | _PAGE_DIRTY)
> +#define _PAGE_KERNEL_RO		(_PAGE_BAP_SR)
> +#define _PAGE_KERNEL_RWX	(_PAGE_BAP_SW | _PAGE_BAP_SR | _PAGE_DIRTY  
> | _PAGE_BAP_SX)
> +#define _PAGE_KERNEL_ROX	(_PAGE_BAP_SR | _PAGE_BAP_SX)
> +#define _PAGE_USER		(_PAGE_BAP_UR | _PAGE_BAP_SR) /* Can be read */
>
> #define _PAGE_HASHPTE	0
> #define _PAGE_BUSY	0
> diff --git a/arch/powerpc/include/asm/pte-common.h b/arch/powerpc/ 
> include/asm/pte-common.h
> index 8bb6464..c3b6507 100644
> --- a/arch/powerpc/include/asm/pte-common.h
> +++ b/arch/powerpc/include/asm/pte-common.h
> @@ -13,9 +13,6 @@
> #ifndef _PAGE_HWWRITE
> #define _PAGE_HWWRITE	0
> #endif
> -#ifndef _PAGE_HWEXEC
> -#define _PAGE_HWEXEC	0
> -#endif
> #ifndef _PAGE_EXEC
> #define _PAGE_EXEC	0
> #endif
> @@ -48,10 +45,16 @@
> #define PMD_PAGE_SIZE(pmd)	bad_call_to_PMD_PAGE_SIZE()
> #endif
> #ifndef _PAGE_KERNEL_RO
> -#define _PAGE_KERNEL_RO	0
> +#define _PAGE_KERNEL_RO		0
> +#endif
> +#ifndef _PAGE_KERNEL_ROX
> +#define _PAGE_KERNEL_ROX	(_PAGE_EXEC)
> #endif
> #ifndef _PAGE_KERNEL_RW
> -#define _PAGE_KERNEL_RW	(_PAGE_DIRTY | _PAGE_RW | _PAGE_HWWRITE)
> +#define _PAGE_KERNEL_RW		(_PAGE_DIRTY | _PAGE_RW | _PAGE_HWWRITE)
> +#endif
> +#ifndef _PAGE_KERNEL_RWX
> +#define _PAGE_KERNEL_RWX	(_PAGE_DIRTY | _PAGE_RW | _PAGE_HWWRITE |  
> _PAGE_EXEC)
> #endif
> #ifndef _PAGE_HPTEFLAGS
> #define _PAGE_HPTEFLAGS _PAGE_HASHPTE
> @@ -96,8 +99,7 @@ extern unsigned long  
> bad_call_to_PMD_PAGE_SIZE(void);
> #define PAGE_PROT_BITS	(_PAGE_GUARDED | _PAGE_COHERENT |  
> _PAGE_NO_CACHE | \
> 			 _PAGE_WRITETHRU | _PAGE_ENDIAN | _PAGE_4K_PFN | \
> 			 _PAGE_USER | _PAGE_ACCESSED | \
> -			 _PAGE_RW | _PAGE_HWWRITE | _PAGE_DIRTY | \
> -			 _PAGE_EXEC | _PAGE_HWEXEC)
> +			 _PAGE_RW | _PAGE_HWWRITE | _PAGE_DIRTY | _PAGE_EXEC)
>
> /*
>  * We define 2 sets of base prot bits, one for basic pages (ie,
> @@ -154,11 +156,9 @@ extern unsigned long  
> bad_call_to_PMD_PAGE_SIZE(void);
> 				 _PAGE_NO_CACHE)
> #define PAGE_KERNEL_NCG	__pgprot(_PAGE_BASE_NC | _PAGE_KERNEL_RW | \
> 				 _PAGE_NO_CACHE | _PAGE_GUARDED)
> -#define PAGE_KERNEL_X	__pgprot(_PAGE_BASE | _PAGE_KERNEL_RW |  
> _PAGE_EXEC | \
> -				 _PAGE_HWEXEC)
> +#define PAGE_KERNEL_X	__pgprot(_PAGE_BASE | _PAGE_KERNEL_RWX)
> #define PAGE_KERNEL_RO	__pgprot(_PAGE_BASE | _PAGE_KERNEL_RO)
> -#define PAGE_KERNEL_ROX	__pgprot(_PAGE_BASE | _PAGE_KERNEL_RO |  
> _PAGE_EXEC | \
> -				 _PAGE_HWEXEC)
> +#define PAGE_KERNEL_ROX	__pgprot(_PAGE_BASE | _PAGE_KERNEL_ROX)
>
> /* Protection used for kernel text. We want the debuggers to be able  
> to
>  * set breakpoints anywhere, so don't write protect the kernel text
> diff --git a/arch/powerpc/include/asm/pte-fsl-booke.h b/arch/powerpc/ 
> include/asm/pte-fsl-booke.h
> index 10820f5..ce8a9e9 100644
> --- a/arch/powerpc/include/asm/pte-fsl-booke.h
> +++ b/arch/powerpc/include/asm/pte-fsl-booke.h
> @@ -23,7 +23,7 @@
> #define _PAGE_FILE	0x00002	/* S: when !present: nonlinear file  
> mapping */
> #define _PAGE_RW	0x00004	/* S: Write permission (SW) */
> #define _PAGE_DIRTY	0x00008	/* S: Page dirty */
> -#define _PAGE_HWEXEC	0x00010	/* H: SX permission */
> +#define _PAGE_EXEC	0x00010	/* H: SX permission */
> #define _PAGE_ACCESSED	0x00020	/* S: Page referenced */
>
> #define _PAGE_ENDIAN	0x00040	/* H: E bit */
> diff --git a/arch/powerpc/include/asm/pte-hash32.h b/arch/powerpc/ 
> include/asm/pte-hash32.h
> index 16e571c..4aad413 100644
> --- a/arch/powerpc/include/asm/pte-hash32.h
> +++ b/arch/powerpc/include/asm/pte-hash32.h
> @@ -26,7 +26,6 @@
> #define _PAGE_WRITETHRU	0x040	/* W: cache write-through */
> #define _PAGE_DIRTY	0x080	/* C: page changed */
> #define _PAGE_ACCESSED	0x100	/* R: page referenced */
> -#define _PAGE_EXEC	0x200	/* software: i-cache coherency required */
> #define _PAGE_RW	0x400	/* software: user write access allowed */
> #define _PAGE_SPECIAL	0x800	/* software: Special page */
>
> diff --git a/arch/powerpc/kernel/head_44x.S b/arch/powerpc/kernel/ 
> head_44x.S
> index 656cfb2..711368b 100644
> --- a/arch/powerpc/kernel/head_44x.S
> +++ b/arch/powerpc/kernel/head_44x.S
> @@ -497,7 +497,7 @@ tlb_44x_patch_hwater_D:
> 	mtspr	SPRN_MMUCR,r12
>
> 	/* Make up the required permissions */
> -	li	r13,_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_HWEXEC
> +	li	r13,_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_EXEC
>
> 	/* Compute pgdir/pmd offset */
> 	rlwinm 	r12, r10, PPC44x_PGD_OFF_SHIFT, PPC44x_PGD_OFF_MASK_BIT, 29
> diff --git a/arch/powerpc/kernel/head_fsl_booke.S b/arch/powerpc/ 
> kernel/head_fsl_booke.S
> index eca8048..2c5af52 100644
> --- a/arch/powerpc/kernel/head_fsl_booke.S
> +++ b/arch/powerpc/kernel/head_fsl_booke.S
> @@ -643,7 +643,7 @@ interrupt_base:
>
> 4:
> 	/* Make up the required permissions */
> -	li	r13,_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_HWEXEC
> +	li	r13,_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_EXEC
>
> 	FIND_PTE
> 	andc.	r13,r13,r11		/* Check permission */
> @@ -742,7 +742,7 @@ finish_tlb_load:
> #endif
> 	mtspr	SPRN_MAS2, r12
>
> -	li	r10, (_PAGE_HWEXEC | _PAGE_PRESENT)
> +	li	r10, (_PAGE_EXEC | _PAGE_PRESENT)
> 	rlwimi	r10, r11, 31, 29, 29	/* extract _PAGE_DIRTY into SW */
> 	and	r12, r11, r10
> 	andi.	r10, r11, _PAGE_USER	/* Test for _PAGE_USER */
> diff --git a/arch/powerpc/mm/40x_mmu.c b/arch/powerpc/mm/40x_mmu.c
> index 29954dc..f5e7b9c 100644
> --- a/arch/powerpc/mm/40x_mmu.c
> +++ b/arch/powerpc/mm/40x_mmu.c
> @@ -105,7 +105,7 @@ unsigned long __init mmu_mapin_ram(void)
>
> 	while (s >= LARGE_PAGE_SIZE_16M) {
> 		pmd_t *pmdp;
> -		unsigned long val = p | _PMD_SIZE_16M | _PAGE_HWEXEC |  
> _PAGE_HWWRITE;
> +		unsigned long val = p | _PMD_SIZE_16M | _PAGE_EXEC | _PAGE_HWWRITE;
>
> 		pmdp = pmd_offset(pud_offset(pgd_offset_k(v), v), v);
> 		pmd_val(*pmdp++) = val;
> @@ -120,7 +120,7 @@ unsigned long __init mmu_mapin_ram(void)
>
> 	while (s >= LARGE_PAGE_SIZE_4M) {
> 		pmd_t *pmdp;
> -		unsigned long val = p | _PMD_SIZE_4M | _PAGE_HWEXEC |  
> _PAGE_HWWRITE;
> +		unsigned long val = p | _PMD_SIZE_4M | _PAGE_EXEC | _PAGE_HWWRITE;
>
> 		pmdp = pmd_offset(pud_offset(pgd_offset_k(v), v), v);
> 		pmd_val(*pmdp) = val;
> diff --git a/arch/powerpc/mm/pgtable.c b/arch/powerpc/mm/pgtable.c
> index cafb2a2..d568d2c 100644
> --- a/arch/powerpc/mm/pgtable.c
> +++ b/arch/powerpc/mm/pgtable.c
> @@ -128,73 +128,126 @@ void pte_free_finish(void)
>
> #endif /* CONFIG_SMP */
>
> +static inline int is_exec_fault(void)
> +{
> +	return current->thread.regs && TRAP(current->thread.regs) == 0x400;
> +}
> +
> +/* We only try to do i/d cache coherency on stuff that looks like
> + * reasonably "normal" PTEs. We currently require a PTE to be present
> + * and we avoid _PAGE_SPECIAL and _PAGE_NO_CACHE. We also only do  
> that
> + * on userspace PTEs
> + */
> +static inline int pte_looks_normal(pte_t pte)
> +{
> +	return (pte_val(pte) &
> +		(_PAGE_PRESENT | _PAGE_SPECIAL | _PAGE_NO_CACHE | _PAGE_USER)) ==
> +		(_PAGE_PRESENT | _PAGE_USER);
> +}
> +
> +
> /*
>  * Handle i/d cache flushing, called from set_pte_at() or  
> ptep_set_access_flags()
>  */
> -static pte_t do_dcache_icache_coherency(pte_t pte)
> +struct page * maybe_pte_to_page(pte_t pte)
> {
> 	unsigned long pfn = pte_pfn(pte);
> 	struct page *page;
>
> 	if (unlikely(!pfn_valid(pfn)))
> -		return pte;
> +		return NULL;
> 	page = pfn_to_page(pfn);
> -
> -	if (!PageReserved(page) && !test_bit(PG_arch_1, &page->flags)) {
> -		pr_devel("do_dcache_icache_coherency... flushing\n");
> -		flush_dcache_icache_page(page);
> -		set_bit(PG_arch_1, &page->flags);
> -	}
> -	else
> -		pr_devel("do_dcache_icache_coherency... already clean\n");
> -	return __pte(pte_val(pte) | _PAGE_HWEXEC);
> +	if (PageReserved(page))
> +		return NULL;
> +	return page;
> }
>
> -static inline int is_exec_fault(void)
> -{
> -	return current->thread.regs && TRAP(current->thread.regs) == 0x400;
> -}
> +#if defined(CONFIG_PPC_STD_MMU) || _PAGE_EXEC == 0
>
> -/* We only try to do i/d cache coherency on stuff that looks like
> - * reasonably "normal" PTEs. We currently require a PTE to be present
> - * and we avoid _PAGE_SPECIAL and _PAGE_NO_CACHE
> +/* Server-style MMU handles coherency when hashing if HW exec  
> permission
> + * is supposed per page (currently 64-bit only). If not, then, we  
> always
> + * flush the cache for valid PTEs in set_pte. Embedded CPU without  
> HW exec
> + * support falls into the same category.
>  */
> -static inline int pte_looks_normal(pte_t pte)
> +
> +static pte_t set_pte_filter(pte_t pte)
> {
> -	return (pte_val(pte) &
> -		(_PAGE_PRESENT | _PAGE_SPECIAL | _PAGE_NO_CACHE)) ==
> -		(_PAGE_PRESENT);
> +	pte = __pte(pte_val(pte) & ~_PAGE_HPTEFLAGS);
> +	if (pte_looks_normal(pte) && ! 
> (cpu_has_feature(CPU_FTR_COHERENT_ICACHE) ||
> +				       cpu_has_feature(CPU_FTR_NOEXECUTE))) {
> +		struct page *pg = maybe_pte_to_page(pte);
> +		if (!pg)
> +			return pte;
> +		if (!test_bit(PG_arch_1, &pg->flags)) {
> +			flush_dcache_icache_page(pg);
> +			set_bit(PG_arch_1, &pg->flags);
> +		}
> +	}
> +	return pte;
> }
>
> -#if defined(CONFIG_PPC_STD_MMU)
> -/* Server-style MMU handles coherency when hashing if HW exec  
> permission
> - * is supposed per page (currently 64-bit only). Else, we always  
> flush
> - * valid PTEs in set_pte.
> - */
> -static inline int pte_need_exec_flush(pte_t pte, int set_pte)
> +static pte_t set_access_flags_filter(pte_t pte, struct  
> vm_area_struct *vma, int dirty)
> {
> -	return set_pte && pte_looks_normal(pte) &&
> -		!(cpu_has_feature(CPU_FTR_COHERENT_ICACHE) ||
> -		  cpu_has_feature(CPU_FTR_NOEXECUTE));
> +	return pte;
> }
> -#elif _PAGE_HWEXEC == 0
> -/* Embedded type MMU without HW exec support (8xx only so far), we  
> flush
> - * the cache for any present PTE
> +
> +#else /* defined(CONFIG_PPC_STD_MMU) || _PAGE_EXEC == 0 */
> +
> +/* Embedded type MMU with HW exec support. This is a bit more  
> complicated
> + * as we don't have two bits to spare for _PAGE_EXEC and  
> _PAGE_HWEXEC so
> + * instead we "filter out" the exec permission for non clean pages.
>  */
> -static inline int pte_need_exec_flush(pte_t pte, int set_pte)
> +static pte_t set_pte_filter(pte_t pte)
> {
> -	return set_pte && pte_looks_normal(pte);
> +	struct page *pg;
> +
> +	/* No exec permission in the first place, move on */
> +	if (!(pte_val(pte) & _PAGE_EXEC) || !pte_looks_normal(pte))
> +		return pte;
> +
> +	/* If you set _PAGE_EXEC on weird pages you're on your own */
> +	pg = maybe_pte_to_page(pte);
> +	if (!pg)
> +		return pte;
> +
> +	/* If the page clean, we move on */
> +	if (test_bit(PG_arch_1, &pg->flags))
> +		return pte;
> +
> +	/* If it's an exec fault, we flush the cache and make it clean */
> +	if (is_exec_fault()) {
> +		flush_dcache_icache_page(pg);
> +		set_bit(PG_arch_1, &pg->flags);
> +		return pte;
> +	}
> +
> +	/* Else, we filter out _PAGE_EXEC */
> +	return __pte(pte_val(pte) & ~_PAGE_EXEC);
> }
> -#else
> -/* Other embedded CPUs with HW exec support per-page, we flush on  
> exec
> - * fault if HWEXEC is not set
> - */
> -static inline int pte_need_exec_flush(pte_t pte, int set_pte)
> +
> +static pte_t set_access_flags_filter(pte_t pte, struct  
> vm_area_struct *vma, int dirty)
> {
> -	return pte_looks_normal(pte) && is_exec_fault() &&
> -		!(pte_val(pte) & _PAGE_HWEXEC);
> +	/* So here, we only care about exec faults, as we use them
> +	 * to recover lost _PAGE_EXEC and perform I$/D$ coherency
> +	 * if necessary. Also if _PAGE_EXEC is already set, same deal,
> +	 * we just bail out
> +	 */
> +	if (dirty || (pte_val(pte) & _PAGE_EXEC) || !is_exec_fault())
> +		return pte;
> +
> +#ifdef CONFIG_DEBUG_VM
> +	/* So this is an exec fault, _PAGE_EXEC is not set. If it was
> +	 * an error we would have bailed out earlier in do_page_fault()
> +	 * but let's make sure of it
> +	 */
> +	if (WARN_ON(!(vma->vm_flags & VM_EXEC)))
> +		return pte;
> +#endif /* CONFIG_DEBUG_VM */
> +
> +	return __pte(pte_val(pte) | _PAGE_EXEC);
> }
> -#endif
> +
> +#endif /* !(defined(CONFIG_PPC_STD_MMU) || _PAGE_EXEC == 0) */
>
> /*
>  * set_pte stores a linux PTE into the linux page table.
> @@ -208,9 +261,7 @@ void set_pte_at(struct mm_struct *mm, unsigned  
> long addr, pte_t *ptep, pte_t pte
> 	 * this context might not have been activated yet when this
> 	 * is called.
> 	 */
> -	pte = __pte(pte_val(pte) & ~_PAGE_HPTEFLAGS);
> -	if (pte_need_exec_flush(pte, 1))
> -		pte = do_dcache_icache_coherency(pte);
> +	pte = set_pte_filter(pte);
>
> 	/* Perform the setting of the PTE */
> 	__set_pte_at(mm, addr, ptep, pte, 0);
> @@ -227,8 +278,7 @@ int ptep_set_access_flags(struct vm_area_struct  
> *vma, unsigned long address,
> 			  pte_t *ptep, pte_t entry, int dirty)
> {
> 	int changed;
> -	if (!dirty && pte_need_exec_flush(entry, 0))
> -		entry = do_dcache_icache_coherency(entry);
> +	entry = set_access_flags_filter(entry, vma, dirty);
> 	changed = !pte_same(*(ptep), entry);
> 	if (changed) {
> 		if (!(vma->vm_flags & VM_HUGETLB))
> diff --git a/arch/powerpc/mm/pgtable_32.c b/arch/powerpc/mm/ 
> pgtable_32.c
> index 5422169..cb96cb2 100644
> --- a/arch/powerpc/mm/pgtable_32.c
> +++ b/arch/powerpc/mm/pgtable_32.c
> @@ -142,7 +142,7 @@ ioremap_flags(phys_addr_t addr, unsigned long  
> size, unsigned long flags)
> 		flags |= _PAGE_DIRTY | _PAGE_HWWRITE;
>
> 	/* we don't want to let _PAGE_USER and _PAGE_EXEC leak out */
> -	flags &= ~(_PAGE_USER | _PAGE_EXEC | _PAGE_HWEXEC);
> +	flags &= ~(_PAGE_USER | _PAGE_EXEC);
>
> 	return __ioremap_caller(addr, size, flags,  
> __builtin_return_address(0));
> }
> diff --git a/arch/powerpc/mm/tlb_low_64e.S b/arch/powerpc/mm/ 
> tlb_low_64e.S
> index 10d524d..cd92f62 100644
> --- a/arch/powerpc/mm/tlb_low_64e.S
> +++ b/arch/powerpc/mm/tlb_low_64e.S
> @@ -133,7 +133,7 @@
>
> 	/* We do the user/kernel test for the PID here along with the RW test
> 	 */
> -	li	r11,_PAGE_PRESENT|_PAGE_HWEXEC	/* Base perm */
> +	li	r11,_PAGE_PRESENT|_PAGE_EXEC	/* Base perm */
> 	oris	r11,r11,_PAGE_ACCESSED at h
>
> 	cmpldi	cr0,r15,0			/* Check for user region */
> @@ -256,7 +256,7 @@ normal_tlb_miss_done:
>
> normal_tlb_miss_access_fault:
> 	/* We need to check if it was an instruction miss */
> -	andi.	r10,r11,_PAGE_HWEXEC
> +	andi.	r10,r11,_PAGE_EXEC
> 	bne	1f
> 	ld	r14,EX_TLB_DEAR(r12)
> 	ld	r15,EX_TLB_ESR(r12)
> -- 
> 1.6.1.2.14.gf26b5
>
>



More information about the Linuxppc-dev mailing list