Make KUAP/KUEP key a variable and also check whether the platform limit the max key such that we can't use the key for KUAP/KEUP.
Signed-off-by: Aneesh Kumar K.V <aneesh.ku...@linux.ibm.com> --- .../powerpc/include/asm/book3s/64/hash-pkey.h | 22 +------- arch/powerpc/include/asm/book3s/64/pkeys.h | 1 + arch/powerpc/mm/book3s64/pkeys.c | 53 ++++++++++++++++--- 3 files changed, 49 insertions(+), 27 deletions(-) diff --git a/arch/powerpc/include/asm/book3s/64/hash-pkey.h b/arch/powerpc/include/asm/book3s/64/hash-pkey.h index 9f44e208f036..ff9907c72ee3 100644 --- a/arch/powerpc/include/asm/book3s/64/hash-pkey.h +++ b/arch/powerpc/include/asm/book3s/64/hash-pkey.h @@ -2,9 +2,7 @@ #ifndef _ASM_POWERPC_BOOK3S_64_HASH_PKEY_H #define _ASM_POWERPC_BOOK3S_64_HASH_PKEY_H -/* We use key 3 for KERNEL */ -#define HASH_DEFAULT_KERNEL_KEY (HPTE_R_KEY_BIT0 | HPTE_R_KEY_BIT1) - +u64 pte_to_hpte_pkey_bits(u64 pteflags, unsigned long flags); static inline u64 hash__vmflag_to_pte_pkey_bits(u64 vm_flags) { return (((vm_flags & VM_PKEY_BIT0) ? H_PTE_PKEY_BIT0 : 0x0UL) | @@ -14,24 +12,6 @@ static inline u64 hash__vmflag_to_pte_pkey_bits(u64 vm_flags) ((vm_flags & VM_PKEY_BIT4) ? H_PTE_PKEY_BIT4 : 0x0UL)); } -static inline u64 pte_to_hpte_pkey_bits(u64 pteflags, unsigned long flags) -{ - unsigned long pte_pkey; - - pte_pkey = (((pteflags & H_PTE_PKEY_BIT4) ? HPTE_R_KEY_BIT4 : 0x0UL) | - ((pteflags & H_PTE_PKEY_BIT3) ? HPTE_R_KEY_BIT3 : 0x0UL) | - ((pteflags & H_PTE_PKEY_BIT2) ? HPTE_R_KEY_BIT2 : 0x0UL) | - ((pteflags & H_PTE_PKEY_BIT1) ? HPTE_R_KEY_BIT1 : 0x0UL) | - ((pteflags & H_PTE_PKEY_BIT0) ? HPTE_R_KEY_BIT0 : 0x0UL)); - - if (mmu_has_feature(MMU_FTR_KUAP) || mmu_has_feature(MMU_FTR_KUEP)) { - if ((pte_pkey == 0) && (flags & HPTE_USE_KERNEL_KEY)) - return HASH_DEFAULT_KERNEL_KEY; - } - - return pte_pkey; -} - static inline u16 hash__pte_to_pkey_bits(u64 pteflags) { return (((pteflags & H_PTE_PKEY_BIT4) ? 0x10 : 0x0UL) | diff --git a/arch/powerpc/include/asm/book3s/64/pkeys.h b/arch/powerpc/include/asm/book3s/64/pkeys.h index 3b8640498f5b..a2b6c4a7275f 100644 --- a/arch/powerpc/include/asm/book3s/64/pkeys.h +++ b/arch/powerpc/include/asm/book3s/64/pkeys.h @@ -8,6 +8,7 @@ extern u64 __ro_after_init default_uamor; extern u64 __ro_after_init default_amr; extern u64 __ro_after_init default_iamr; +extern int kup_key; static inline u64 vmflag_to_pte_pkey_bits(u64 vm_flags) { diff --git a/arch/powerpc/mm/book3s64/pkeys.c b/arch/powerpc/mm/book3s64/pkeys.c index f029e7bf5ca2..204e4598b45c 100644 --- a/arch/powerpc/mm/book3s64/pkeys.c +++ b/arch/powerpc/mm/book3s64/pkeys.c @@ -37,7 +37,10 @@ u64 default_uamor __ro_after_init; */ static int execute_only_key = 2; static bool pkey_execute_disable_supported; - +/* + * key used to implement KUAP/KUEP with hash translation. + */ +int kup_key = 3; #define AMR_BITS_PER_PKEY 2 #define AMR_RD_BIT 0x1UL @@ -185,6 +188,25 @@ void __init pkey_early_init_devtree(void) default_uamor &= ~(0x3ul << pkeyshift(execute_only_key)); } + if (unlikely(num_pkey <= kup_key)) { + /* + * Insufficient number of keys to support + * KUAP/KUEP feature. + */ + kup_key = -1; + } else { + /* handle key which is used by kernel for KAUP */ + reserved_allocation_mask |= (0x1 << kup_key); + /* + * Mark access for kup_key in default amr so that + * we continue to operate with that AMR in + * copy_to/from_user(). + */ + default_amr &= ~(0x3ul << pkeyshift(kup_key)); + default_iamr &= ~(0x1ul << pkeyshift(kup_key)); + default_uamor &= ~(0x3ul << pkeyshift(kup_key)); + } + /* * Allow access for only key 0. And prevent any other modification. */ @@ -205,9 +227,6 @@ void __init pkey_early_init_devtree(void) reserved_allocation_mask |= (0x1 << 1); default_uamor &= ~(0x3ul << pkeyshift(1)); - /* handle key 3 which is used by kernel for KAUP */ - reserved_allocation_mask |= (0x1 << 3); - default_uamor &= ~(0x3ul << pkeyshift(3)); /* * Prevent the usage of OS reserved keys. Update UAMOR @@ -236,7 +255,7 @@ void __init pkey_early_init_devtree(void) #ifdef CONFIG_PPC_KUEP void __init setup_kuep(bool disabled) { - if (disabled) + if (disabled || kup_key == -1) return; /* * On hash if PKEY feature is not enabled, disable KUAP too. @@ -262,7 +281,7 @@ void __init setup_kuep(bool disabled) #ifdef CONFIG_PPC_KUAP void __init setup_kuap(bool disabled) { - if (disabled) + if (disabled || kup_key == -1) return; /* * On hash if PKEY feature is not enabled, disable KUAP too. @@ -458,4 +477,26 @@ void arch_dup_pkeys(struct mm_struct *oldmm, struct mm_struct *mm) mm->context.execute_only_pkey = oldmm->context.execute_only_pkey; } +u64 pte_to_hpte_pkey_bits(u64 pteflags, unsigned long flags) +{ + unsigned long pte_pkey; + + pte_pkey = (((pteflags & H_PTE_PKEY_BIT4) ? HPTE_R_KEY_BIT4 : 0x0UL) | + ((pteflags & H_PTE_PKEY_BIT3) ? HPTE_R_KEY_BIT3 : 0x0UL) | + ((pteflags & H_PTE_PKEY_BIT2) ? HPTE_R_KEY_BIT2 : 0x0UL) | + ((pteflags & H_PTE_PKEY_BIT1) ? HPTE_R_KEY_BIT1 : 0x0UL) | + ((pteflags & H_PTE_PKEY_BIT0) ? HPTE_R_KEY_BIT0 : 0x0UL)); + + if (mmu_has_feature(MMU_FTR_KUAP) || mmu_has_feature(MMU_FTR_KUEP)) { + if ((pte_pkey == 0) && + (flags & HPTE_USE_KERNEL_KEY) && (kup_key != -1)) { + u64 vm_flag = pkey_to_vmflag_bits(kup_key); + u64 pte_flag = hash__vmflag_to_pte_pkey_bits(vm_flag); + return pte_to_hpte_pkey_bits(pte_flag, 0); + } + } + + return pte_pkey; +} + #endif /* CONFIG_PPC_MEM_KEYS */ -- 2.28.0