This patch provides the implementation of execute-only pkey.
The architecture-independent layer expects the arch-dependent
layer, to support the ability to create and enable a special
key which has execute-only permission.

Signed-off-by: Ram Pai <linux...@us.ibm.com>
---
 arch/powerpc/include/asm/book3s/64/mmu.h |    1 +
 arch/powerpc/include/asm/pkeys.h         |    9 ++++-
 arch/powerpc/mm/pkeys.c                  |   57 ++++++++++++++++++++++++++++++
 3 files changed, 66 insertions(+), 1 deletions(-)

diff --git a/arch/powerpc/include/asm/book3s/64/mmu.h 
b/arch/powerpc/include/asm/book3s/64/mmu.h
index 55950f4..ee18ba0 100644
--- a/arch/powerpc/include/asm/book3s/64/mmu.h
+++ b/arch/powerpc/include/asm/book3s/64/mmu.h
@@ -115,6 +115,7 @@ struct patb_entry {
         * bit unset -> key available for allocation
         */
        u32 pkey_allocation_map;
+       s16 execute_only_pkey; /* key holding execute-only protection */
 #endif
 } mm_context_t;
 
diff --git a/arch/powerpc/include/asm/pkeys.h b/arch/powerpc/include/asm/pkeys.h
index 78c5362..0cf115f 100644
--- a/arch/powerpc/include/asm/pkeys.h
+++ b/arch/powerpc/include/asm/pkeys.h
@@ -115,11 +115,16 @@ static inline int mm_pkey_free(struct mm_struct *mm, int 
pkey)
  * Try to dedicate one of the protection keys to be used as an
  * execute-only protection key.
  */
+extern int __execute_only_pkey(struct mm_struct *mm);
 static inline int execute_only_pkey(struct mm_struct *mm)
 {
-       return 0;
+       if (!pkey_inited || !pkey_execute_disable_support)
+               return -1;
+
+       return __execute_only_pkey(mm);
 }
 
+
 static inline int arch_override_mprotect_pkey(struct vm_area_struct *vma,
                int prot, int pkey)
 {
@@ -141,6 +146,8 @@ static inline void pkey_mm_init(struct mm_struct *mm)
        if (!pkey_inited)
                return;
        mm_pkey_allocation_map(mm) = initial_allocation_mask;
+       /* -1 means unallocated or invalid */
+       mm->context.execute_only_pkey = -1;
 }
 
 extern void thread_pkey_regs_save(struct thread_struct *thread);
diff --git a/arch/powerpc/mm/pkeys.c b/arch/powerpc/mm/pkeys.c
index 7cd1be4..8a24983 100644
--- a/arch/powerpc/mm/pkeys.c
+++ b/arch/powerpc/mm/pkeys.c
@@ -188,3 +188,60 @@ void thread_pkey_regs_init(struct thread_struct *thread)
        write_iamr(0x0ul);
        write_uamor(0x0ul);
 }
+
+static inline bool pkey_allows_readwrite(int pkey)
+{
+       int pkey_shift = pkeyshift(pkey);
+
+       if (!(read_uamor() & (0x3UL << pkey_shift)))
+               return true;
+
+       return !(read_amr() & ((AMR_RD_BIT|AMR_WR_BIT) << pkey_shift));
+}
+
+int __execute_only_pkey(struct mm_struct *mm)
+{
+       bool need_to_set_mm_pkey = false;
+       int execute_only_pkey = mm->context.execute_only_pkey;
+       int ret;
+
+       /* Do we need to assign a pkey for mm's execute-only maps? */
+       if (execute_only_pkey == -1) {
+               /* Go allocate one to use, which might fail */
+               execute_only_pkey = mm_pkey_alloc(mm);
+               if (execute_only_pkey < 0)
+                       return -1;
+               need_to_set_mm_pkey = true;
+       }
+
+       /*
+        * We do not want to go through the relatively costly
+        * dance to set AMR if we do not need to.  Check it
+        * first and assume that if the execute-only pkey is
+        * readwrite-disabled than we do not have to set it
+        * ourselves.
+        */
+       if (!need_to_set_mm_pkey &&
+           !pkey_allows_readwrite(execute_only_pkey))
+               return execute_only_pkey;
+
+       /*
+        * Set up AMR so that it denies access for everything
+        * other than execution.
+        */
+       ret = __arch_set_user_pkey_access(current, execute_only_pkey,
+                       (PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE));
+       /*
+        * If the AMR-set operation failed somehow, just return
+        * 0 and effectively disable execute-only support.
+        */
+       if (ret) {
+               mm_set_pkey_free(mm, execute_only_pkey);
+               return -1;
+       }
+
+       /* We got one, store it and use it from here on out */
+       if (need_to_set_mm_pkey)
+               mm->context.execute_only_pkey = execute_only_pkey;
+       return execute_only_pkey;
+}
-- 
1.7.1

Reply via email to