18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PowerPC Memory Protection Keys management 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2017, Ram Pai, IBM Corporation. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <asm/mman.h> 98c2ecf20Sopenharmony_ci#include <asm/mmu_context.h> 108c2ecf20Sopenharmony_ci#include <asm/mmu.h> 118c2ecf20Sopenharmony_ci#include <asm/setup.h> 128c2ecf20Sopenharmony_ci#include <linux/pkeys.h> 138c2ecf20Sopenharmony_ci#include <linux/of_fdt.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ciint num_pkey; /* Max number of pkeys supported */ 168c2ecf20Sopenharmony_ci/* 178c2ecf20Sopenharmony_ci * Keys marked in the reservation list cannot be allocated by userspace 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ciu32 reserved_allocation_mask __ro_after_init; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* Bits set for the initially allocated keys */ 228c2ecf20Sopenharmony_cistatic u32 initial_allocation_mask __ro_after_init; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* 258c2ecf20Sopenharmony_ci * Even if we allocate keys with sys_pkey_alloc(), we need to make sure 268c2ecf20Sopenharmony_ci * other thread still find the access denied using the same keys. 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_cistatic u64 default_amr = ~0x0UL; 298c2ecf20Sopenharmony_cistatic u64 default_iamr = 0x5555555555555555UL; 308c2ecf20Sopenharmony_ciu64 default_uamor __ro_after_init; 318c2ecf20Sopenharmony_ci/* 328c2ecf20Sopenharmony_ci * Key used to implement PROT_EXEC mmap. Denies READ/WRITE 338c2ecf20Sopenharmony_ci * We pick key 2 because 0 is special key and 1 is reserved as per ISA. 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_cistatic int execute_only_key = 2; 368c2ecf20Sopenharmony_cistatic bool pkey_execute_disable_supported; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define AMR_BITS_PER_PKEY 2 408c2ecf20Sopenharmony_ci#define AMR_RD_BIT 0x1UL 418c2ecf20Sopenharmony_ci#define AMR_WR_BIT 0x2UL 428c2ecf20Sopenharmony_ci#define IAMR_EX_BIT 0x1UL 438c2ecf20Sopenharmony_ci#define PKEY_REG_BITS (sizeof(u64) * 8) 448c2ecf20Sopenharmony_ci#define pkeyshift(pkey) (PKEY_REG_BITS - ((pkey+1) * AMR_BITS_PER_PKEY)) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic int __init dt_scan_storage_keys(unsigned long node, 478c2ecf20Sopenharmony_ci const char *uname, int depth, 488c2ecf20Sopenharmony_ci void *data) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci const char *type = of_get_flat_dt_prop(node, "device_type", NULL); 518c2ecf20Sopenharmony_ci const __be32 *prop; 528c2ecf20Sopenharmony_ci int *pkeys_total = (int *) data; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* We are scanning "cpu" nodes only */ 558c2ecf20Sopenharmony_ci if (type == NULL || strcmp(type, "cpu") != 0) 568c2ecf20Sopenharmony_ci return 0; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci prop = of_get_flat_dt_prop(node, "ibm,processor-storage-keys", NULL); 598c2ecf20Sopenharmony_ci if (!prop) 608c2ecf20Sopenharmony_ci return 0; 618c2ecf20Sopenharmony_ci *pkeys_total = be32_to_cpu(prop[0]); 628c2ecf20Sopenharmony_ci return 1; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int scan_pkey_feature(void) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci int ret; 688c2ecf20Sopenharmony_ci int pkeys_total = 0; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* 718c2ecf20Sopenharmony_ci * Pkey is not supported with Radix translation. 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_ci if (early_radix_enabled()) 748c2ecf20Sopenharmony_ci return 0; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci ret = of_scan_flat_dt(dt_scan_storage_keys, &pkeys_total); 778c2ecf20Sopenharmony_ci if (ret == 0) { 788c2ecf20Sopenharmony_ci /* 798c2ecf20Sopenharmony_ci * Let's assume 32 pkeys on P8/P9 bare metal, if its not defined by device 808c2ecf20Sopenharmony_ci * tree. We make this exception since some version of skiboot forgot to 818c2ecf20Sopenharmony_ci * expose this property on power8/9. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci if (!firmware_has_feature(FW_FEATURE_LPAR)) { 848c2ecf20Sopenharmony_ci unsigned long pvr = mfspr(SPRN_PVR); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (PVR_VER(pvr) == PVR_POWER8 || PVR_VER(pvr) == PVR_POWER8E || 878c2ecf20Sopenharmony_ci PVR_VER(pvr) == PVR_POWER8NVL || PVR_VER(pvr) == PVR_POWER9) 888c2ecf20Sopenharmony_ci pkeys_total = 32; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* 938c2ecf20Sopenharmony_ci * Adjust the upper limit, based on the number of bits supported by 948c2ecf20Sopenharmony_ci * arch-neutral code. 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_ci pkeys_total = min_t(int, pkeys_total, 978c2ecf20Sopenharmony_ci ((ARCH_VM_PKEY_FLAGS >> VM_PKEY_SHIFT) + 1)); 988c2ecf20Sopenharmony_ci return pkeys_total; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_civoid __init pkey_early_init_devtree(void) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci int pkeys_total, i; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* 1068c2ecf20Sopenharmony_ci * We define PKEY_DISABLE_EXECUTE in addition to the arch-neutral 1078c2ecf20Sopenharmony_ci * generic defines for PKEY_DISABLE_ACCESS and PKEY_DISABLE_WRITE. 1088c2ecf20Sopenharmony_ci * Ensure that the bits a distinct. 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_ci BUILD_BUG_ON(PKEY_DISABLE_EXECUTE & 1118c2ecf20Sopenharmony_ci (PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE)); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* 1148c2ecf20Sopenharmony_ci * pkey_to_vmflag_bits() assumes that the pkey bits are contiguous 1158c2ecf20Sopenharmony_ci * in the vmaflag. Make sure that is really the case. 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_ci BUILD_BUG_ON(__builtin_clzl(ARCH_VM_PKEY_FLAGS >> VM_PKEY_SHIFT) + 1188c2ecf20Sopenharmony_ci __builtin_popcountl(ARCH_VM_PKEY_FLAGS >> VM_PKEY_SHIFT) 1198c2ecf20Sopenharmony_ci != (sizeof(u64) * BITS_PER_BYTE)); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* 1228c2ecf20Sopenharmony_ci * Only P7 and above supports SPRN_AMR update with MSR[PR] = 1 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ci if (!early_cpu_has_feature(CPU_FTR_ARCH_206)) 1258c2ecf20Sopenharmony_ci return; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* scan the device tree for pkey feature */ 1288c2ecf20Sopenharmony_ci pkeys_total = scan_pkey_feature(); 1298c2ecf20Sopenharmony_ci if (!pkeys_total) 1308c2ecf20Sopenharmony_ci goto out; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* Allow all keys to be modified by default */ 1338c2ecf20Sopenharmony_ci default_uamor = ~0x0UL; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci cur_cpu_spec->mmu_features |= MMU_FTR_PKEY; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* 1388c2ecf20Sopenharmony_ci * The device tree cannot be relied to indicate support for 1398c2ecf20Sopenharmony_ci * execute_disable support. Instead we use a PVR check. 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_ci if (pvr_version_is(PVR_POWER7) || pvr_version_is(PVR_POWER7p)) 1428c2ecf20Sopenharmony_ci pkey_execute_disable_supported = false; 1438c2ecf20Sopenharmony_ci else 1448c2ecf20Sopenharmony_ci pkey_execute_disable_supported = true; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_4K_PAGES 1478c2ecf20Sopenharmony_ci /* 1488c2ecf20Sopenharmony_ci * The OS can manage only 8 pkeys due to its inability to represent them 1498c2ecf20Sopenharmony_ci * in the Linux 4K PTE. Mark all other keys reserved. 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_ci num_pkey = min(8, pkeys_total); 1528c2ecf20Sopenharmony_ci#else 1538c2ecf20Sopenharmony_ci num_pkey = pkeys_total; 1548c2ecf20Sopenharmony_ci#endif 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (unlikely(num_pkey <= execute_only_key) || !pkey_execute_disable_supported) { 1578c2ecf20Sopenharmony_ci /* 1588c2ecf20Sopenharmony_ci * Insufficient number of keys to support 1598c2ecf20Sopenharmony_ci * execute only key. Mark it unavailable. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_ci execute_only_key = -1; 1628c2ecf20Sopenharmony_ci } else { 1638c2ecf20Sopenharmony_ci /* 1648c2ecf20Sopenharmony_ci * Mark the execute_only_pkey as not available for 1658c2ecf20Sopenharmony_ci * user allocation via pkey_alloc. 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_ci reserved_allocation_mask |= (0x1 << execute_only_key); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* 1708c2ecf20Sopenharmony_ci * Deny READ/WRITE for execute_only_key. 1718c2ecf20Sopenharmony_ci * Allow execute in IAMR. 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_ci default_amr |= (0x3ul << pkeyshift(execute_only_key)); 1748c2ecf20Sopenharmony_ci default_iamr &= ~(0x1ul << pkeyshift(execute_only_key)); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* 1778c2ecf20Sopenharmony_ci * Clear the uamor bits for this key. 1788c2ecf20Sopenharmony_ci */ 1798c2ecf20Sopenharmony_ci default_uamor &= ~(0x3ul << pkeyshift(execute_only_key)); 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* 1838c2ecf20Sopenharmony_ci * Allow access for only key 0. And prevent any other modification. 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_ci default_amr &= ~(0x3ul << pkeyshift(0)); 1868c2ecf20Sopenharmony_ci default_iamr &= ~(0x1ul << pkeyshift(0)); 1878c2ecf20Sopenharmony_ci default_uamor &= ~(0x3ul << pkeyshift(0)); 1888c2ecf20Sopenharmony_ci /* 1898c2ecf20Sopenharmony_ci * key 0 is special in that we want to consider it an allocated 1908c2ecf20Sopenharmony_ci * key which is preallocated. We don't allow changing AMR bits 1918c2ecf20Sopenharmony_ci * w.r.t key 0. But one can pkey_free(key0) 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_ci initial_allocation_mask |= (0x1 << 0); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* 1968c2ecf20Sopenharmony_ci * key 1 is recommended not to be used. PowerISA(3.0) page 1015, 1978c2ecf20Sopenharmony_ci * programming note. 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_ci reserved_allocation_mask |= (0x1 << 1); 2008c2ecf20Sopenharmony_ci default_uamor &= ~(0x3ul << pkeyshift(1)); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* 2038c2ecf20Sopenharmony_ci * Prevent the usage of OS reserved keys. Update UAMOR 2048c2ecf20Sopenharmony_ci * for those keys. Also mark the rest of the bits in the 2058c2ecf20Sopenharmony_ci * 32 bit mask as reserved. 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_ci for (i = num_pkey; i < 32 ; i++) { 2088c2ecf20Sopenharmony_ci reserved_allocation_mask |= (0x1 << i); 2098c2ecf20Sopenharmony_ci default_uamor &= ~(0x3ul << pkeyshift(i)); 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci /* 2128c2ecf20Sopenharmony_ci * Prevent the allocation of reserved keys too. 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci initial_allocation_mask |= reserved_allocation_mask; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci pr_info("Enabling pkeys with max key count %d\n", num_pkey); 2178c2ecf20Sopenharmony_ciout: 2188c2ecf20Sopenharmony_ci /* 2198c2ecf20Sopenharmony_ci * Setup uamor on boot cpu 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_ci mtspr(SPRN_UAMOR, default_uamor); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_civoid pkey_mm_init(struct mm_struct *mm) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci if (!mmu_has_feature(MMU_FTR_PKEY)) 2298c2ecf20Sopenharmony_ci return; 2308c2ecf20Sopenharmony_ci mm_pkey_allocation_map(mm) = initial_allocation_mask; 2318c2ecf20Sopenharmony_ci mm->context.execute_only_pkey = execute_only_key; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic inline u64 read_amr(void) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci return mfspr(SPRN_AMR); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic inline void write_amr(u64 value) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci mtspr(SPRN_AMR, value); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic inline u64 read_iamr(void) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci if (!likely(pkey_execute_disable_supported)) 2478c2ecf20Sopenharmony_ci return 0x0UL; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci return mfspr(SPRN_IAMR); 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic inline void write_iamr(u64 value) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci if (!likely(pkey_execute_disable_supported)) 2558c2ecf20Sopenharmony_ci return; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci mtspr(SPRN_IAMR, value); 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic inline void init_amr(int pkey, u8 init_bits) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci u64 new_amr_bits = (((u64)init_bits & 0x3UL) << pkeyshift(pkey)); 2638c2ecf20Sopenharmony_ci u64 old_amr = read_amr() & ~((u64)(0x3ul) << pkeyshift(pkey)); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci write_amr(old_amr | new_amr_bits); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic inline void init_iamr(int pkey, u8 init_bits) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci u64 new_iamr_bits = (((u64)init_bits & 0x1UL) << pkeyshift(pkey)); 2718c2ecf20Sopenharmony_ci u64 old_iamr = read_iamr() & ~((u64)(0x1ul) << pkeyshift(pkey)); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci write_iamr(old_iamr | new_iamr_bits); 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci/* 2778c2ecf20Sopenharmony_ci * Set the access rights in AMR IAMR and UAMOR registers for @pkey to that 2788c2ecf20Sopenharmony_ci * specified in @init_val. 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_ciint __arch_set_user_pkey_access(struct task_struct *tsk, int pkey, 2818c2ecf20Sopenharmony_ci unsigned long init_val) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci u64 new_amr_bits = 0x0ul; 2848c2ecf20Sopenharmony_ci u64 new_iamr_bits = 0x0ul; 2858c2ecf20Sopenharmony_ci u64 pkey_bits, uamor_pkey_bits; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* 2888c2ecf20Sopenharmony_ci * Check whether the key is disabled by UAMOR. 2898c2ecf20Sopenharmony_ci */ 2908c2ecf20Sopenharmony_ci pkey_bits = 0x3ul << pkeyshift(pkey); 2918c2ecf20Sopenharmony_ci uamor_pkey_bits = (default_uamor & pkey_bits); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci /* 2948c2ecf20Sopenharmony_ci * Both the bits in UAMOR corresponding to the key should be set 2958c2ecf20Sopenharmony_ci */ 2968c2ecf20Sopenharmony_ci if (uamor_pkey_bits != pkey_bits) 2978c2ecf20Sopenharmony_ci return -EINVAL; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (init_val & PKEY_DISABLE_EXECUTE) { 3008c2ecf20Sopenharmony_ci if (!pkey_execute_disable_supported) 3018c2ecf20Sopenharmony_ci return -EINVAL; 3028c2ecf20Sopenharmony_ci new_iamr_bits |= IAMR_EX_BIT; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci init_iamr(pkey, new_iamr_bits); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* Set the bits we need in AMR: */ 3078c2ecf20Sopenharmony_ci if (init_val & PKEY_DISABLE_ACCESS) 3088c2ecf20Sopenharmony_ci new_amr_bits |= AMR_RD_BIT | AMR_WR_BIT; 3098c2ecf20Sopenharmony_ci else if (init_val & PKEY_DISABLE_WRITE) 3108c2ecf20Sopenharmony_ci new_amr_bits |= AMR_WR_BIT; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci init_amr(pkey, new_amr_bits); 3138c2ecf20Sopenharmony_ci return 0; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_civoid thread_pkey_regs_save(struct thread_struct *thread) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci if (!mmu_has_feature(MMU_FTR_PKEY)) 3198c2ecf20Sopenharmony_ci return; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* 3228c2ecf20Sopenharmony_ci * TODO: Skip saving registers if @thread hasn't used any keys yet. 3238c2ecf20Sopenharmony_ci */ 3248c2ecf20Sopenharmony_ci thread->amr = read_amr(); 3258c2ecf20Sopenharmony_ci thread->iamr = read_iamr(); 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_civoid thread_pkey_regs_restore(struct thread_struct *new_thread, 3298c2ecf20Sopenharmony_ci struct thread_struct *old_thread) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci if (!mmu_has_feature(MMU_FTR_PKEY)) 3328c2ecf20Sopenharmony_ci return; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (old_thread->amr != new_thread->amr) 3358c2ecf20Sopenharmony_ci write_amr(new_thread->amr); 3368c2ecf20Sopenharmony_ci if (old_thread->iamr != new_thread->iamr) 3378c2ecf20Sopenharmony_ci write_iamr(new_thread->iamr); 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_civoid thread_pkey_regs_init(struct thread_struct *thread) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci if (!mmu_has_feature(MMU_FTR_PKEY)) 3438c2ecf20Sopenharmony_ci return; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci thread->amr = default_amr; 3468c2ecf20Sopenharmony_ci thread->iamr = default_iamr; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci write_amr(default_amr); 3498c2ecf20Sopenharmony_ci write_iamr(default_iamr); 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ciint execute_only_pkey(struct mm_struct *mm) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci return mm->context.execute_only_pkey; 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic inline bool vma_is_pkey_exec_only(struct vm_area_struct *vma) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci /* Do this check first since the vm_flags should be hot */ 3608c2ecf20Sopenharmony_ci if ((vma->vm_flags & VM_ACCESS_FLAGS) != VM_EXEC) 3618c2ecf20Sopenharmony_ci return false; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci return (vma_pkey(vma) == vma->vm_mm->context.execute_only_pkey); 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci/* 3678c2ecf20Sopenharmony_ci * This should only be called for *plain* mprotect calls. 3688c2ecf20Sopenharmony_ci */ 3698c2ecf20Sopenharmony_ciint __arch_override_mprotect_pkey(struct vm_area_struct *vma, int prot, 3708c2ecf20Sopenharmony_ci int pkey) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci /* 3738c2ecf20Sopenharmony_ci * If the currently associated pkey is execute-only, but the requested 3748c2ecf20Sopenharmony_ci * protection is not execute-only, move it back to the default pkey. 3758c2ecf20Sopenharmony_ci */ 3768c2ecf20Sopenharmony_ci if (vma_is_pkey_exec_only(vma) && (prot != PROT_EXEC)) 3778c2ecf20Sopenharmony_ci return 0; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* 3808c2ecf20Sopenharmony_ci * The requested protection is execute-only. Hence let's use an 3818c2ecf20Sopenharmony_ci * execute-only pkey. 3828c2ecf20Sopenharmony_ci */ 3838c2ecf20Sopenharmony_ci if (prot == PROT_EXEC) { 3848c2ecf20Sopenharmony_ci pkey = execute_only_pkey(vma->vm_mm); 3858c2ecf20Sopenharmony_ci if (pkey > 0) 3868c2ecf20Sopenharmony_ci return pkey; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci /* Nothing to override. */ 3908c2ecf20Sopenharmony_ci return vma_pkey(vma); 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistatic bool pkey_access_permitted(int pkey, bool write, bool execute) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci int pkey_shift; 3968c2ecf20Sopenharmony_ci u64 amr; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci pkey_shift = pkeyshift(pkey); 3998c2ecf20Sopenharmony_ci if (execute) 4008c2ecf20Sopenharmony_ci return !(read_iamr() & (IAMR_EX_BIT << pkey_shift)); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci amr = read_amr(); 4038c2ecf20Sopenharmony_ci if (write) 4048c2ecf20Sopenharmony_ci return !(amr & (AMR_WR_BIT << pkey_shift)); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci return !(amr & (AMR_RD_BIT << pkey_shift)); 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cibool arch_pte_access_permitted(u64 pte, bool write, bool execute) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci if (!mmu_has_feature(MMU_FTR_PKEY)) 4128c2ecf20Sopenharmony_ci return true; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci return pkey_access_permitted(pte_to_pkey_bits(pte), write, execute); 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci/* 4188c2ecf20Sopenharmony_ci * We only want to enforce protection keys on the current thread because we 4198c2ecf20Sopenharmony_ci * effectively have no access to AMR/IAMR for other threads or any way to tell 4208c2ecf20Sopenharmony_ci * which AMR/IAMR in a threaded process we could use. 4218c2ecf20Sopenharmony_ci * 4228c2ecf20Sopenharmony_ci * So do not enforce things if the VMA is not from the current mm, or if we are 4238c2ecf20Sopenharmony_ci * in a kernel thread. 4248c2ecf20Sopenharmony_ci */ 4258c2ecf20Sopenharmony_cibool arch_vma_access_permitted(struct vm_area_struct *vma, bool write, 4268c2ecf20Sopenharmony_ci bool execute, bool foreign) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci if (!mmu_has_feature(MMU_FTR_PKEY)) 4298c2ecf20Sopenharmony_ci return true; 4308c2ecf20Sopenharmony_ci /* 4318c2ecf20Sopenharmony_ci * Do not enforce our key-permissions on a foreign vma. 4328c2ecf20Sopenharmony_ci */ 4338c2ecf20Sopenharmony_ci if (foreign || vma_is_foreign(vma)) 4348c2ecf20Sopenharmony_ci return true; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci return pkey_access_permitted(vma_pkey(vma), write, execute); 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_civoid arch_dup_pkeys(struct mm_struct *oldmm, struct mm_struct *mm) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci if (!mmu_has_feature(MMU_FTR_PKEY)) 4428c2ecf20Sopenharmony_ci return; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* Duplicate the oldmm pkey state in mm: */ 4458c2ecf20Sopenharmony_ci mm_pkey_allocation_map(mm) = mm_pkey_allocation_map(oldmm); 4468c2ecf20Sopenharmony_ci mm->context.execute_only_pkey = oldmm->context.execute_only_pkey; 4478c2ecf20Sopenharmony_ci} 448