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