131ed2b13SChristophe Leroy /* SPDX-License-Identifier: GPL-2.0 */ 231ed2b13SChristophe Leroy #ifndef _ASM_POWERPC_BOOK3S_32_KUP_H 331ed2b13SChristophe Leroy #define _ASM_POWERPC_BOOK3S_32_KUP_H 431ed2b13SChristophe Leroy 574016701SChristophe Leroy #include <asm/bug.h> 631ed2b13SChristophe Leroy #include <asm/book3s/32/mmu-hash.h> 7ef486bf4SChristophe Leroy #include <asm/mmu.h> 8ef486bf4SChristophe Leroy #include <asm/synch.h> 931ed2b13SChristophe Leroy 10c1672883SChristophe Leroy #ifndef __ASSEMBLY__ 11a68c31fcSChristophe Leroy 1250d2f104SChristophe Leroy #include <linux/jump_label.h> 1350d2f104SChristophe Leroy 146b4d6300SChristophe Leroy extern struct static_key_false disable_kuap_key; 1550d2f104SChristophe Leroy 16863771a2SChristophe Leroy static __always_inline bool kuap_is_disabled(void) 17863771a2SChristophe Leroy { 186b4d6300SChristophe Leroy return !IS_ENABLED(CONFIG_PPC_KUAP) || static_branch_unlikely(&disable_kuap_key); 19863771a2SChristophe Leroy } 20863771a2SChristophe Leroy 2191bb3082SChristophe Leroy static __always_inline bool kuep_is_disabled(void) 2291bb3082SChristophe Leroy { 23df415cd7SChristophe Leroy return !IS_ENABLED(CONFIG_PPC_KUEP); 2491bb3082SChristophe Leroy } 2591bb3082SChristophe Leroy 26a68c31fcSChristophe Leroy #ifdef CONFIG_PPC_KUAP 27a68c31fcSChristophe Leroy 28a68c31fcSChristophe Leroy #include <linux/sched.h> 29a68c31fcSChristophe Leroy 3016132529SChristophe Leroy #define KUAP_NONE (~0UL) 3116132529SChristophe Leroy #define KUAP_ALL (~1UL) 3216132529SChristophe Leroy 3316132529SChristophe Leroy static inline void kuap_lock_one(unsigned long addr) 34a68c31fcSChristophe Leroy { 3516132529SChristophe Leroy mtsr(mfsr(addr) | SR_KS, addr); 36179ae57dSChristophe Leroy isync(); /* Context sync required after mtsr() */ 37a68c31fcSChristophe Leroy } 38a68c31fcSChristophe Leroy 3916132529SChristophe Leroy static inline void kuap_unlock_one(unsigned long addr) 4016132529SChristophe Leroy { 4116132529SChristophe Leroy mtsr(mfsr(addr) & ~SR_KS, addr); 4216132529SChristophe Leroy isync(); /* Context sync required after mtsr() */ 4316132529SChristophe Leroy } 4416132529SChristophe Leroy 4516132529SChristophe Leroy static inline void kuap_lock_all(void) 4616132529SChristophe Leroy { 4716132529SChristophe Leroy update_user_segments(mfsr(0) | SR_KS); 4816132529SChristophe Leroy isync(); /* Context sync required after mtsr() */ 4916132529SChristophe Leroy } 5016132529SChristophe Leroy 5116132529SChristophe Leroy static inline void kuap_unlock_all(void) 5216132529SChristophe Leroy { 5316132529SChristophe Leroy update_user_segments(mfsr(0) & ~SR_KS); 5416132529SChristophe Leroy isync(); /* Context sync required after mtsr() */ 5516132529SChristophe Leroy } 5616132529SChristophe Leroy 5716132529SChristophe Leroy void kuap_lock_all_ool(void); 5816132529SChristophe Leroy void kuap_unlock_all_ool(void); 5916132529SChristophe Leroy 6016132529SChristophe Leroy static inline void kuap_lock(unsigned long addr, bool ool) 6116132529SChristophe Leroy { 6216132529SChristophe Leroy if (likely(addr != KUAP_ALL)) 6316132529SChristophe Leroy kuap_lock_one(addr); 6416132529SChristophe Leroy else if (!ool) 6516132529SChristophe Leroy kuap_lock_all(); 6616132529SChristophe Leroy else 6716132529SChristophe Leroy kuap_lock_all_ool(); 6816132529SChristophe Leroy } 6916132529SChristophe Leroy 7016132529SChristophe Leroy static inline void kuap_unlock(unsigned long addr, bool ool) 7116132529SChristophe Leroy { 7216132529SChristophe Leroy if (likely(addr != KUAP_ALL)) 7316132529SChristophe Leroy kuap_unlock_one(addr); 7416132529SChristophe Leroy else if (!ool) 7516132529SChristophe Leroy kuap_unlock_all(); 7616132529SChristophe Leroy else 7716132529SChristophe Leroy kuap_unlock_all_ool(); 7816132529SChristophe Leroy } 7916132529SChristophe Leroy 80*ba454f9cSChristophe Leroy static inline void __kuap_save_and_lock(struct pt_regs *regs) 8121eb58aeSChristophe Leroy { 8221eb58aeSChristophe Leroy unsigned long kuap = current->thread.kuap; 8321eb58aeSChristophe Leroy 846b4d6300SChristophe Leroy if (kuap_is_disabled()) 856b4d6300SChristophe Leroy return; 866b4d6300SChristophe Leroy 8721eb58aeSChristophe Leroy regs->kuap = kuap; 8816132529SChristophe Leroy if (unlikely(kuap == KUAP_NONE)) 8921eb58aeSChristophe Leroy return; 9021eb58aeSChristophe Leroy 9116132529SChristophe Leroy current->thread.kuap = KUAP_NONE; 9216132529SChristophe Leroy kuap_lock(kuap, false); 9321eb58aeSChristophe Leroy } 9421eb58aeSChristophe Leroy 9521eb58aeSChristophe Leroy static inline void kuap_user_restore(struct pt_regs *regs) 9621eb58aeSChristophe Leroy { 9721eb58aeSChristophe Leroy } 9821eb58aeSChristophe Leroy 99*ba454f9cSChristophe Leroy static inline void __kuap_kernel_restore(struct pt_regs *regs, unsigned long kuap) 10021eb58aeSChristophe Leroy { 1016b4d6300SChristophe Leroy if (kuap_is_disabled()) 1026b4d6300SChristophe Leroy return; 1036b4d6300SChristophe Leroy 104d93f9e23SChristophe Leroy if (unlikely(kuap != KUAP_NONE)) { 105d93f9e23SChristophe Leroy current->thread.kuap = KUAP_NONE; 106d93f9e23SChristophe Leroy kuap_lock(kuap, false); 107d93f9e23SChristophe Leroy } 108d93f9e23SChristophe Leroy 109d93f9e23SChristophe Leroy if (likely(regs->kuap == KUAP_NONE)) 110d93f9e23SChristophe Leroy return; 111d93f9e23SChristophe Leroy 11221eb58aeSChristophe Leroy current->thread.kuap = regs->kuap; 11321eb58aeSChristophe Leroy 11416132529SChristophe Leroy kuap_unlock(regs->kuap, false); 11521eb58aeSChristophe Leroy } 11621eb58aeSChristophe Leroy 117*ba454f9cSChristophe Leroy static inline unsigned long __kuap_get_and_assert_locked(void) 11821eb58aeSChristophe Leroy { 11921eb58aeSChristophe Leroy unsigned long kuap = current->thread.kuap; 12021eb58aeSChristophe Leroy 1216b4d6300SChristophe Leroy if (kuap_is_disabled()) 12216132529SChristophe Leroy return KUAP_NONE; 1236b4d6300SChristophe Leroy 12416132529SChristophe Leroy WARN_ON_ONCE(IS_ENABLED(CONFIG_PPC_KUAP_DEBUG) && kuap != KUAP_NONE); 12521eb58aeSChristophe Leroy 12621eb58aeSChristophe Leroy return kuap; 12721eb58aeSChristophe Leroy } 12821eb58aeSChristophe Leroy 129*ba454f9cSChristophe Leroy static inline void __kuap_assert_locked(void) 13021eb58aeSChristophe Leroy { 131*ba454f9cSChristophe Leroy __kuap_get_and_assert_locked(); 13221eb58aeSChristophe Leroy } 13321eb58aeSChristophe Leroy 134*ba454f9cSChristophe Leroy static __always_inline void __allow_user_access(void __user *to, const void __user *from, 1351d8f739bSChristophe Leroy u32 size, unsigned long dir) 136a68c31fcSChristophe Leroy { 1376b4d6300SChristophe Leroy if (kuap_is_disabled()) 1386b4d6300SChristophe Leroy return; 1396b4d6300SChristophe Leroy 1401d8f739bSChristophe Leroy BUILD_BUG_ON(!__builtin_constant_p(dir)); 141bedb4dbeSChristophe Leroy 1421d8f739bSChristophe Leroy if (!(dir & KUAP_WRITE)) 143a68c31fcSChristophe Leroy return; 144a68c31fcSChristophe Leroy 14516132529SChristophe Leroy current->thread.kuap = (__force u32)to; 14616132529SChristophe Leroy kuap_unlock_one((__force u32)to); 147a68c31fcSChristophe Leroy } 148a68c31fcSChristophe Leroy 149*ba454f9cSChristophe Leroy static __always_inline void __prevent_user_access(unsigned long dir) 150a68c31fcSChristophe Leroy { 15116132529SChristophe Leroy u32 kuap = current->thread.kuap; 152a68c31fcSChristophe Leroy 1536b4d6300SChristophe Leroy if (kuap_is_disabled()) 1546b4d6300SChristophe Leroy return; 1556b4d6300SChristophe Leroy 1561d8f739bSChristophe Leroy BUILD_BUG_ON(!__builtin_constant_p(dir)); 157bedb4dbeSChristophe Leroy 15816132529SChristophe Leroy if (!(dir & KUAP_WRITE)) 1591d8f739bSChristophe Leroy return; 1601d8f739bSChristophe Leroy 16116132529SChristophe Leroy current->thread.kuap = KUAP_NONE; 16216132529SChristophe Leroy kuap_lock(kuap, true); 163a68c31fcSChristophe Leroy } 164a68c31fcSChristophe Leroy 165*ba454f9cSChristophe Leroy static inline unsigned long __prevent_user_access_return(void) 1663d7dfd63SChristophe Leroy { 1673d7dfd63SChristophe Leroy unsigned long flags = current->thread.kuap; 1683d7dfd63SChristophe Leroy 1696b4d6300SChristophe Leroy if (kuap_is_disabled()) 17016132529SChristophe Leroy return KUAP_NONE; 1716b4d6300SChristophe Leroy 17216132529SChristophe Leroy if (flags != KUAP_NONE) { 17316132529SChristophe Leroy current->thread.kuap = KUAP_NONE; 17416132529SChristophe Leroy kuap_lock(flags, true); 17516132529SChristophe Leroy } 1763d7dfd63SChristophe Leroy 1773d7dfd63SChristophe Leroy return flags; 1783d7dfd63SChristophe Leroy } 1793d7dfd63SChristophe Leroy 180*ba454f9cSChristophe Leroy static inline void __restore_user_access(unsigned long flags) 1813d7dfd63SChristophe Leroy { 1826b4d6300SChristophe Leroy if (kuap_is_disabled()) 1836b4d6300SChristophe Leroy return; 1846b4d6300SChristophe Leroy 18516132529SChristophe Leroy if (flags != KUAP_NONE) { 18616132529SChristophe Leroy current->thread.kuap = flags; 18716132529SChristophe Leroy kuap_unlock(flags, true); 18816132529SChristophe Leroy } 1893d7dfd63SChristophe Leroy } 1903d7dfd63SChristophe Leroy 191475c8749SAneesh Kumar K.V static inline bool 192*ba454f9cSChristophe Leroy __bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write) 193a68c31fcSChristophe Leroy { 19416132529SChristophe Leroy unsigned long kuap = regs->kuap; 1956ec20aa2SChristophe Leroy 1966b4d6300SChristophe Leroy if (kuap_is_disabled()) 1976b4d6300SChristophe Leroy return false; 1986b4d6300SChristophe Leroy 19916132529SChristophe Leroy if (!is_write || kuap == KUAP_ALL) 20016132529SChristophe Leroy return false; 20116132529SChristophe Leroy if (kuap == KUAP_NONE) 20216132529SChristophe Leroy return true; 20316132529SChristophe Leroy 20416132529SChristophe Leroy /* If faulting address doesn't match unlocked segment, unlock all */ 20516132529SChristophe Leroy if ((kuap ^ address) & 0xf0000000) 20616132529SChristophe Leroy regs->kuap = KUAP_ALL; 20716132529SChristophe Leroy 20816132529SChristophe Leroy return false; 209a68c31fcSChristophe Leroy } 210a68c31fcSChristophe Leroy 211a68c31fcSChristophe Leroy #endif /* CONFIG_PPC_KUAP */ 212a68c31fcSChristophe Leroy 21331ed2b13SChristophe Leroy #endif /* __ASSEMBLY__ */ 21431ed2b13SChristophe Leroy 21531ed2b13SChristophe Leroy #endif /* _ASM_POWERPC_BOOK3S_32_KUP_H */ 216