/src/xen/xen/arch/x86/x86_emulate/0fc7.c
Line | Count | Source |
1 | | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
2 | | /****************************************************************************** |
3 | | * 0fc7.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 | | /* Avoid namespace pollution. */ |
14 | | #undef cmpxchg |
15 | | |
16 | | int x86emul_0fc7(struct x86_emulate_state *s, |
17 | | struct cpu_user_regs *regs, |
18 | | struct operand *dst, |
19 | | struct x86_emulate_ctxt *ctxt, |
20 | | const struct x86_emulate_ops *ops, |
21 | | mmval_t *mmvalp) |
22 | 1.45k | { |
23 | 1.45k | int rc; |
24 | | |
25 | 1.45k | if ( s->ea.type == OP_REG ) |
26 | 520 | { |
27 | 520 | bool __maybe_unused carry; |
28 | | |
29 | 520 | switch ( s->modrm_reg & 7 ) |
30 | 520 | { |
31 | 1 | default: |
32 | 1 | return X86EMUL_UNRECOGNIZED; |
33 | | |
34 | 317 | case 6: /* rdrand */ |
35 | 317 | generate_exception_if(s->vex.pfx >= vex_f3, X86_EXC_UD); |
36 | 317 | vcpu_must_have(rdrand); |
37 | 316 | *dst = s->ea; |
38 | 316 | switch ( s->op_bytes ) |
39 | 316 | { |
40 | 192 | case 2: |
41 | 192 | asm ( "rdrand %w0" ASM_FLAG_OUT(, "; setc %1") |
42 | 192 | : "=r" (dst->val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) ); |
43 | 192 | break; |
44 | 61 | default: |
45 | 61 | #ifdef __x86_64__ |
46 | 61 | asm ( "rdrand %k0" ASM_FLAG_OUT(, "; setc %1") |
47 | 61 | : "=r" (dst->val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) ); |
48 | 61 | break; |
49 | 63 | case 8: |
50 | 63 | #endif |
51 | 63 | asm ( "rdrand %0" ASM_FLAG_OUT(, "; setc %1") |
52 | 63 | : "=r" (dst->val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) ); |
53 | 63 | break; |
54 | 316 | } |
55 | 316 | regs->eflags &= ~EFLAGS_MASK; |
56 | 316 | if ( carry ) |
57 | 316 | regs->eflags |= X86_EFLAGS_CF; |
58 | 316 | break; |
59 | | |
60 | 202 | case 7: /* rdseed / rdpid */ |
61 | 202 | if ( s->vex.pfx == vex_f3 ) /* rdpid */ |
62 | 61 | { |
63 | 61 | uint64_t msr_val; |
64 | | |
65 | 61 | generate_exception_if(s->ea.type != OP_REG, X86_EXC_UD); |
66 | 61 | vcpu_must_have(rdpid); |
67 | 61 | fail_if(!ops->read_msr); |
68 | 60 | if ( (rc = ops->read_msr(MSR_TSC_AUX, &msr_val, |
69 | 60 | ctxt)) != X86EMUL_OKAY ) |
70 | 2 | goto done; |
71 | 58 | *dst = s->ea; |
72 | 58 | dst->val = msr_val; |
73 | 58 | dst->bytes = 4; |
74 | 58 | break; |
75 | 60 | } |
76 | | |
77 | 202 | generate_exception_if(s->vex.pfx >= vex_f3, X86_EXC_UD); |
78 | 141 | vcpu_must_have(rdseed); |
79 | 140 | *dst = s->ea; |
80 | 140 | switch ( s->op_bytes ) |
81 | 140 | { |
82 | 52 | case 2: |
83 | 52 | asm ( "rdseed %w0" ASM_FLAG_OUT(, "; setc %1") |
84 | 52 | : "=r" (dst->val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) ); |
85 | 52 | break; |
86 | 22 | default: |
87 | 22 | #ifdef __x86_64__ |
88 | 22 | asm ( "rdseed %k0" ASM_FLAG_OUT(, "; setc %1") |
89 | 22 | : "=r" (dst->val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) ); |
90 | 22 | break; |
91 | 66 | case 8: |
92 | 66 | #endif |
93 | 66 | asm ( "rdseed %0" ASM_FLAG_OUT(, "; setc %1") |
94 | 66 | : "=r" (dst->val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) ); |
95 | 66 | break; |
96 | 140 | } |
97 | 140 | regs->eflags &= ~EFLAGS_MASK; |
98 | 140 | if ( carry ) |
99 | 140 | regs->eflags |= X86_EFLAGS_CF; |
100 | 140 | break; |
101 | 520 | } |
102 | 520 | } |
103 | 934 | else |
104 | 934 | { |
105 | 934 | union { |
106 | 934 | uint32_t u32[2]; |
107 | 934 | uint64_t u64[2]; |
108 | 934 | } *old, *aux; |
109 | | |
110 | | /* cmpxchg8b/cmpxchg16b */ |
111 | 934 | generate_exception_if((s->modrm_reg & 7) != 1, X86_EXC_UD); |
112 | 934 | fail_if(!ops->cmpxchg); |
113 | 929 | if ( s->rex_prefix & REX_W ) |
114 | 338 | { |
115 | 338 | vcpu_must_have(cx16); |
116 | 338 | generate_exception_if(!is_aligned(s->ea.mem.seg, s->ea.mem.off, 16, |
117 | 338 | ctxt, ops), |
118 | 338 | X86_EXC_GP, 0); |
119 | 338 | s->op_bytes = 16; |
120 | 330 | } |
121 | 591 | else |
122 | 591 | { |
123 | 591 | vcpu_must_have(cx8); |
124 | 591 | s->op_bytes = 8; |
125 | 591 | } |
126 | | |
127 | 929 | old = container_of(&mmvalp->ymm[0], typeof(*old), u64[0]); |
128 | 921 | aux = container_of(&mmvalp->ymm[2], typeof(*aux), u64[0]); |
129 | | |
130 | | /* Get actual old value. */ |
131 | 921 | if ( (rc = ops->read(s->ea.mem.seg, s->ea.mem.off, old, s->op_bytes, |
132 | 921 | ctxt)) != X86EMUL_OKAY ) |
133 | 20 | goto done; |
134 | | |
135 | | /* Get expected value. */ |
136 | 901 | if ( s->op_bytes == 8 ) |
137 | 580 | { |
138 | 580 | aux->u32[0] = regs->eax; |
139 | 580 | aux->u32[1] = regs->edx; |
140 | 580 | } |
141 | 321 | else |
142 | 321 | { |
143 | 321 | aux->u64[0] = regs->r(ax); |
144 | 321 | aux->u64[1] = regs->r(dx); |
145 | 321 | } |
146 | | |
147 | 901 | if ( memcmp(old, aux, s->op_bytes) ) |
148 | 742 | { |
149 | 742 | cmpxchgNb_failed: |
150 | | /* Expected != actual: store actual to rDX:rAX and clear ZF. */ |
151 | 742 | regs->r(ax) = s->op_bytes == 8 ? old->u32[0] : old->u64[0]; |
152 | 742 | regs->r(dx) = s->op_bytes == 8 ? old->u32[1] : old->u64[1]; |
153 | 742 | regs->eflags &= ~X86_EFLAGS_ZF; |
154 | 742 | } |
155 | 159 | else |
156 | 159 | { |
157 | | /* |
158 | | * Expected == actual: Get proposed value, attempt atomic cmpxchg |
159 | | * and set ZF if successful. |
160 | | */ |
161 | 159 | if ( s->op_bytes == 8 ) |
162 | 71 | { |
163 | 71 | aux->u32[0] = regs->ebx; |
164 | 71 | aux->u32[1] = regs->ecx; |
165 | 71 | } |
166 | 88 | else |
167 | 88 | { |
168 | 88 | aux->u64[0] = regs->r(bx); |
169 | 88 | aux->u64[1] = regs->r(cx); |
170 | 88 | } |
171 | | |
172 | 159 | switch ( rc = ops->cmpxchg(s->ea.mem.seg, s->ea.mem.off, old, aux, |
173 | 159 | s->op_bytes, s->lock_prefix, ctxt) ) |
174 | 159 | { |
175 | 152 | case X86EMUL_OKAY: |
176 | 152 | regs->eflags |= X86_EFLAGS_ZF; |
177 | 152 | break; |
178 | | |
179 | 0 | case X86EMUL_CMPXCHG_FAILED: |
180 | 0 | rc = X86EMUL_OKAY; |
181 | 0 | goto cmpxchgNb_failed; |
182 | | |
183 | 7 | default: |
184 | 7 | goto done; |
185 | 159 | } |
186 | 159 | } |
187 | 901 | } |
188 | | |
189 | 1.40k | rc = X86EMUL_OKAY; |
190 | | |
191 | 1.45k | done: |
192 | 1.45k | return rc; |
193 | 1.40k | } |