.. | .. |
---|
6 | 6 | #ifdef __KERNEL__ |
---|
7 | 7 | |
---|
8 | 8 | #include <asm/nops.h> |
---|
| 9 | +#include <asm/processor-flags.h> |
---|
| 10 | +#include <linux/irqflags.h> |
---|
| 11 | +#include <linux/jump_label.h> |
---|
9 | 12 | |
---|
10 | 13 | /* |
---|
11 | | - * Volatile isn't enough to prevent the compiler from reordering the |
---|
12 | | - * read/write functions for the control registers and messing everything up. |
---|
13 | | - * A memory clobber would solve the problem, but would prevent reordering of |
---|
14 | | - * all loads stores around it, which can hurt performance. Solution is to |
---|
15 | | - * use a variable and mimic reads and writes to it to enforce serialization |
---|
| 14 | + * The compiler should not reorder volatile asm statements with respect to each |
---|
| 15 | + * other: they should execute in program order. However GCC 4.9.x and 5.x have |
---|
| 16 | + * a bug (which was fixed in 8.1, 7.3 and 6.5) where they might reorder |
---|
| 17 | + * volatile asm. The write functions are not affected since they have memory |
---|
| 18 | + * clobbers preventing reordering. To prevent reads from being reordered with |
---|
| 19 | + * respect to writes, use a dummy memory operand. |
---|
16 | 20 | */ |
---|
17 | | -extern unsigned long __force_order; |
---|
| 21 | + |
---|
| 22 | +#define __FORCE_ORDER "m"(*(unsigned int *)0x1000UL) |
---|
| 23 | + |
---|
| 24 | +void native_write_cr0(unsigned long val); |
---|
18 | 25 | |
---|
19 | 26 | static inline unsigned long native_read_cr0(void) |
---|
20 | 27 | { |
---|
21 | 28 | unsigned long val; |
---|
22 | | - asm volatile("mov %%cr0,%0\n\t" : "=r" (val), "=m" (__force_order)); |
---|
| 29 | + asm volatile("mov %%cr0,%0\n\t" : "=r" (val) : __FORCE_ORDER); |
---|
23 | 30 | return val; |
---|
24 | 31 | } |
---|
25 | 32 | |
---|
26 | | -static inline void native_write_cr0(unsigned long val) |
---|
27 | | -{ |
---|
28 | | - asm volatile("mov %0,%%cr0": : "r" (val), "m" (__force_order)); |
---|
29 | | -} |
---|
30 | | - |
---|
31 | | -static inline unsigned long native_read_cr2(void) |
---|
| 33 | +static __always_inline unsigned long native_read_cr2(void) |
---|
32 | 34 | { |
---|
33 | 35 | unsigned long val; |
---|
34 | | - asm volatile("mov %%cr2,%0\n\t" : "=r" (val), "=m" (__force_order)); |
---|
| 36 | + asm volatile("mov %%cr2,%0\n\t" : "=r" (val) : __FORCE_ORDER); |
---|
35 | 37 | return val; |
---|
36 | 38 | } |
---|
37 | 39 | |
---|
38 | | -static inline void native_write_cr2(unsigned long val) |
---|
| 40 | +static __always_inline void native_write_cr2(unsigned long val) |
---|
39 | 41 | { |
---|
40 | | - asm volatile("mov %0,%%cr2": : "r" (val), "m" (__force_order)); |
---|
| 42 | + asm volatile("mov %0,%%cr2": : "r" (val) : "memory"); |
---|
41 | 43 | } |
---|
42 | 44 | |
---|
43 | 45 | static inline unsigned long __native_read_cr3(void) |
---|
44 | 46 | { |
---|
45 | 47 | unsigned long val; |
---|
46 | | - asm volatile("mov %%cr3,%0\n\t" : "=r" (val), "=m" (__force_order)); |
---|
| 48 | + asm volatile("mov %%cr3,%0\n\t" : "=r" (val) : __FORCE_ORDER); |
---|
47 | 49 | return val; |
---|
48 | 50 | } |
---|
49 | 51 | |
---|
50 | 52 | static inline void native_write_cr3(unsigned long val) |
---|
51 | 53 | { |
---|
52 | | - asm volatile("mov %0,%%cr3": : "r" (val), "m" (__force_order)); |
---|
| 54 | + asm volatile("mov %0,%%cr3": : "r" (val) : "memory"); |
---|
53 | 55 | } |
---|
54 | 56 | |
---|
55 | 57 | static inline unsigned long native_read_cr4(void) |
---|
.. | .. |
---|
64 | 66 | asm volatile("1: mov %%cr4, %0\n" |
---|
65 | 67 | "2:\n" |
---|
66 | 68 | _ASM_EXTABLE(1b, 2b) |
---|
67 | | - : "=r" (val), "=m" (__force_order) : "0" (0)); |
---|
| 69 | + : "=r" (val) : "0" (0), __FORCE_ORDER); |
---|
68 | 70 | #else |
---|
69 | 71 | /* CR4 always exists on x86_64. */ |
---|
70 | | - asm volatile("mov %%cr4,%0\n\t" : "=r" (val), "=m" (__force_order)); |
---|
| 72 | + asm volatile("mov %%cr4,%0\n\t" : "=r" (val) : __FORCE_ORDER); |
---|
71 | 73 | #endif |
---|
72 | 74 | return val; |
---|
73 | 75 | } |
---|
74 | 76 | |
---|
75 | | -static inline void native_write_cr4(unsigned long val) |
---|
76 | | -{ |
---|
77 | | - asm volatile("mov %0,%%cr4": : "r" (val), "m" (__force_order)); |
---|
78 | | -} |
---|
79 | | - |
---|
80 | | -#ifdef CONFIG_X86_64 |
---|
81 | | -static inline unsigned long native_read_cr8(void) |
---|
82 | | -{ |
---|
83 | | - unsigned long cr8; |
---|
84 | | - asm volatile("movq %%cr8,%0" : "=r" (cr8)); |
---|
85 | | - return cr8; |
---|
86 | | -} |
---|
87 | | - |
---|
88 | | -static inline void native_write_cr8(unsigned long val) |
---|
89 | | -{ |
---|
90 | | - asm volatile("movq %0,%%cr8" :: "r" (val) : "memory"); |
---|
91 | | -} |
---|
92 | | -#endif |
---|
| 77 | +void native_write_cr4(unsigned long val); |
---|
93 | 78 | |
---|
94 | 79 | #ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS |
---|
95 | | -static inline u32 __read_pkru(void) |
---|
| 80 | +static inline u32 rdpkru(void) |
---|
96 | 81 | { |
---|
97 | 82 | u32 ecx = 0; |
---|
98 | 83 | u32 edx, pkru; |
---|
.. | .. |
---|
107 | 92 | return pkru; |
---|
108 | 93 | } |
---|
109 | 94 | |
---|
110 | | -static inline void __write_pkru(u32 pkru) |
---|
| 95 | +static inline void wrpkru(u32 pkru) |
---|
111 | 96 | { |
---|
112 | 97 | u32 ecx = 0, edx = 0; |
---|
113 | 98 | |
---|
.. | .. |
---|
118 | 103 | asm volatile(".byte 0x0f,0x01,0xef\n\t" |
---|
119 | 104 | : : "a" (pkru), "c"(ecx), "d"(edx)); |
---|
120 | 105 | } |
---|
| 106 | + |
---|
| 107 | +static inline void __write_pkru(u32 pkru) |
---|
| 108 | +{ |
---|
| 109 | + /* |
---|
| 110 | + * WRPKRU is relatively expensive compared to RDPKRU. |
---|
| 111 | + * Avoid WRPKRU when it would not change the value. |
---|
| 112 | + */ |
---|
| 113 | + if (pkru == rdpkru()) |
---|
| 114 | + return; |
---|
| 115 | + |
---|
| 116 | + wrpkru(pkru); |
---|
| 117 | +} |
---|
| 118 | + |
---|
121 | 119 | #else |
---|
122 | | -static inline u32 __read_pkru(void) |
---|
| 120 | +static inline u32 rdpkru(void) |
---|
123 | 121 | { |
---|
124 | 122 | return 0; |
---|
125 | 123 | } |
---|
.. | .. |
---|
134 | 132 | asm volatile("wbinvd": : :"memory"); |
---|
135 | 133 | } |
---|
136 | 134 | |
---|
137 | | -extern asmlinkage void native_load_gs_index(unsigned); |
---|
| 135 | +extern asmlinkage void asm_load_gs_index(unsigned int selector); |
---|
| 136 | + |
---|
| 137 | +static inline void native_load_gs_index(unsigned int selector) |
---|
| 138 | +{ |
---|
| 139 | + unsigned long flags; |
---|
| 140 | + |
---|
| 141 | + local_irq_save(flags); |
---|
| 142 | + asm_load_gs_index(selector); |
---|
| 143 | + local_irq_restore(flags); |
---|
| 144 | +} |
---|
138 | 145 | |
---|
139 | 146 | static inline unsigned long __read_cr4(void) |
---|
140 | 147 | { |
---|
141 | 148 | return native_read_cr4(); |
---|
142 | 149 | } |
---|
143 | 150 | |
---|
144 | | -#ifdef CONFIG_PARAVIRT |
---|
| 151 | +#ifdef CONFIG_PARAVIRT_XXL |
---|
145 | 152 | #include <asm/paravirt.h> |
---|
146 | 153 | #else |
---|
147 | 154 | |
---|
.. | .. |
---|
155 | 162 | native_write_cr0(x); |
---|
156 | 163 | } |
---|
157 | 164 | |
---|
158 | | -static inline unsigned long read_cr2(void) |
---|
| 165 | +static __always_inline unsigned long read_cr2(void) |
---|
159 | 166 | { |
---|
160 | 167 | return native_read_cr2(); |
---|
161 | 168 | } |
---|
162 | 169 | |
---|
163 | | -static inline void write_cr2(unsigned long x) |
---|
| 170 | +static __always_inline void write_cr2(unsigned long x) |
---|
164 | 171 | { |
---|
165 | 172 | native_write_cr2(x); |
---|
166 | 173 | } |
---|
.. | .. |
---|
191 | 198 | |
---|
192 | 199 | #ifdef CONFIG_X86_64 |
---|
193 | 200 | |
---|
194 | | -static inline unsigned long read_cr8(void) |
---|
195 | | -{ |
---|
196 | | - return native_read_cr8(); |
---|
197 | | -} |
---|
198 | | - |
---|
199 | | -static inline void write_cr8(unsigned long x) |
---|
200 | | -{ |
---|
201 | | - native_write_cr8(x); |
---|
202 | | -} |
---|
203 | | - |
---|
204 | | -static inline void load_gs_index(unsigned selector) |
---|
| 201 | +static inline void load_gs_index(unsigned int selector) |
---|
205 | 202 | { |
---|
206 | 203 | native_load_gs_index(selector); |
---|
207 | 204 | } |
---|
208 | 205 | |
---|
209 | 206 | #endif |
---|
210 | 207 | |
---|
211 | | -#endif/* CONFIG_PARAVIRT */ |
---|
| 208 | +#endif /* CONFIG_PARAVIRT_XXL */ |
---|
212 | 209 | |
---|
213 | 210 | static inline void clflush(volatile void *__p) |
---|
214 | 211 | { |
---|
.. | .. |
---|
239 | 236 | |
---|
240 | 237 | #define nop() asm volatile ("nop") |
---|
241 | 238 | |
---|
| 239 | +static inline void serialize(void) |
---|
| 240 | +{ |
---|
| 241 | + /* Instruction opcode for SERIALIZE; supported in binutils >= 2.35. */ |
---|
| 242 | + asm volatile(".byte 0xf, 0x1, 0xe8" ::: "memory"); |
---|
| 243 | +} |
---|
| 244 | + |
---|
| 245 | +/* The dst parameter must be 64-bytes aligned */ |
---|
| 246 | +static inline void movdir64b(void *dst, const void *src) |
---|
| 247 | +{ |
---|
| 248 | + const struct { char _[64]; } *__src = src; |
---|
| 249 | + struct { char _[64]; } *__dst = dst; |
---|
| 250 | + |
---|
| 251 | + /* |
---|
| 252 | + * MOVDIR64B %(rdx), rax. |
---|
| 253 | + * |
---|
| 254 | + * Both __src and __dst must be memory constraints in order to tell the |
---|
| 255 | + * compiler that no other memory accesses should be reordered around |
---|
| 256 | + * this one. |
---|
| 257 | + * |
---|
| 258 | + * Also, both must be supplied as lvalues because this tells |
---|
| 259 | + * the compiler what the object is (its size) the instruction accesses. |
---|
| 260 | + * I.e., not the pointers but what they point to, thus the deref'ing '*'. |
---|
| 261 | + */ |
---|
| 262 | + asm volatile(".byte 0x66, 0x0f, 0x38, 0xf8, 0x02" |
---|
| 263 | + : "+m" (*__dst) |
---|
| 264 | + : "m" (*__src), "a" (__dst), "d" (__src)); |
---|
| 265 | +} |
---|
| 266 | + |
---|
| 267 | +/** |
---|
| 268 | + * enqcmds - Enqueue a command in supervisor (CPL0) mode |
---|
| 269 | + * @dst: destination, in MMIO space (must be 512-bit aligned) |
---|
| 270 | + * @src: 512 bits memory operand |
---|
| 271 | + * |
---|
| 272 | + * The ENQCMDS instruction allows software to write a 512-bit command to |
---|
| 273 | + * a 512-bit-aligned special MMIO region that supports the instruction. |
---|
| 274 | + * A return status is loaded into the ZF flag in the RFLAGS register. |
---|
| 275 | + * ZF = 0 equates to success, and ZF = 1 indicates retry or error. |
---|
| 276 | + * |
---|
| 277 | + * This function issues the ENQCMDS instruction to submit data from |
---|
| 278 | + * kernel space to MMIO space, in a unit of 512 bits. Order of data access |
---|
| 279 | + * is not guaranteed, nor is a memory barrier performed afterwards. It |
---|
| 280 | + * returns 0 on success and -EAGAIN on failure. |
---|
| 281 | + * |
---|
| 282 | + * Warning: Do not use this helper unless your driver has checked that the |
---|
| 283 | + * ENQCMDS instruction is supported on the platform and the device accepts |
---|
| 284 | + * ENQCMDS. |
---|
| 285 | + */ |
---|
| 286 | +static inline int enqcmds(void __iomem *dst, const void *src) |
---|
| 287 | +{ |
---|
| 288 | + const struct { char _[64]; } *__src = src; |
---|
| 289 | + struct { char _[64]; } __iomem *__dst = dst; |
---|
| 290 | + bool zf; |
---|
| 291 | + |
---|
| 292 | + /* |
---|
| 293 | + * ENQCMDS %(rdx), rax |
---|
| 294 | + * |
---|
| 295 | + * See movdir64b()'s comment on operand specification. |
---|
| 296 | + */ |
---|
| 297 | + asm volatile(".byte 0xf3, 0x0f, 0x38, 0xf8, 0x02, 0x66, 0x90" |
---|
| 298 | + CC_SET(z) |
---|
| 299 | + : CC_OUT(z) (zf), "+m" (*__dst) |
---|
| 300 | + : "m" (*__src), "a" (__dst), "d" (__src)); |
---|
| 301 | + |
---|
| 302 | + /* Submission failure is indicated via EFLAGS.ZF=1 */ |
---|
| 303 | + if (zf) |
---|
| 304 | + return -EAGAIN; |
---|
| 305 | + |
---|
| 306 | + return 0; |
---|
| 307 | +} |
---|
242 | 308 | |
---|
243 | 309 | #endif /* __KERNEL__ */ |
---|
244 | 310 | |
---|