162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PowerPC Memory Protection Keys management 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2017, Ram Pai, IBM Corporation. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <asm/mman.h> 962306a36Sopenharmony_ci#include <asm/mmu_context.h> 1062306a36Sopenharmony_ci#include <asm/mmu.h> 1162306a36Sopenharmony_ci#include <asm/setup.h> 1262306a36Sopenharmony_ci#include <asm/smp.h> 1362306a36Sopenharmony_ci#include <asm/firmware.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/pkeys.h> 1662306a36Sopenharmony_ci#include <linux/of_fdt.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ciint num_pkey; /* Max number of pkeys supported */ 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * Keys marked in the reservation list cannot be allocated by userspace 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ciu32 reserved_allocation_mask __ro_after_init; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* Bits set for the initially allocated keys */ 2662306a36Sopenharmony_cistatic u32 initial_allocation_mask __ro_after_init; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * Even if we allocate keys with sys_pkey_alloc(), we need to make sure 3062306a36Sopenharmony_ci * other thread still find the access denied using the same keys. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ciu64 default_amr __ro_after_init = ~0x0UL; 3362306a36Sopenharmony_ciu64 default_iamr __ro_after_init = 0x5555555555555555UL; 3462306a36Sopenharmony_ciu64 default_uamor __ro_after_init; 3562306a36Sopenharmony_ciEXPORT_SYMBOL(default_amr); 3662306a36Sopenharmony_ci/* 3762306a36Sopenharmony_ci * Key used to implement PROT_EXEC mmap. Denies READ/WRITE 3862306a36Sopenharmony_ci * We pick key 2 because 0 is special key and 1 is reserved as per ISA. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_cistatic int execute_only_key = 2; 4162306a36Sopenharmony_cistatic bool pkey_execute_disable_supported; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define AMR_BITS_PER_PKEY 2 4562306a36Sopenharmony_ci#define AMR_RD_BIT 0x1UL 4662306a36Sopenharmony_ci#define AMR_WR_BIT 0x2UL 4762306a36Sopenharmony_ci#define IAMR_EX_BIT 0x1UL 4862306a36Sopenharmony_ci#define PKEY_REG_BITS (sizeof(u64) * 8) 4962306a36Sopenharmony_ci#define pkeyshift(pkey) (PKEY_REG_BITS - ((pkey+1) * AMR_BITS_PER_PKEY)) 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic int __init dt_scan_storage_keys(unsigned long node, 5262306a36Sopenharmony_ci const char *uname, int depth, 5362306a36Sopenharmony_ci void *data) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci const char *type = of_get_flat_dt_prop(node, "device_type", NULL); 5662306a36Sopenharmony_ci const __be32 *prop; 5762306a36Sopenharmony_ci int *pkeys_total = (int *) data; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci /* We are scanning "cpu" nodes only */ 6062306a36Sopenharmony_ci if (type == NULL || strcmp(type, "cpu") != 0) 6162306a36Sopenharmony_ci return 0; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci prop = of_get_flat_dt_prop(node, "ibm,processor-storage-keys", NULL); 6462306a36Sopenharmony_ci if (!prop) 6562306a36Sopenharmony_ci return 0; 6662306a36Sopenharmony_ci *pkeys_total = be32_to_cpu(prop[0]); 6762306a36Sopenharmony_ci return 1; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic int __init scan_pkey_feature(void) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci int ret; 7362306a36Sopenharmony_ci int pkeys_total = 0; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* 7662306a36Sopenharmony_ci * Pkey is not supported with Radix translation. 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_ci if (early_radix_enabled()) 7962306a36Sopenharmony_ci return 0; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci ret = of_scan_flat_dt(dt_scan_storage_keys, &pkeys_total); 8262306a36Sopenharmony_ci if (ret == 0) { 8362306a36Sopenharmony_ci /* 8462306a36Sopenharmony_ci * Let's assume 32 pkeys on P8/P9 bare metal, if its not defined by device 8562306a36Sopenharmony_ci * tree. We make this exception since some version of skiboot forgot to 8662306a36Sopenharmony_ci * expose this property on power8/9. 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ci if (!firmware_has_feature(FW_FEATURE_LPAR)) { 8962306a36Sopenharmony_ci unsigned long pvr = mfspr(SPRN_PVR); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (PVR_VER(pvr) == PVR_POWER8 || PVR_VER(pvr) == PVR_POWER8E || 9262306a36Sopenharmony_ci PVR_VER(pvr) == PVR_POWER8NVL || PVR_VER(pvr) == PVR_POWER9) 9362306a36Sopenharmony_ci pkeys_total = 32; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#ifdef CONFIG_PPC_MEM_KEYS 9862306a36Sopenharmony_ci /* 9962306a36Sopenharmony_ci * Adjust the upper limit, based on the number of bits supported by 10062306a36Sopenharmony_ci * arch-neutral code. 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_ci pkeys_total = min_t(int, pkeys_total, 10362306a36Sopenharmony_ci ((ARCH_VM_PKEY_FLAGS >> VM_PKEY_SHIFT) + 1)); 10462306a36Sopenharmony_ci#endif 10562306a36Sopenharmony_ci return pkeys_total; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_civoid __init pkey_early_init_devtree(void) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci int pkeys_total, i; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci#ifdef CONFIG_PPC_MEM_KEYS 11362306a36Sopenharmony_ci /* 11462306a36Sopenharmony_ci * We define PKEY_DISABLE_EXECUTE in addition to the arch-neutral 11562306a36Sopenharmony_ci * generic defines for PKEY_DISABLE_ACCESS and PKEY_DISABLE_WRITE. 11662306a36Sopenharmony_ci * Ensure that the bits a distinct. 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_ci BUILD_BUG_ON(PKEY_DISABLE_EXECUTE & 11962306a36Sopenharmony_ci (PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE)); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* 12262306a36Sopenharmony_ci * pkey_to_vmflag_bits() assumes that the pkey bits are contiguous 12362306a36Sopenharmony_ci * in the vmaflag. Make sure that is really the case. 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_ci BUILD_BUG_ON(__builtin_clzl(ARCH_VM_PKEY_FLAGS >> VM_PKEY_SHIFT) + 12662306a36Sopenharmony_ci __builtin_popcountl(ARCH_VM_PKEY_FLAGS >> VM_PKEY_SHIFT) 12762306a36Sopenharmony_ci != (sizeof(u64) * BITS_PER_BYTE)); 12862306a36Sopenharmony_ci#endif 12962306a36Sopenharmony_ci /* 13062306a36Sopenharmony_ci * Only P7 and above supports SPRN_AMR update with MSR[PR] = 1 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_ci if (!early_cpu_has_feature(CPU_FTR_ARCH_206)) 13362306a36Sopenharmony_ci return; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* scan the device tree for pkey feature */ 13662306a36Sopenharmony_ci pkeys_total = scan_pkey_feature(); 13762306a36Sopenharmony_ci if (!pkeys_total) 13862306a36Sopenharmony_ci goto out; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* Allow all keys to be modified by default */ 14162306a36Sopenharmony_ci default_uamor = ~0x0UL; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci cur_cpu_spec->mmu_features |= MMU_FTR_PKEY; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* 14662306a36Sopenharmony_ci * The device tree cannot be relied to indicate support for 14762306a36Sopenharmony_ci * execute_disable support. Instead we use a PVR check. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_ci if (pvr_version_is(PVR_POWER7) || pvr_version_is(PVR_POWER7p)) 15062306a36Sopenharmony_ci pkey_execute_disable_supported = false; 15162306a36Sopenharmony_ci else 15262306a36Sopenharmony_ci pkey_execute_disable_supported = true; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci#ifdef CONFIG_PPC_4K_PAGES 15562306a36Sopenharmony_ci /* 15662306a36Sopenharmony_ci * The OS can manage only 8 pkeys due to its inability to represent them 15762306a36Sopenharmony_ci * in the Linux 4K PTE. Mark all other keys reserved. 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_ci num_pkey = min(8, pkeys_total); 16062306a36Sopenharmony_ci#else 16162306a36Sopenharmony_ci num_pkey = pkeys_total; 16262306a36Sopenharmony_ci#endif 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (unlikely(num_pkey <= execute_only_key) || !pkey_execute_disable_supported) { 16562306a36Sopenharmony_ci /* 16662306a36Sopenharmony_ci * Insufficient number of keys to support 16762306a36Sopenharmony_ci * execute only key. Mark it unavailable. 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_ci execute_only_key = -1; 17062306a36Sopenharmony_ci } else { 17162306a36Sopenharmony_ci /* 17262306a36Sopenharmony_ci * Mark the execute_only_pkey as not available for 17362306a36Sopenharmony_ci * user allocation via pkey_alloc. 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_ci reserved_allocation_mask |= (0x1 << execute_only_key); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* 17862306a36Sopenharmony_ci * Deny READ/WRITE for execute_only_key. 17962306a36Sopenharmony_ci * Allow execute in IAMR. 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_ci default_amr |= (0x3ul << pkeyshift(execute_only_key)); 18262306a36Sopenharmony_ci default_iamr &= ~(0x1ul << pkeyshift(execute_only_key)); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* 18562306a36Sopenharmony_ci * Clear the uamor bits for this key. 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_ci default_uamor &= ~(0x3ul << pkeyshift(execute_only_key)); 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (unlikely(num_pkey <= 3)) { 19162306a36Sopenharmony_ci /* 19262306a36Sopenharmony_ci * Insufficient number of keys to support 19362306a36Sopenharmony_ci * KUAP/KUEP feature. 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_ci disable_kuep = true; 19662306a36Sopenharmony_ci disable_kuap = true; 19762306a36Sopenharmony_ci WARN(1, "Disabling kernel user protection due to low (%d) max supported keys\n", num_pkey); 19862306a36Sopenharmony_ci } else { 19962306a36Sopenharmony_ci /* handle key which is used by kernel for KAUP */ 20062306a36Sopenharmony_ci reserved_allocation_mask |= (0x1 << 3); 20162306a36Sopenharmony_ci /* 20262306a36Sopenharmony_ci * Mark access for kup_key in default amr so that 20362306a36Sopenharmony_ci * we continue to operate with that AMR in 20462306a36Sopenharmony_ci * copy_to/from_user(). 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_ci default_amr &= ~(0x3ul << pkeyshift(3)); 20762306a36Sopenharmony_ci default_iamr &= ~(0x1ul << pkeyshift(3)); 20862306a36Sopenharmony_ci default_uamor &= ~(0x3ul << pkeyshift(3)); 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* 21262306a36Sopenharmony_ci * Allow access for only key 0. And prevent any other modification. 21362306a36Sopenharmony_ci */ 21462306a36Sopenharmony_ci default_amr &= ~(0x3ul << pkeyshift(0)); 21562306a36Sopenharmony_ci default_iamr &= ~(0x1ul << pkeyshift(0)); 21662306a36Sopenharmony_ci default_uamor &= ~(0x3ul << pkeyshift(0)); 21762306a36Sopenharmony_ci /* 21862306a36Sopenharmony_ci * key 0 is special in that we want to consider it an allocated 21962306a36Sopenharmony_ci * key which is preallocated. We don't allow changing AMR bits 22062306a36Sopenharmony_ci * w.r.t key 0. But one can pkey_free(key0) 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_ci initial_allocation_mask |= (0x1 << 0); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* 22562306a36Sopenharmony_ci * key 1 is recommended not to be used. PowerISA(3.0) page 1015, 22662306a36Sopenharmony_ci * programming note. 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci reserved_allocation_mask |= (0x1 << 1); 22962306a36Sopenharmony_ci default_uamor &= ~(0x3ul << pkeyshift(1)); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* 23262306a36Sopenharmony_ci * Prevent the usage of OS reserved keys. Update UAMOR 23362306a36Sopenharmony_ci * for those keys. Also mark the rest of the bits in the 23462306a36Sopenharmony_ci * 32 bit mask as reserved. 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_ci for (i = num_pkey; i < 32 ; i++) { 23762306a36Sopenharmony_ci reserved_allocation_mask |= (0x1 << i); 23862306a36Sopenharmony_ci default_uamor &= ~(0x3ul << pkeyshift(i)); 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci /* 24162306a36Sopenharmony_ci * Prevent the allocation of reserved keys too. 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_ci initial_allocation_mask |= reserved_allocation_mask; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci pr_info("Enabling pkeys with max key count %d\n", num_pkey); 24662306a36Sopenharmony_ciout: 24762306a36Sopenharmony_ci /* 24862306a36Sopenharmony_ci * Setup uamor on boot cpu 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_ci mtspr(SPRN_UAMOR, default_uamor); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return; 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci#ifdef CONFIG_PPC_KUEP 25662306a36Sopenharmony_civoid setup_kuep(bool disabled) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci if (disabled) 25962306a36Sopenharmony_ci return; 26062306a36Sopenharmony_ci /* 26162306a36Sopenharmony_ci * On hash if PKEY feature is not enabled, disable KUAP too. 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_ci if (!early_radix_enabled() && !early_mmu_has_feature(MMU_FTR_PKEY)) 26462306a36Sopenharmony_ci return; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (smp_processor_id() == boot_cpuid) { 26762306a36Sopenharmony_ci pr_info("Activating Kernel Userspace Execution Prevention\n"); 26862306a36Sopenharmony_ci cur_cpu_spec->mmu_features |= MMU_FTR_BOOK3S_KUEP; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* 27262306a36Sopenharmony_ci * Radix always uses key0 of the IAMR to determine if an access is 27362306a36Sopenharmony_ci * allowed. We set bit 0 (IBM bit 1) of key0, to prevent instruction 27462306a36Sopenharmony_ci * fetch. 27562306a36Sopenharmony_ci */ 27662306a36Sopenharmony_ci mtspr(SPRN_IAMR, AMR_KUEP_BLOCKED); 27762306a36Sopenharmony_ci isync(); 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci#endif 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci#ifdef CONFIG_PPC_KUAP 28262306a36Sopenharmony_civoid setup_kuap(bool disabled) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci if (disabled) 28562306a36Sopenharmony_ci return; 28662306a36Sopenharmony_ci /* 28762306a36Sopenharmony_ci * On hash if PKEY feature is not enabled, disable KUAP too. 28862306a36Sopenharmony_ci */ 28962306a36Sopenharmony_ci if (!early_radix_enabled() && !early_mmu_has_feature(MMU_FTR_PKEY)) 29062306a36Sopenharmony_ci return; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (smp_processor_id() == boot_cpuid) { 29362306a36Sopenharmony_ci pr_info("Activating Kernel Userspace Access Prevention\n"); 29462306a36Sopenharmony_ci cur_cpu_spec->mmu_features |= MMU_FTR_KUAP; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* 29862306a36Sopenharmony_ci * Set the default kernel AMR values on all cpus. 29962306a36Sopenharmony_ci */ 30062306a36Sopenharmony_ci mtspr(SPRN_AMR, AMR_KUAP_BLOCKED); 30162306a36Sopenharmony_ci isync(); 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci#endif 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci#ifdef CONFIG_PPC_MEM_KEYS 30662306a36Sopenharmony_civoid pkey_mm_init(struct mm_struct *mm) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci if (!mmu_has_feature(MMU_FTR_PKEY)) 30962306a36Sopenharmony_ci return; 31062306a36Sopenharmony_ci mm_pkey_allocation_map(mm) = initial_allocation_mask; 31162306a36Sopenharmony_ci mm->context.execute_only_pkey = execute_only_key; 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic inline void init_amr(int pkey, u8 init_bits) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci u64 new_amr_bits = (((u64)init_bits & 0x3UL) << pkeyshift(pkey)); 31762306a36Sopenharmony_ci u64 old_amr = current_thread_amr() & ~((u64)(0x3ul) << pkeyshift(pkey)); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci current->thread.regs->amr = old_amr | new_amr_bits; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic inline void init_iamr(int pkey, u8 init_bits) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci u64 new_iamr_bits = (((u64)init_bits & 0x1UL) << pkeyshift(pkey)); 32562306a36Sopenharmony_ci u64 old_iamr = current_thread_iamr() & ~((u64)(0x1ul) << pkeyshift(pkey)); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (!likely(pkey_execute_disable_supported)) 32862306a36Sopenharmony_ci return; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci current->thread.regs->iamr = old_iamr | new_iamr_bits; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci/* 33462306a36Sopenharmony_ci * Set the access rights in AMR IAMR and UAMOR registers for @pkey to that 33562306a36Sopenharmony_ci * specified in @init_val. 33662306a36Sopenharmony_ci */ 33762306a36Sopenharmony_ciint __arch_set_user_pkey_access(struct task_struct *tsk, int pkey, 33862306a36Sopenharmony_ci unsigned long init_val) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci u64 new_amr_bits = 0x0ul; 34162306a36Sopenharmony_ci u64 new_iamr_bits = 0x0ul; 34262306a36Sopenharmony_ci u64 pkey_bits, uamor_pkey_bits; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* 34562306a36Sopenharmony_ci * Check whether the key is disabled by UAMOR. 34662306a36Sopenharmony_ci */ 34762306a36Sopenharmony_ci pkey_bits = 0x3ul << pkeyshift(pkey); 34862306a36Sopenharmony_ci uamor_pkey_bits = (default_uamor & pkey_bits); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* 35162306a36Sopenharmony_ci * Both the bits in UAMOR corresponding to the key should be set 35262306a36Sopenharmony_ci */ 35362306a36Sopenharmony_ci if (uamor_pkey_bits != pkey_bits) 35462306a36Sopenharmony_ci return -EINVAL; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (init_val & PKEY_DISABLE_EXECUTE) { 35762306a36Sopenharmony_ci if (!pkey_execute_disable_supported) 35862306a36Sopenharmony_ci return -EINVAL; 35962306a36Sopenharmony_ci new_iamr_bits |= IAMR_EX_BIT; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci init_iamr(pkey, new_iamr_bits); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* Set the bits we need in AMR: */ 36462306a36Sopenharmony_ci if (init_val & PKEY_DISABLE_ACCESS) 36562306a36Sopenharmony_ci new_amr_bits |= AMR_RD_BIT | AMR_WR_BIT; 36662306a36Sopenharmony_ci else if (init_val & PKEY_DISABLE_WRITE) 36762306a36Sopenharmony_ci new_amr_bits |= AMR_WR_BIT; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci init_amr(pkey, new_amr_bits); 37062306a36Sopenharmony_ci return 0; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ciint execute_only_pkey(struct mm_struct *mm) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci return mm->context.execute_only_pkey; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic inline bool vma_is_pkey_exec_only(struct vm_area_struct *vma) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci /* Do this check first since the vm_flags should be hot */ 38162306a36Sopenharmony_ci if ((vma->vm_flags & VM_ACCESS_FLAGS) != VM_EXEC) 38262306a36Sopenharmony_ci return false; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci return (vma_pkey(vma) == vma->vm_mm->context.execute_only_pkey); 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci/* 38862306a36Sopenharmony_ci * This should only be called for *plain* mprotect calls. 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_ciint __arch_override_mprotect_pkey(struct vm_area_struct *vma, int prot, 39162306a36Sopenharmony_ci int pkey) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci /* 39462306a36Sopenharmony_ci * If the currently associated pkey is execute-only, but the requested 39562306a36Sopenharmony_ci * protection is not execute-only, move it back to the default pkey. 39662306a36Sopenharmony_ci */ 39762306a36Sopenharmony_ci if (vma_is_pkey_exec_only(vma) && (prot != PROT_EXEC)) 39862306a36Sopenharmony_ci return 0; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci /* 40162306a36Sopenharmony_ci * The requested protection is execute-only. Hence let's use an 40262306a36Sopenharmony_ci * execute-only pkey. 40362306a36Sopenharmony_ci */ 40462306a36Sopenharmony_ci if (prot == PROT_EXEC) { 40562306a36Sopenharmony_ci pkey = execute_only_pkey(vma->vm_mm); 40662306a36Sopenharmony_ci if (pkey > 0) 40762306a36Sopenharmony_ci return pkey; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* Nothing to override. */ 41162306a36Sopenharmony_ci return vma_pkey(vma); 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic bool pkey_access_permitted(int pkey, bool write, bool execute) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci int pkey_shift; 41762306a36Sopenharmony_ci u64 amr; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci pkey_shift = pkeyshift(pkey); 42062306a36Sopenharmony_ci if (execute) 42162306a36Sopenharmony_ci return !(current_thread_iamr() & (IAMR_EX_BIT << pkey_shift)); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci amr = current_thread_amr(); 42462306a36Sopenharmony_ci if (write) 42562306a36Sopenharmony_ci return !(amr & (AMR_WR_BIT << pkey_shift)); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci return !(amr & (AMR_RD_BIT << pkey_shift)); 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cibool arch_pte_access_permitted(u64 pte, bool write, bool execute) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci if (!mmu_has_feature(MMU_FTR_PKEY)) 43362306a36Sopenharmony_ci return true; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci return pkey_access_permitted(pte_to_pkey_bits(pte), write, execute); 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci/* 43962306a36Sopenharmony_ci * We only want to enforce protection keys on the current thread because we 44062306a36Sopenharmony_ci * effectively have no access to AMR/IAMR for other threads or any way to tell 44162306a36Sopenharmony_ci * which AMR/IAMR in a threaded process we could use. 44262306a36Sopenharmony_ci * 44362306a36Sopenharmony_ci * So do not enforce things if the VMA is not from the current mm, or if we are 44462306a36Sopenharmony_ci * in a kernel thread. 44562306a36Sopenharmony_ci */ 44662306a36Sopenharmony_cibool arch_vma_access_permitted(struct vm_area_struct *vma, bool write, 44762306a36Sopenharmony_ci bool execute, bool foreign) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci if (!mmu_has_feature(MMU_FTR_PKEY)) 45062306a36Sopenharmony_ci return true; 45162306a36Sopenharmony_ci /* 45262306a36Sopenharmony_ci * Do not enforce our key-permissions on a foreign vma. 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_ci if (foreign || vma_is_foreign(vma)) 45562306a36Sopenharmony_ci return true; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci return pkey_access_permitted(vma_pkey(vma), write, execute); 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_civoid arch_dup_pkeys(struct mm_struct *oldmm, struct mm_struct *mm) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci if (!mmu_has_feature(MMU_FTR_PKEY)) 46362306a36Sopenharmony_ci return; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* Duplicate the oldmm pkey state in mm: */ 46662306a36Sopenharmony_ci mm_pkey_allocation_map(mm) = mm_pkey_allocation_map(oldmm); 46762306a36Sopenharmony_ci mm->context.execute_only_pkey = oldmm->context.execute_only_pkey; 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci#endif /* CONFIG_PPC_MEM_KEYS */ 471