Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid
[linux-2.6-microblaze.git] / arch / powerpc / include / asm / book3s / 32 / kup.h
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 */