xref: /linux/arch/powerpc/include/asm/book3s/32/kup.h (revision ba454f9c8e4efcc47c772b7642a5c8c6d1343cbf)
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