1 /* SPDX-License-Identifier: GPL-2.0 */ 2 #ifndef _ASM_X86_SPECIAL_INSNS_H 3 #define _ASM_X86_SPECIAL_INSNS_H 4 5 6 #ifdef __KERNEL__ 7 8 #include <asm/nops.h> 9 #include <asm/processor-flags.h> 10 #include <linux/jump_label.h> 11 12 /* 13 * Volatile isn't enough to prevent the compiler from reordering the 14 * read/write functions for the control registers and messing everything up. 15 * A memory clobber would solve the problem, but would prevent reordering of 16 * all loads stores around it, which can hurt performance. Solution is to 17 * use a variable and mimic reads and writes to it to enforce serialization 18 */ 19 extern unsigned long __force_order; 20 21 /* Starts false and gets enabled once CPU feature detection is done. */ 22 DECLARE_STATIC_KEY_FALSE(cr_pinning); 23 extern unsigned long cr4_pinned_bits; 24 25 static inline unsigned long native_read_cr0(void) 26 { 27 unsigned long val; 28 asm volatile("mov %%cr0,%0\n\t" : "=r" (val), "=m" (__force_order)); 29 return val; 30 } 31 32 static inline void native_write_cr0(unsigned long val) 33 { 34 unsigned long bits_missing = 0; 35 36 set_register: 37 asm volatile("mov %0,%%cr0": "+r" (val), "+m" (__force_order)); 38 39 if (static_branch_likely(&cr_pinning)) { 40 if (unlikely((val & X86_CR0_WP) != X86_CR0_WP)) { 41 bits_missing = X86_CR0_WP; 42 val |= bits_missing; 43 goto set_register; 44 } 45 /* Warn after we've set the missing bits. */ 46 WARN_ONCE(bits_missing, "CR0 WP bit went missing!?\n"); 47 } 48 } 49 50 static inline unsigned long native_read_cr2(void) 51 { 52 unsigned long val; 53 asm volatile("mov %%cr2,%0\n\t" : "=r" (val), "=m" (__force_order)); 54 return val; 55 } 56 57 static inline void native_write_cr2(unsigned long val) 58 { 59 asm volatile("mov %0,%%cr2": : "r" (val), "m" (__force_order)); 60 } 61 62 static inline unsigned long __native_read_cr3(void) 63 { 64 unsigned long val; 65 asm volatile("mov %%cr3,%0\n\t" : "=r" (val), "=m" (__force_order)); 66 return val; 67 } 68 69 static inline void native_write_cr3(unsigned long val) 70 { 71 asm volatile("mov %0,%%cr3": : "r" (val), "m" (__force_order)); 72 } 73 74 static inline unsigned long native_read_cr4(void) 75 { 76 unsigned long val; 77 #ifdef CONFIG_X86_32 78 /* 79 * This could fault if CR4 does not exist. Non-existent CR4 80 * is functionally equivalent to CR4 == 0. Keep it simple and pretend 81 * that CR4 == 0 on CPUs that don't have CR4. 82 */ 83 asm volatile("1: mov %%cr4, %0\n" 84 "2:\n" 85 _ASM_EXTABLE(1b, 2b) 86 : "=r" (val), "=m" (__force_order) : "0" (0)); 87 #else 88 /* CR4 always exists on x86_64. */ 89 asm volatile("mov %%cr4,%0\n\t" : "=r" (val), "=m" (__force_order)); 90 #endif 91 return val; 92 } 93 94 static inline void native_write_cr4(unsigned long val) 95 { 96 unsigned long bits_missing = 0; 97 98 set_register: 99 asm volatile("mov %0,%%cr4": "+r" (val), "+m" (cr4_pinned_bits)); 100 101 if (static_branch_likely(&cr_pinning)) { 102 if (unlikely((val & cr4_pinned_bits) != cr4_pinned_bits)) { 103 bits_missing = ~val & cr4_pinned_bits; 104 val |= bits_missing; 105 goto set_register; 106 } 107 /* Warn after we've set the missing bits. */ 108 WARN_ONCE(bits_missing, "CR4 bits went missing: %lx!?\n", 109 bits_missing); 110 } 111 } 112 113 #ifdef CONFIG_X86_64 114 static inline unsigned long native_read_cr8(void) 115 { 116 unsigned long cr8; 117 asm volatile("movq %%cr8,%0" : "=r" (cr8)); 118 return cr8; 119 } 120 121 static inline void native_write_cr8(unsigned long val) 122 { 123 asm volatile("movq %0,%%cr8" :: "r" (val) : "memory"); 124 } 125 #endif 126 127 #ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS 128 static inline u32 rdpkru(void) 129 { 130 u32 ecx = 0; 131 u32 edx, pkru; 132 133 /* 134 * "rdpkru" instruction. Places PKRU contents in to EAX, 135 * clears EDX and requires that ecx=0. 136 */ 137 asm volatile(".byte 0x0f,0x01,0xee\n\t" 138 : "=a" (pkru), "=d" (edx) 139 : "c" (ecx)); 140 return pkru; 141 } 142 143 static inline void wrpkru(u32 pkru) 144 { 145 u32 ecx = 0, edx = 0; 146 147 /* 148 * "wrpkru" instruction. Loads contents in EAX to PKRU, 149 * requires that ecx = edx = 0. 150 */ 151 asm volatile(".byte 0x0f,0x01,0xef\n\t" 152 : : "a" (pkru), "c"(ecx), "d"(edx)); 153 } 154 155 static inline void __write_pkru(u32 pkru) 156 { 157 /* 158 * WRPKRU is relatively expensive compared to RDPKRU. 159 * Avoid WRPKRU when it would not change the value. 160 */ 161 if (pkru == rdpkru()) 162 return; 163 164 wrpkru(pkru); 165 } 166 167 #else 168 static inline u32 rdpkru(void) 169 { 170 return 0; 171 } 172 173 static inline void __write_pkru(u32 pkru) 174 { 175 } 176 #endif 177 178 static inline void native_wbinvd(void) 179 { 180 asm volatile("wbinvd": : :"memory"); 181 } 182 183 extern asmlinkage void native_load_gs_index(unsigned); 184 185 static inline unsigned long __read_cr4(void) 186 { 187 return native_read_cr4(); 188 } 189 190 #ifdef CONFIG_PARAVIRT_XXL 191 #include <asm/paravirt.h> 192 #else 193 194 static inline unsigned long read_cr0(void) 195 { 196 return native_read_cr0(); 197 } 198 199 static inline void write_cr0(unsigned long x) 200 { 201 native_write_cr0(x); 202 } 203 204 static inline unsigned long read_cr2(void) 205 { 206 return native_read_cr2(); 207 } 208 209 static inline void write_cr2(unsigned long x) 210 { 211 native_write_cr2(x); 212 } 213 214 /* 215 * Careful! CR3 contains more than just an address. You probably want 216 * read_cr3_pa() instead. 217 */ 218 static inline unsigned long __read_cr3(void) 219 { 220 return __native_read_cr3(); 221 } 222 223 static inline void write_cr3(unsigned long x) 224 { 225 native_write_cr3(x); 226 } 227 228 static inline void __write_cr4(unsigned long x) 229 { 230 native_write_cr4(x); 231 } 232 233 static inline void wbinvd(void) 234 { 235 native_wbinvd(); 236 } 237 238 #ifdef CONFIG_X86_64 239 240 static inline unsigned long read_cr8(void) 241 { 242 return native_read_cr8(); 243 } 244 245 static inline void write_cr8(unsigned long x) 246 { 247 native_write_cr8(x); 248 } 249 250 static inline void load_gs_index(unsigned selector) 251 { 252 native_load_gs_index(selector); 253 } 254 255 #endif 256 257 #endif /* CONFIG_PARAVIRT_XXL */ 258 259 static inline void clflush(volatile void *__p) 260 { 261 asm volatile("clflush %0" : "+m" (*(volatile char __force *)__p)); 262 } 263 264 static inline void clflushopt(volatile void *__p) 265 { 266 alternative_io(".byte " __stringify(NOP_DS_PREFIX) "; clflush %P0", 267 ".byte 0x66; clflush %P0", 268 X86_FEATURE_CLFLUSHOPT, 269 "+m" (*(volatile char __force *)__p)); 270 } 271 272 static inline void clwb(volatile void *__p) 273 { 274 volatile struct { char x[64]; } *p = __p; 275 276 asm volatile(ALTERNATIVE_2( 277 ".byte " __stringify(NOP_DS_PREFIX) "; clflush (%[pax])", 278 ".byte 0x66; clflush (%[pax])", /* clflushopt (%%rax) */ 279 X86_FEATURE_CLFLUSHOPT, 280 ".byte 0x66, 0x0f, 0xae, 0x30", /* clwb (%%rax) */ 281 X86_FEATURE_CLWB) 282 : [p] "+m" (*p) 283 : [pax] "a" (p)); 284 } 285 286 #define nop() asm volatile ("nop") 287 288 289 #endif /* __KERNEL__ */ 290 291 #endif /* _ASM_X86_SPECIAL_INSNS_H */ 292