1 /* SPDX-License-Identifier: GPL-2.0 */
2 #ifndef _ASM_POWERPC_BOOK3S_32_KUP_H
3 #define _ASM_POWERPC_BOOK3S_32_KUP_H
4 
5 #include <asm/bug.h>
6 #include <asm/book3s/32/mmu-hash.h>
7 #include <asm/mmu.h>
8 #include <asm/synch.h>
9 
10 #ifndef __ASSEMBLY__
11 
12 #include <linux/jump_label.h>
13 
14 extern struct static_key_false disable_kuap_key;
15 
kuep_is_disabled(void)16 static __always_inline bool kuep_is_disabled(void)
17 {
18 	return !IS_ENABLED(CONFIG_PPC_KUEP);
19 }
20 
21 #ifdef CONFIG_PPC_KUAP
22 
23 #include <linux/sched.h>
24 
25 #define KUAP_NONE	(~0UL)
26 #define KUAP_ALL	(~1UL)
27 
kuap_is_disabled(void)28 static __always_inline bool kuap_is_disabled(void)
29 {
30 	return static_branch_unlikely(&disable_kuap_key);
31 }
32 
kuap_lock_one(unsigned long addr)33 static inline void kuap_lock_one(unsigned long addr)
34 {
35 	mtsr(mfsr(addr) | SR_KS, addr);
36 	isync();	/* Context sync required after mtsr() */
37 }
38 
kuap_unlock_one(unsigned long addr)39 static inline void kuap_unlock_one(unsigned long addr)
40 {
41 	mtsr(mfsr(addr) & ~SR_KS, addr);
42 	isync();	/* Context sync required after mtsr() */
43 }
44 
kuap_lock_all(void)45 static inline void kuap_lock_all(void)
46 {
47 	update_user_segments(mfsr(0) | SR_KS);
48 	isync();	/* Context sync required after mtsr() */
49 }
50 
kuap_unlock_all(void)51 static inline void kuap_unlock_all(void)
52 {
53 	update_user_segments(mfsr(0) & ~SR_KS);
54 	isync();	/* Context sync required after mtsr() */
55 }
56 
57 void kuap_lock_all_ool(void);
58 void kuap_unlock_all_ool(void);
59 
kuap_lock_addr(unsigned long addr,bool ool)60 static inline void kuap_lock_addr(unsigned long addr, bool ool)
61 {
62 	if (likely(addr != KUAP_ALL))
63 		kuap_lock_one(addr);
64 	else if (!ool)
65 		kuap_lock_all();
66 	else
67 		kuap_lock_all_ool();
68 }
69 
kuap_unlock(unsigned long addr,bool ool)70 static inline void kuap_unlock(unsigned long addr, bool ool)
71 {
72 	if (likely(addr != KUAP_ALL))
73 		kuap_unlock_one(addr);
74 	else if (!ool)
75 		kuap_unlock_all();
76 	else
77 		kuap_unlock_all_ool();
78 }
79 
__kuap_lock(void)80 static inline void __kuap_lock(void)
81 {
82 }
83 
__kuap_save_and_lock(struct pt_regs * regs)84 static inline void __kuap_save_and_lock(struct pt_regs *regs)
85 {
86 	unsigned long kuap = current->thread.kuap;
87 
88 	regs->kuap = kuap;
89 	if (unlikely(kuap == KUAP_NONE))
90 		return;
91 
92 	current->thread.kuap = KUAP_NONE;
93 	kuap_lock_addr(kuap, false);
94 }
95 
kuap_user_restore(struct pt_regs * regs)96 static inline void kuap_user_restore(struct pt_regs *regs)
97 {
98 }
99 
__kuap_kernel_restore(struct pt_regs * regs,unsigned long kuap)100 static inline void __kuap_kernel_restore(struct pt_regs *regs, unsigned long kuap)
101 {
102 	if (unlikely(kuap != KUAP_NONE)) {
103 		current->thread.kuap = KUAP_NONE;
104 		kuap_lock_addr(kuap, false);
105 	}
106 
107 	if (likely(regs->kuap == KUAP_NONE))
108 		return;
109 
110 	current->thread.kuap = regs->kuap;
111 
112 	kuap_unlock(regs->kuap, false);
113 }
114 
__kuap_get_and_assert_locked(void)115 static inline unsigned long __kuap_get_and_assert_locked(void)
116 {
117 	unsigned long kuap = current->thread.kuap;
118 
119 	WARN_ON_ONCE(IS_ENABLED(CONFIG_PPC_KUAP_DEBUG) && kuap != KUAP_NONE);
120 
121 	return kuap;
122 }
123 
__allow_user_access(void __user * to,const void __user * from,u32 size,unsigned long dir)124 static __always_inline void __allow_user_access(void __user *to, const void __user *from,
125 						u32 size, unsigned long dir)
126 {
127 	BUILD_BUG_ON(!__builtin_constant_p(dir));
128 
129 	if (!(dir & KUAP_WRITE))
130 		return;
131 
132 	current->thread.kuap = (__force u32)to;
133 	kuap_unlock_one((__force u32)to);
134 }
135 
__prevent_user_access(unsigned long dir)136 static __always_inline void __prevent_user_access(unsigned long dir)
137 {
138 	u32 kuap = current->thread.kuap;
139 
140 	BUILD_BUG_ON(!__builtin_constant_p(dir));
141 
142 	if (!(dir & KUAP_WRITE))
143 		return;
144 
145 	current->thread.kuap = KUAP_NONE;
146 	kuap_lock_addr(kuap, true);
147 }
148 
__prevent_user_access_return(void)149 static inline unsigned long __prevent_user_access_return(void)
150 {
151 	unsigned long flags = current->thread.kuap;
152 
153 	if (flags != KUAP_NONE) {
154 		current->thread.kuap = KUAP_NONE;
155 		kuap_lock_addr(flags, true);
156 	}
157 
158 	return flags;
159 }
160 
__restore_user_access(unsigned long flags)161 static inline void __restore_user_access(unsigned long flags)
162 {
163 	if (flags != KUAP_NONE) {
164 		current->thread.kuap = flags;
165 		kuap_unlock(flags, true);
166 	}
167 }
168 
169 static inline bool
__bad_kuap_fault(struct pt_regs * regs,unsigned long address,bool is_write)170 __bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write)
171 {
172 	unsigned long kuap = regs->kuap;
173 
174 	if (!is_write || kuap == KUAP_ALL)
175 		return false;
176 	if (kuap == KUAP_NONE)
177 		return true;
178 
179 	/* If faulting address doesn't match unlocked segment, unlock all */
180 	if ((kuap ^ address) & 0xf0000000)
181 		regs->kuap = KUAP_ALL;
182 
183 	return false;
184 }
185 
186 #endif /* CONFIG_PPC_KUAP */
187 
188 #endif /* __ASSEMBLY__ */
189 
190 #endif /* _ASM_POWERPC_BOOK3S_32_KUP_H */
191