162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci#ifndef _ASM_POWERPC_KUP_BOOKE_H_
362306a36Sopenharmony_ci#define _ASM_POWERPC_KUP_BOOKE_H_
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <asm/bug.h>
662306a36Sopenharmony_ci#include <asm/mmu.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#ifdef CONFIG_PPC_KUAP
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#ifdef __ASSEMBLY__
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci.macro kuap_check_amr	gpr1, gpr2
1362306a36Sopenharmony_ci.endm
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#else
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/sched.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <asm/reg.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic __always_inline void __kuap_lock(void)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	mtspr(SPRN_PID, 0);
2462306a36Sopenharmony_ci	isync();
2562306a36Sopenharmony_ci}
2662306a36Sopenharmony_ci#define __kuap_lock __kuap_lock
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic __always_inline void __kuap_save_and_lock(struct pt_regs *regs)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	regs->kuap = mfspr(SPRN_PID);
3162306a36Sopenharmony_ci	mtspr(SPRN_PID, 0);
3262306a36Sopenharmony_ci	isync();
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci#define __kuap_save_and_lock __kuap_save_and_lock
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic __always_inline void kuap_user_restore(struct pt_regs *regs)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	if (kuap_is_disabled())
3962306a36Sopenharmony_ci		return;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	mtspr(SPRN_PID, current->thread.pid);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	/* Context synchronisation is performed by rfi */
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic __always_inline void __kuap_kernel_restore(struct pt_regs *regs, unsigned long kuap)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	if (regs->kuap)
4962306a36Sopenharmony_ci		mtspr(SPRN_PID, current->thread.pid);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	/* Context synchronisation is performed by rfi */
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#ifdef CONFIG_PPC_KUAP_DEBUG
5562306a36Sopenharmony_cistatic __always_inline unsigned long __kuap_get_and_assert_locked(void)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	WARN_ON_ONCE(mfspr(SPRN_PID));
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	return 0;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci#define __kuap_get_and_assert_locked __kuap_get_and_assert_locked
6262306a36Sopenharmony_ci#endif
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic __always_inline void uaccess_begin_booke(unsigned long val)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	asm(ASM_MMU_FTR_IFSET("mtspr %0, %1; isync", "", %2) : :
6762306a36Sopenharmony_ci	    "i"(SPRN_PID), "r"(val), "i"(MMU_FTR_KUAP) : "memory");
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic __always_inline void uaccess_end_booke(void)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	asm(ASM_MMU_FTR_IFSET("mtspr %0, %1; isync", "", %2) : :
7362306a36Sopenharmony_ci	    "i"(SPRN_PID), "r"(0), "i"(MMU_FTR_KUAP) : "memory");
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic __always_inline void allow_user_access(void __user *to, const void __user *from,
7762306a36Sopenharmony_ci					      unsigned long size, unsigned long dir)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	uaccess_begin_booke(current->thread.pid);
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic __always_inline void prevent_user_access(unsigned long dir)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	uaccess_end_booke();
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic __always_inline unsigned long prevent_user_access_return(void)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	unsigned long flags = mfspr(SPRN_PID);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	uaccess_end_booke();
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	return flags;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic __always_inline void restore_user_access(unsigned long flags)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	if (flags)
9962306a36Sopenharmony_ci		uaccess_begin_booke(current->thread.pid);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic __always_inline bool
10362306a36Sopenharmony_ci__bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	return !regs->kuap;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci#endif /* !__ASSEMBLY__ */
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci#endif /* CONFIG_PPC_KUAP */
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci#endif /* _ASM_POWERPC_KUP_BOOKE_H_ */
113