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