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 
8 #ifndef __ASSEMBLY__
9 
10 #include <linux/jump_label.h>
11 
12 extern struct static_key_false disable_kuap_key;
13 extern struct static_key_false disable_kuep_key;
14 
15 static __always_inline bool kuap_is_disabled(void)
16 {
17 	return !IS_ENABLED(CONFIG_PPC_KUAP) || static_branch_unlikely(&disable_kuap_key);
18 }
19 
20 static __always_inline bool kuep_is_disabled(void)
21 {
22 	return !IS_ENABLED(CONFIG_PPC_KUEP) || static_branch_unlikely(&disable_kuep_key);
23 }
24 
25 static inline void kuep_lock(void)
26 {
27 	if (kuep_is_disabled())
28 		return;
29 
30 	update_user_segments(mfsr(0) | SR_NX);
31 }
32 
33 static inline void kuep_unlock(void)
34 {
35 	if (kuep_is_disabled())
36 		return;
37 
38 	update_user_segments(mfsr(0) & ~SR_NX);
39 }
40 
41 #ifdef CONFIG_PPC_KUAP
42 
43 #include <linux/sched.h>
44 
45 #define KUAP_NONE	(~0UL)
46 #define KUAP_ALL	(~1UL)
47 
48 static inline void kuap_lock_one(unsigned long addr)
49 {
50 	mtsr(mfsr(addr) | SR_KS, addr);
51 	isync();	/* Context sync required after mtsr() */
52 }
53 
54 static inline void kuap_unlock_one(unsigned long addr)
55 {
56 	mtsr(mfsr(addr) & ~SR_KS, addr);
57 	isync();	/* Context sync required after mtsr() */
58 }
59 
60 static inline void kuap_lock_all(void)
61 {
62 	update_user_segments(mfsr(0) | SR_KS);
63 	isync();	/* Context sync required after mtsr() */
64 }
65 
66 static inline void kuap_unlock_all(void)
67 {
68 	update_user_segments(mfsr(0) & ~SR_KS);
69 	isync();	/* Context sync required after mtsr() */
70 }
71 
72 void kuap_lock_all_ool(void);
73 void kuap_unlock_all_ool(void);
74 
75 static inline void kuap_lock(unsigned long addr, bool ool)
76 {
77 	if (likely(addr != KUAP_ALL))
78 		kuap_lock_one(addr);
79 	else if (!ool)
80 		kuap_lock_all();
81 	else
82 		kuap_lock_all_ool();
83 }
84 
85 static inline void kuap_unlock(unsigned long addr, bool ool)
86 {
87 	if (likely(addr != KUAP_ALL))
88 		kuap_unlock_one(addr);
89 	else if (!ool)
90 		kuap_unlock_all();
91 	else
92 		kuap_unlock_all_ool();
93 }
94 
95 static inline void kuap_save_and_lock(struct pt_regs *regs)
96 {
97 	unsigned long kuap = current->thread.kuap;
98 
99 	if (kuap_is_disabled())
100 		return;
101 
102 	regs->kuap = kuap;
103 	if (unlikely(kuap == KUAP_NONE))
104 		return;
105 
106 	current->thread.kuap = KUAP_NONE;
107 	kuap_lock(kuap, false);
108 }
109 
110 static inline void kuap_user_restore(struct pt_regs *regs)
111 {
112 }
113 
114 static inline void kuap_kernel_restore(struct pt_regs *regs, unsigned long kuap)
115 {
116 	if (kuap_is_disabled())
117 		return;
118 
119 	current->thread.kuap = regs->kuap;
120 
121 	kuap_unlock(regs->kuap, false);
122 }
123 
124 static inline unsigned long kuap_get_and_assert_locked(void)
125 {
126 	unsigned long kuap = current->thread.kuap;
127 
128 	if (kuap_is_disabled())
129 		return KUAP_NONE;
130 
131 	WARN_ON_ONCE(IS_ENABLED(CONFIG_PPC_KUAP_DEBUG) && kuap != KUAP_NONE);
132 
133 	return kuap;
134 }
135 
136 static inline void kuap_assert_locked(void)
137 {
138 	kuap_get_and_assert_locked();
139 }
140 
141 static __always_inline void allow_user_access(void __user *to, const void __user *from,
142 					      u32 size, unsigned long dir)
143 {
144 	if (kuap_is_disabled())
145 		return;
146 
147 	BUILD_BUG_ON(!__builtin_constant_p(dir));
148 
149 	if (!(dir & KUAP_WRITE))
150 		return;
151 
152 	current->thread.kuap = (__force u32)to;
153 	kuap_unlock_one((__force u32)to);
154 }
155 
156 static __always_inline void prevent_user_access(unsigned long dir)
157 {
158 	u32 kuap = current->thread.kuap;
159 
160 	if (kuap_is_disabled())
161 		return;
162 
163 	BUILD_BUG_ON(!__builtin_constant_p(dir));
164 
165 	if (!(dir & KUAP_WRITE))
166 		return;
167 
168 	current->thread.kuap = KUAP_NONE;
169 	kuap_lock(kuap, true);
170 }
171 
172 static inline unsigned long prevent_user_access_return(void)
173 {
174 	unsigned long flags = current->thread.kuap;
175 
176 	if (kuap_is_disabled())
177 		return KUAP_NONE;
178 
179 	if (flags != KUAP_NONE) {
180 		current->thread.kuap = KUAP_NONE;
181 		kuap_lock(flags, true);
182 	}
183 
184 	return flags;
185 }
186 
187 static inline void restore_user_access(unsigned long flags)
188 {
189 	if (kuap_is_disabled())
190 		return;
191 
192 	if (flags != KUAP_NONE) {
193 		current->thread.kuap = flags;
194 		kuap_unlock(flags, true);
195 	}
196 }
197 
198 static inline bool
199 bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write)
200 {
201 	unsigned long kuap = regs->kuap;
202 
203 	if (kuap_is_disabled())
204 		return false;
205 
206 	if (!is_write || kuap == KUAP_ALL)
207 		return false;
208 	if (kuap == KUAP_NONE)
209 		return true;
210 
211 	/* If faulting address doesn't match unlocked segment, unlock all */
212 	if ((kuap ^ address) & 0xf0000000)
213 		regs->kuap = KUAP_ALL;
214 
215 	return false;
216 }
217 
218 #endif /* CONFIG_PPC_KUAP */
219 
220 #endif /* __ASSEMBLY__ */
221 
222 #endif /* _ASM_POWERPC_BOOK3S_32_KUP_H */
223