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