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