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#ifndef _ASM_POWERPC_KEYS_H
98c2ecf20Sopenharmony_ci#define _ASM_POWERPC_KEYS_H
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/jump_label.h>
128c2ecf20Sopenharmony_ci#include <asm/firmware.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ciextern int num_pkey;
158c2ecf20Sopenharmony_ciextern u32 reserved_allocation_mask; /* bits set for reserved keys */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define ARCH_VM_PKEY_FLAGS (VM_PKEY_BIT0 | VM_PKEY_BIT1 | VM_PKEY_BIT2 | \
188c2ecf20Sopenharmony_ci			    VM_PKEY_BIT3 | VM_PKEY_BIT4)
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/* Override any generic PKEY permission defines */
218c2ecf20Sopenharmony_ci#define PKEY_DISABLE_EXECUTE   0x4
228c2ecf20Sopenharmony_ci#define PKEY_ACCESS_MASK       (PKEY_DISABLE_ACCESS | \
238c2ecf20Sopenharmony_ci				PKEY_DISABLE_WRITE  | \
248c2ecf20Sopenharmony_ci				PKEY_DISABLE_EXECUTE)
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_BOOK3S_64
278c2ecf20Sopenharmony_ci#include <asm/book3s/64/pkeys.h>
288c2ecf20Sopenharmony_ci#else
298c2ecf20Sopenharmony_ci#error "Not supported"
308c2ecf20Sopenharmony_ci#endif
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic inline u64 pkey_to_vmflag_bits(u16 pkey)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	return (((u64)pkey << VM_PKEY_SHIFT) & ARCH_VM_PKEY_FLAGS);
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic inline int vma_pkey(struct vm_area_struct *vma)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	if (!mmu_has_feature(MMU_FTR_PKEY))
418c2ecf20Sopenharmony_ci		return 0;
428c2ecf20Sopenharmony_ci	return (vma->vm_flags & ARCH_VM_PKEY_FLAGS) >> VM_PKEY_SHIFT;
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic inline int arch_max_pkey(void)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	return num_pkey;
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#define pkey_alloc_mask(pkey) (0x1 << pkey)
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#define mm_pkey_allocation_map(mm) (mm->context.pkey_allocation_map)
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#define __mm_pkey_allocated(mm, pkey) {	\
558c2ecf20Sopenharmony_ci	mm_pkey_allocation_map(mm) |= pkey_alloc_mask(pkey); \
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci#define __mm_pkey_free(mm, pkey) {	\
598c2ecf20Sopenharmony_ci	mm_pkey_allocation_map(mm) &= ~pkey_alloc_mask(pkey);	\
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#define __mm_pkey_is_allocated(mm, pkey)	\
638c2ecf20Sopenharmony_ci	(mm_pkey_allocation_map(mm) & pkey_alloc_mask(pkey))
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#define __mm_pkey_is_reserved(pkey) (reserved_allocation_mask & \
668c2ecf20Sopenharmony_ci				       pkey_alloc_mask(pkey))
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic inline bool mm_pkey_is_allocated(struct mm_struct *mm, int pkey)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	if (pkey < 0 || pkey >= arch_max_pkey())
718c2ecf20Sopenharmony_ci		return false;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	/* Reserved keys are never allocated. */
748c2ecf20Sopenharmony_ci	if (__mm_pkey_is_reserved(pkey))
758c2ecf20Sopenharmony_ci		return false;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	return __mm_pkey_is_allocated(mm, pkey);
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/*
818c2ecf20Sopenharmony_ci * Returns a positive, 5-bit key on success, or -1 on failure.
828c2ecf20Sopenharmony_ci * Relies on the mmap_lock to protect against concurrency in mm_pkey_alloc() and
838c2ecf20Sopenharmony_ci * mm_pkey_free().
848c2ecf20Sopenharmony_ci */
858c2ecf20Sopenharmony_cistatic inline int mm_pkey_alloc(struct mm_struct *mm)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	/*
888c2ecf20Sopenharmony_ci	 * Note: this is the one and only place we make sure that the pkey is
898c2ecf20Sopenharmony_ci	 * valid as far as the hardware is concerned. The rest of the kernel
908c2ecf20Sopenharmony_ci	 * trusts that only good, valid pkeys come out of here.
918c2ecf20Sopenharmony_ci	 */
928c2ecf20Sopenharmony_ci	u32 all_pkeys_mask = (u32)(~(0x0));
938c2ecf20Sopenharmony_ci	int ret;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (!mmu_has_feature(MMU_FTR_PKEY))
968c2ecf20Sopenharmony_ci		return -1;
978c2ecf20Sopenharmony_ci	/*
988c2ecf20Sopenharmony_ci	 * Are we out of pkeys? We must handle this specially because ffz()
998c2ecf20Sopenharmony_ci	 * behavior is undefined if there are no zeros.
1008c2ecf20Sopenharmony_ci	 */
1018c2ecf20Sopenharmony_ci	if (mm_pkey_allocation_map(mm) == all_pkeys_mask)
1028c2ecf20Sopenharmony_ci		return -1;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	ret = ffz((u32)mm_pkey_allocation_map(mm));
1058c2ecf20Sopenharmony_ci	__mm_pkey_allocated(mm, ret);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	return ret;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic inline int mm_pkey_free(struct mm_struct *mm, int pkey)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	if (!mmu_has_feature(MMU_FTR_PKEY))
1138c2ecf20Sopenharmony_ci		return -1;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	if (!mm_pkey_is_allocated(mm, pkey))
1168c2ecf20Sopenharmony_ci		return -EINVAL;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	__mm_pkey_free(mm, pkey);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	return 0;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/*
1248c2ecf20Sopenharmony_ci * Try to dedicate one of the protection keys to be used as an
1258c2ecf20Sopenharmony_ci * execute-only protection key.
1268c2ecf20Sopenharmony_ci */
1278c2ecf20Sopenharmony_ciextern int execute_only_pkey(struct mm_struct *mm);
1288c2ecf20Sopenharmony_ciextern int __arch_override_mprotect_pkey(struct vm_area_struct *vma,
1298c2ecf20Sopenharmony_ci					 int prot, int pkey);
1308c2ecf20Sopenharmony_cistatic inline int arch_override_mprotect_pkey(struct vm_area_struct *vma,
1318c2ecf20Sopenharmony_ci					      int prot, int pkey)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	if (!mmu_has_feature(MMU_FTR_PKEY))
1348c2ecf20Sopenharmony_ci		return 0;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	/*
1378c2ecf20Sopenharmony_ci	 * Is this an mprotect_pkey() call? If so, never override the value that
1388c2ecf20Sopenharmony_ci	 * came from the user.
1398c2ecf20Sopenharmony_ci	 */
1408c2ecf20Sopenharmony_ci	if (pkey != -1)
1418c2ecf20Sopenharmony_ci		return pkey;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	return __arch_override_mprotect_pkey(vma, prot, pkey);
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ciextern int __arch_set_user_pkey_access(struct task_struct *tsk, int pkey,
1478c2ecf20Sopenharmony_ci				       unsigned long init_val);
1488c2ecf20Sopenharmony_cistatic inline int arch_set_user_pkey_access(struct task_struct *tsk, int pkey,
1498c2ecf20Sopenharmony_ci					    unsigned long init_val)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	if (!mmu_has_feature(MMU_FTR_PKEY))
1528c2ecf20Sopenharmony_ci		return -EINVAL;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	/*
1558c2ecf20Sopenharmony_ci	 * userspace should not change pkey-0 permissions.
1568c2ecf20Sopenharmony_ci	 * pkey-0 is associated with every page in the kernel.
1578c2ecf20Sopenharmony_ci	 * If userspace denies any permission on pkey-0, the
1588c2ecf20Sopenharmony_ci	 * kernel cannot operate.
1598c2ecf20Sopenharmony_ci	 */
1608c2ecf20Sopenharmony_ci	if (pkey == 0)
1618c2ecf20Sopenharmony_ci		return init_val ? -EINVAL : 0;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	return __arch_set_user_pkey_access(tsk, pkey, init_val);
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic inline bool arch_pkeys_enabled(void)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	return mmu_has_feature(MMU_FTR_PKEY);
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ciextern void pkey_mm_init(struct mm_struct *mm);
1728c2ecf20Sopenharmony_ciextern bool arch_supports_pkeys(int cap);
1738c2ecf20Sopenharmony_ciextern unsigned int arch_usable_pkeys(void);
1748c2ecf20Sopenharmony_ciextern void thread_pkey_regs_save(struct thread_struct *thread);
1758c2ecf20Sopenharmony_ciextern void thread_pkey_regs_restore(struct thread_struct *new_thread,
1768c2ecf20Sopenharmony_ci				     struct thread_struct *old_thread);
1778c2ecf20Sopenharmony_ciextern void thread_pkey_regs_init(struct thread_struct *thread);
1788c2ecf20Sopenharmony_ci#endif /*_ASM_POWERPC_KEYS_H */
179