Coverage Report

Created: 2026-09-29 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}