/src/xen/xen/arch/x86/x86_emulate/0f01.c
Line | Count | Source |
1 | | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
2 | | /****************************************************************************** |
3 | | * 0f01.c - helper for x86_emulate.c |
4 | | * |
5 | | * Generic x86 (32-bit and 64-bit) instruction decoder and emulator. |
6 | | * |
7 | | * Copyright (c) 2005-2007 Keir Fraser |
8 | | * Copyright (c) 2005-2007 XenSource Inc. |
9 | | */ |
10 | | |
11 | | #include "private.h" |
12 | | |
13 | | #ifdef __XEN__ |
14 | | #include <asm/prot-key.h> |
15 | | #endif |
16 | | |
17 | 1.77k | #define ad_bytes (s->ad_bytes) /* for truncate_ea() */ |
18 | | |
19 | | int x86emul_0f01(struct x86_emulate_state *s, |
20 | | struct cpu_user_regs *regs, |
21 | | struct operand *dst, |
22 | | struct x86_emulate_ctxt *ctxt, |
23 | | const struct x86_emulate_ops *ops) |
24 | 36.5k | { |
25 | 36.5k | enum x86_segment seg = (s->modrm_reg & 1) ? x86_seg_idtr : x86_seg_gdtr; |
26 | 36.5k | int rc; |
27 | | |
28 | 36.5k | switch ( s->modrm ) |
29 | 36.5k | { |
30 | 0 | unsigned long base, limit, cr0, cr0w, cr4; |
31 | 0 | struct segment_register sreg; |
32 | 0 | uint64_t msr_val; |
33 | | |
34 | 199 | case 0xc6: |
35 | 199 | switch ( s->vex.pfx ) |
36 | 199 | { |
37 | 198 | case vex_np: /* wrmsrns */ |
38 | 198 | vcpu_must_have(wrmsrns); |
39 | 198 | generate_exception_if(!mode_ring0(), X86_EXC_GP, 0); |
40 | 198 | fail_if(!ops->write_msr); |
41 | 196 | rc = ops->write_msr(regs->ecx, |
42 | 196 | ((uint64_t)regs->r(dx) << 32) | regs->eax, |
43 | 196 | ctxt, true); |
44 | 196 | goto done; |
45 | 199 | } |
46 | 1 | generate_exception(X86_EXC_UD); |
47 | | |
48 | 19 | case 0xca: /* clac */ |
49 | 216 | case 0xcb: /* stac */ |
50 | 216 | vcpu_must_have(smap); |
51 | 216 | generate_exception_if(s->vex.pfx || !mode_ring0(), X86_EXC_UD); |
52 | | |
53 | 216 | regs->eflags &= ~X86_EFLAGS_AC; |
54 | 213 | if ( s->modrm == 0xcb ) |
55 | 194 | regs->eflags |= X86_EFLAGS_AC; |
56 | 213 | break; |
57 | | |
58 | 70 | case 0xd0: /* xgetbv */ |
59 | 70 | generate_exception_if(s->vex.pfx, X86_EXC_UD); |
60 | 70 | if ( !ops->read_cr || !ops->read_xcr || |
61 | 68 | ops->read_cr(4, &cr4, ctxt) != X86EMUL_OKAY ) |
62 | 1 | cr4 = 0; |
63 | 69 | generate_exception_if(!(cr4 & X86_CR4_OSXSAVE), X86_EXC_UD); |
64 | 69 | rc = ops->read_xcr(regs->ecx, &msr_val, ctxt); |
65 | 68 | if ( rc != X86EMUL_OKAY ) |
66 | 1 | goto done; |
67 | 67 | regs->r(ax) = (uint32_t)msr_val; |
68 | 67 | regs->r(dx) = msr_val >> 32; |
69 | 67 | break; |
70 | | |
71 | 3 | case 0xd1: /* xsetbv */ |
72 | 3 | generate_exception_if(s->vex.pfx, X86_EXC_UD); |
73 | 3 | if ( !ops->read_cr || !ops->write_xcr || |
74 | 0 | ops->read_cr(4, &cr4, ctxt) != X86EMUL_OKAY ) |
75 | 2 | cr4 = 0; |
76 | 2 | generate_exception_if(!(cr4 & X86_CR4_OSXSAVE), X86_EXC_UD); |
77 | 2 | generate_exception_if(!mode_ring0(), X86_EXC_GP, 0); |
78 | 0 | rc = ops->write_xcr(regs->ecx, |
79 | 0 | regs->eax | ((uint64_t)regs->edx << 32), ctxt); |
80 | 0 | if ( rc != X86EMUL_OKAY ) |
81 | 0 | goto done; |
82 | 0 | break; |
83 | | |
84 | 1 | case 0xd4: /* vmfunc */ |
85 | 1 | generate_exception_if(s->vex.pfx, X86_EXC_UD); |
86 | 1 | fail_if(!ops->vmfunc); |
87 | 0 | if ( (rc = ops->vmfunc(ctxt)) != X86EMUL_OKAY ) |
88 | 0 | goto done; |
89 | 0 | break; |
90 | | |
91 | 2 | case 0xd5: /* xend */ |
92 | 2 | generate_exception_if(s->vex.pfx, X86_EXC_UD); |
93 | 2 | generate_exception_if(!vcpu_has_rtm(), X86_EXC_UD); |
94 | 1 | generate_exception_if(vcpu_has_rtm(), X86_EXC_GP, 0); |
95 | 0 | break; |
96 | | |
97 | 9 | case 0xd6: /* xtest */ |
98 | 9 | generate_exception_if(s->vex.pfx, X86_EXC_UD); |
99 | 9 | generate_exception_if(!vcpu_has_rtm() && !vcpu_has_hle(), |
100 | 8 | X86_EXC_UD); |
101 | | /* Neither HLE nor RTM can be active when we get here. */ |
102 | 8 | regs->eflags |= X86_EFLAGS_ZF; |
103 | 0 | break; |
104 | | |
105 | 455 | case 0xdf: /* invlpga */ |
106 | 455 | fail_if(!ops->read_msr); |
107 | 454 | if ( (rc = ops->read_msr(MSR_EFER, |
108 | 454 | &msr_val, ctxt)) != X86EMUL_OKAY ) |
109 | 0 | goto done; |
110 | | /* Finding SVME set implies vcpu_has_svm(). */ |
111 | 454 | generate_exception_if(!(msr_val & EFER_SVME) || |
112 | 454 | !in_protmode(ctxt, ops), X86_EXC_UD); |
113 | 454 | generate_exception_if(!mode_ring0(), X86_EXC_GP, 0); |
114 | 452 | fail_if(!ops->tlb_op); |
115 | 449 | if ( (rc = ops->tlb_op(x86emul_invlpga, truncate_ea(regs->r(ax)), |
116 | 449 | regs->ecx, ctxt)) != X86EMUL_OKAY ) |
117 | 3 | goto done; |
118 | 446 | break; |
119 | | |
120 | 446 | case 0xe8: |
121 | 1 | switch ( s->vex.pfx ) |
122 | 1 | { |
123 | 0 | case vex_np: /* serialize */ |
124 | 0 | vcpu_must_have(serialize); |
125 | 0 | asm volatile ( ".byte 0x0f, 0x01, 0xe8" ); /* Binutils >= 2.34, Clang >= 11 */ |
126 | 0 | break; |
127 | 0 | case vex_f2: /* xsusldtrk */ |
128 | 0 | vcpu_must_have(tsxldtrk); |
129 | | /* |
130 | | * We're never in a transactional region when coming here |
131 | | * - nothing else to do. |
132 | | */ |
133 | 0 | break; |
134 | 1 | default: |
135 | 1 | return X86EMUL_UNIMPLEMENTED; |
136 | 1 | } |
137 | 0 | break; |
138 | | |
139 | 2 | case 0xe9: |
140 | 2 | switch ( s->vex.pfx ) |
141 | 2 | { |
142 | 1 | case vex_f2: /* xresldtrk */ |
143 | 1 | vcpu_must_have(tsxldtrk); |
144 | | /* |
145 | | * We're never in a transactional region when coming here |
146 | | * - nothing else to do. |
147 | | */ |
148 | 1 | break; |
149 | 1 | default: |
150 | 1 | return X86EMUL_UNIMPLEMENTED; |
151 | 2 | } |
152 | 0 | break; |
153 | | |
154 | 241 | case 0xee: |
155 | 241 | switch ( s->vex.pfx ) |
156 | 241 | { |
157 | 240 | case vex_np: /* rdpkru */ |
158 | 240 | if ( !ops->read_cr || |
159 | 239 | ops->read_cr(4, &cr4, ctxt) != X86EMUL_OKAY ) |
160 | 1 | cr4 = 0; |
161 | 240 | generate_exception_if(!(cr4 & X86_CR4_PKE), X86_EXC_UD); |
162 | 240 | generate_exception_if(regs->ecx, X86_EXC_GP, 0); |
163 | 239 | regs->r(ax) = rdpkru(); |
164 | 194 | regs->r(dx) = 0; |
165 | 194 | break; |
166 | 1 | default: |
167 | 1 | return X86EMUL_UNIMPLEMENTED; |
168 | 241 | } |
169 | 194 | break; |
170 | | |
171 | 194 | case 0xef: |
172 | 111 | switch ( s->vex.pfx ) |
173 | 111 | { |
174 | 110 | case vex_np: /* wrpkru */ |
175 | 110 | if ( !ops->read_cr || |
176 | 109 | ops->read_cr(4, &cr4, ctxt) != X86EMUL_OKAY ) |
177 | 1 | cr4 = 0; |
178 | 110 | generate_exception_if(!(cr4 & X86_CR4_PKE), X86_EXC_UD); |
179 | 110 | generate_exception_if(regs->ecx | regs->edx, X86_EXC_GP, 0); |
180 | 109 | wrpkru(regs->eax); |
181 | 62 | break; |
182 | 1 | default: |
183 | 1 | return X86EMUL_UNIMPLEMENTED; |
184 | 111 | } |
185 | 62 | break; |
186 | | |
187 | 62 | case 0xf8: /* swapgs */ |
188 | 5 | generate_exception_if(!mode_64bit(), X86_EXC_UD); |
189 | 5 | generate_exception_if(!mode_ring0(), X86_EXC_GP, 0); |
190 | 4 | fail_if(!ops->read_msr || !ops->write_msr); |
191 | 1 | if ( (rc = ops->read_msr(MSR_GS_BASE, &sreg.base, |
192 | 1 | ctxt)) != X86EMUL_OKAY || |
193 | 0 | (rc = ops->read_msr(MSR_SHADOW_GS_BASE, &msr_val, |
194 | 0 | ctxt)) != X86EMUL_OKAY ) |
195 | 0 | goto done; |
196 | 1 | if ( (rc = ops->write_msr(MSR_SHADOW_GS_BASE, sreg.base, |
197 | 1 | ctxt, false)) != X86EMUL_OKAY || |
198 | 0 | (rc = ops->write_msr(MSR_GS_BASE, msr_val, |
199 | 0 | ctxt, false)) != X86EMUL_OKAY ) |
200 | 0 | { |
201 | | /* |
202 | | * In real hardware, access to the registers cannot fail. It is |
203 | | * an error in Xen if the writes fail given that both MSRs have |
204 | | * equivalent checks. |
205 | | */ |
206 | 0 | ASSERT_UNREACHABLE(); |
207 | 0 | x86_emul_reset_event(ctxt); |
208 | 0 | generate_exception(X86_EXC_DF, 0); |
209 | 0 | } |
210 | 1 | break; |
211 | | |
212 | 68 | case 0xf9: /* rdtscp */ |
213 | 68 | fail_if(ops->read_msr == NULL); |
214 | 67 | if ( (rc = ops->read_msr(MSR_TSC_AUX, |
215 | 67 | &msr_val, ctxt)) != X86EMUL_OKAY ) |
216 | 1 | goto done; |
217 | 66 | regs->r(cx) = (uint32_t)msr_val; |
218 | 66 | return X86EMUL_rdtsc; |
219 | | |
220 | 1.01k | case 0xfc: /* clzero */ |
221 | 1.01k | { |
222 | 1.01k | unsigned long zero = 0; |
223 | | |
224 | 1.01k | vcpu_must_have(clzero); |
225 | | |
226 | 1.01k | base = ad_bytes == 8 ? regs->r(ax) : |
227 | 1.01k | ad_bytes == 4 ? regs->eax : regs->ax; |
228 | 1.01k | limit = ctxt->cpuid->basic.clflush_size * 8; |
229 | 1.01k | generate_exception_if(limit < sizeof(long) || |
230 | 1.01k | (limit & (limit - 1)), X86_EXC_UD); |
231 | 1.01k | base &= ~(limit - 1); |
232 | 1.01k | if ( ops->rep_stos ) |
233 | 799 | { |
234 | 799 | unsigned long nr_reps = limit / sizeof(zero); |
235 | | |
236 | 799 | rc = ops->rep_stos(&zero, s->ea.mem.seg, base, sizeof(zero), |
237 | 799 | &nr_reps, ctxt); |
238 | 799 | if ( rc == X86EMUL_OKAY ) |
239 | 589 | { |
240 | 589 | base += nr_reps * sizeof(zero); |
241 | 589 | limit -= nr_reps * sizeof(zero); |
242 | 589 | } |
243 | 210 | else if ( rc != X86EMUL_UNHANDLEABLE ) |
244 | 11 | goto done; |
245 | 799 | } |
246 | 1.00k | fail_if(limit && !ops->write); |
247 | 4.20k | while ( limit ) |
248 | 3.21k | { |
249 | 3.21k | rc = ops->write(s->ea.mem.seg, base, &zero, sizeof(zero), ctxt); |
250 | 3.21k | if ( rc != X86EMUL_OKAY ) |
251 | 17 | goto done; |
252 | 3.19k | base += sizeof(zero); |
253 | 3.19k | limit -= sizeof(zero); |
254 | 3.19k | } |
255 | 985 | break; |
256 | 1.00k | } |
257 | | |
258 | 985 | #define _GRP7(mod, reg) \ |
259 | 76.2k | (((mod) << 6) | ((reg) << 3)) ... (((mod) << 6) | ((reg) << 3) | 7) |
260 | 39.5k | #define GRP7_MEM(reg) _GRP7(0, reg): case _GRP7(1, reg): case _GRP7(2, reg) |
261 | 12.5k | #define GRP7_ALL(reg) GRP7_MEM(reg): case _GRP7(3, reg) |
262 | | |
263 | 7.56k | case GRP7_MEM(0): /* sgdt */ |
264 | 16.4k | case GRP7_MEM(1): /* sidt */ |
265 | 16.4k | ASSERT(s->ea.type == OP_MEM); |
266 | 16.4k | generate_exception_if(umip_active(ctxt, ops), X86_EXC_GP, 0); |
267 | 9.01k | fail_if(!ops->read_segment || !ops->write); |
268 | 8.99k | if ( (rc = ops->read_segment(seg, &sreg, ctxt)) ) |
269 | 0 | goto done; |
270 | 8.99k | if ( mode_64bit() ) |
271 | 2.37k | s->op_bytes = 8; |
272 | 6.61k | else if ( s->op_bytes == 2 ) |
273 | 4.96k | { |
274 | 4.96k | sreg.base &= 0xffffff; |
275 | 4.96k | s->op_bytes = 4; |
276 | 4.96k | } |
277 | 8.99k | if ( (rc = ops->write(s->ea.mem.seg, s->ea.mem.off, &sreg.limit, |
278 | 8.99k | 2, ctxt)) != X86EMUL_OKAY || |
279 | 8.86k | (rc = ops->write(s->ea.mem.seg, truncate_ea(s->ea.mem.off + 2), |
280 | 8.86k | &sreg.base, s->op_bytes, ctxt)) != X86EMUL_OKAY ) |
281 | 214 | goto done; |
282 | 8.77k | break; |
283 | | |
284 | 8.77k | case GRP7_MEM(2): /* lgdt */ |
285 | 15.1k | case GRP7_MEM(3): /* lidt */ |
286 | 15.1k | ASSERT(s->ea.type == OP_MEM); |
287 | 15.1k | generate_exception_if(!mode_ring0(), X86_EXC_GP, 0); |
288 | 8.16k | fail_if(ops->write_segment == NULL); |
289 | 8.14k | memset(&sreg, 0, sizeof(sreg)); |
290 | 8.14k | if ( (rc = read_ulong(s->ea.mem.seg, s->ea.mem.off, |
291 | 8.14k | &limit, 2, ctxt, ops)) || |
292 | 7.92k | (rc = read_ulong(s->ea.mem.seg, truncate_ea(s->ea.mem.off + 2), |
293 | 7.92k | &base, mode_64bit() ? 8 : 4, ctxt, ops)) ) |
294 | 273 | goto done; |
295 | 8.14k | generate_exception_if(!is_canonical_address(base), X86_EXC_GP, 0); |
296 | 7.86k | sreg.base = base; |
297 | 7.86k | sreg.limit = limit; |
298 | 7.86k | if ( !mode_64bit() && s->op_bytes == 2 ) |
299 | 6.68k | sreg.base &= 0xffffff; |
300 | 7.86k | if ( (rc = ops->write_segment(seg, &sreg, ctxt)) ) |
301 | 8 | goto done; |
302 | 7.85k | break; |
303 | | |
304 | 14.9k | case GRP7_ALL(4): /* smsw */ |
305 | 14.9k | generate_exception_if(umip_active(ctxt, ops), X86_EXC_GP, 0); |
306 | 14.9k | if ( s->ea.type == OP_MEM ) |
307 | 4.87k | { |
308 | 4.87k | fail_if(!ops->write); |
309 | 4.86k | s->desc |= Mov; /* force writeback */ |
310 | 4.86k | s->ea.bytes = 2; |
311 | 4.86k | } |
312 | 1.58k | else |
313 | 1.58k | s->ea.bytes = s->op_bytes; |
314 | 6.44k | *dst = s->ea; |
315 | 6.44k | fail_if(ops->read_cr == NULL); |
316 | 6.44k | if ( (rc = ops->read_cr(0, &dst->val, ctxt)) ) |
317 | 0 | goto done; |
318 | 6.44k | break; |
319 | | |
320 | 14.4k | case GRP7_ALL(6): /* lmsw */ |
321 | 14.4k | fail_if(ops->read_cr == NULL); |
322 | 6.07k | fail_if(ops->write_cr == NULL); |
323 | 6.07k | generate_exception_if(!mode_ring0(), X86_EXC_GP, 0); |
324 | 6.07k | if ( (rc = ops->read_cr(0, &cr0, ctxt)) ) |
325 | 0 | goto done; |
326 | 6.06k | if ( s->ea.type == OP_REG ) |
327 | 1.22k | cr0w = *s->ea.reg; |
328 | 4.83k | else if ( (rc = read_ulong(s->ea.mem.seg, s->ea.mem.off, |
329 | 4.83k | &cr0w, 2, ctxt, ops)) ) |
330 | 90 | goto done; |
331 | | /* LMSW can: (1) set bits 0-3; (2) clear bits 1-3. */ |
332 | 5.97k | cr0 = (cr0 & ~0xe) | (cr0w & 0xf); |
333 | 5.97k | if ( (rc = ops->write_cr(0, cr0, ctxt)) ) |
334 | 36 | goto done; |
335 | 5.94k | break; |
336 | | |
337 | 8.05k | case GRP7_MEM(7): /* invlpg */ |
338 | 8.05k | ASSERT(s->ea.type == OP_MEM); |
339 | 8.05k | generate_exception_if(!mode_ring0(), X86_EXC_GP, 0); |
340 | 4.40k | fail_if(!ops->tlb_op); |
341 | 4.40k | if ( (rc = ops->tlb_op(x86emul_invlpg, s->ea.mem.off, s->ea.mem.seg, |
342 | 4.40k | ctxt)) != X86EMUL_OKAY ) |
343 | 4 | goto done; |
344 | 4.39k | break; |
345 | | |
346 | 4.39k | #undef GRP7_ALL |
347 | 4.39k | #undef GRP7_MEM |
348 | 4.39k | #undef _GRP7 |
349 | | |
350 | 4.39k | default: |
351 | 2 | return X86EMUL_UNIMPLEMENTED; |
352 | 36.5k | } |
353 | | |
354 | 35.3k | rc = X86EMUL_OKAY; |
355 | | |
356 | 36.4k | done: |
357 | 36.4k | return rc; |
358 | 35.3k | } |