From: Jan Beulich <jbeulich@suse.com>
To: "xen-devel@lists.xenproject.org" <xen-devel@lists.xenproject.org>
Cc: "Andrew Cooper" <andrew.cooper3@citrix.com>,
"Wei Liu" <wl@xen.org>, "Roger Pau Monné" <roger.pau@citrix.com>
Subject: [PATCH 3/7] x86emul: split off opcode 0fc7 handling
Date: Wed, 11 Aug 2021 14:23:45 +0200 [thread overview]
Message-ID: <fd182db0-6f8d-2591-eab7-bbeeac93157b@suse.com> (raw)
In-Reply-To: <bdc58964-e2a8-af36-1653-41c7146bdfc9@suse.com>
There's a fair amount of sub-cases (with some yet to be implemented), so
a separate function seems warranted.
Code moved gets slightly adjusted in a few places, e.g. replacing EXC_*
by X86_EXC_* (such that EXC_* don't need to move as well; we want these
to be phased out anyway).
Signed-off-by: Jan Beulich <jbeulich@suse.com>
--- a/tools/fuzz/x86_instruction_emulator/Makefile
+++ b/tools/fuzz/x86_instruction_emulator/Makefile
@@ -35,7 +35,7 @@ x86.h := $(addprefix $(XEN_ROOT)/tools/i
x86_emulate.h := x86-emulate.h x86_emulate/x86_emulate.h $(x86.h)
OBJS := fuzz-emul.o x86-emulate.o
-OBJS += x86_emulate/0f01.o x86_emulate/0fae.o
+OBJS += x86_emulate/0f01.o x86_emulate/0fae.o x86_emulate/0fc7.o
# x86-emulate.c will be implicit for both
x86-emulate.o x86-emulate-cov.o: x86_emulate/x86_emulate.c $(x86_emulate.h) x86_emulate/private.h
--- a/tools/tests/x86_emulator/Makefile
+++ b/tools/tests/x86_emulator/Makefile
@@ -251,7 +251,7 @@ xop.h avx512f.h: simd-fma.c
endif # 32-bit override
OBJS := x86-emulate.o cpuid.o test_x86_emulator.o evex-disp8.o predicates.o wrappers.o
-OBJS += x86_emulate/0f01.o x86_emulate/0fae.o
+OBJS += x86_emulate/0f01.o x86_emulate/0fae.o x86_emulate/0fc7.o
$(TARGET): $(OBJS)
$(HOSTCC) $(HOSTCFLAGS) -o $@ $^
--- /dev/null
+++ b/xen/arch/x86/x86_emulate/0fc7.c
@@ -0,0 +1,210 @@
+/******************************************************************************
+ * 0fc7.c - helper for x86_emulate.c
+ *
+ * Generic x86 (32-bit and 64-bit) instruction decoder and emulator.
+ *
+ * Copyright (c) 2005-2007 Keir Fraser
+ * Copyright (c) 2005-2007 XenSource Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "private.h"
+
+/* Avoid namespace pollution. */
+#undef cmpxchg
+
+int x86emul_0fc7(struct x86_emulate_state *s,
+ struct cpu_user_regs *regs,
+ struct operand *dst,
+ struct x86_emulate_ctxt *ctxt,
+ const struct x86_emulate_ops *ops,
+ mmval_t *mmvalp)
+{
+ int rc;
+
+ if ( s->ea.type == OP_REG )
+ {
+ bool __maybe_unused carry;
+
+ switch ( s->modrm_reg & 7 )
+ {
+ default:
+ return X86EMUL_UNRECOGNIZED;
+
+ case 6: /* rdrand */
+#ifdef HAVE_AS_RDRAND
+ generate_exception_if(s->vex.pfx >= vex_f3, X86_EXC_UD);
+ host_and_vcpu_must_have(rdrand);
+ *dst = s->ea;
+ switch ( s->op_bytes )
+ {
+ case 2:
+ asm ( "rdrand %w0" ASM_FLAG_OUT(, "; setc %1")
+ : "=r" (dst->val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) );
+ break;
+ default:
+# ifdef __x86_64__
+ asm ( "rdrand %k0" ASM_FLAG_OUT(, "; setc %1")
+ : "=r" (dst->val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) );
+ break;
+ case 8:
+# endif
+ asm ( "rdrand %0" ASM_FLAG_OUT(, "; setc %1")
+ : "=r" (dst->val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) );
+ break;
+ }
+ regs->eflags &= ~EFLAGS_MASK;
+ if ( carry )
+ regs->eflags |= X86_EFLAGS_CF;
+ break;
+#else
+ return X86EMUL_UNIMPLEMENTED;
+#endif
+
+ case 7: /* rdseed / rdpid */
+ if ( s->vex.pfx == vex_f3 ) /* rdpid */
+ {
+ uint64_t msr_val;
+
+ generate_exception_if(s->ea.type != OP_REG, X86_EXC_UD);
+ vcpu_must_have(rdpid);
+ fail_if(!ops->read_msr);
+ if ( (rc = ops->read_msr(MSR_TSC_AUX, &msr_val,
+ ctxt)) != X86EMUL_OKAY )
+ goto done;
+ *dst = s->ea;
+ dst->val = msr_val;
+ dst->bytes = 4;
+ break;
+ }
+#ifdef HAVE_AS_RDSEED
+ generate_exception_if(s->vex.pfx >= vex_f3, X86_EXC_UD);
+ host_and_vcpu_must_have(rdseed);
+ *dst = s->ea;
+ switch ( s->op_bytes )
+ {
+ case 2:
+ asm ( "rdseed %w0" ASM_FLAG_OUT(, "; setc %1")
+ : "=r" (dst->val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) );
+ break;
+ default:
+# ifdef __x86_64__
+ asm ( "rdseed %k0" ASM_FLAG_OUT(, "; setc %1")
+ : "=r" (dst->val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) );
+ break;
+ case 8:
+# endif
+ asm ( "rdseed %0" ASM_FLAG_OUT(, "; setc %1")
+ : "=r" (dst->val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) );
+ break;
+ }
+ regs->eflags &= ~EFLAGS_MASK;
+ if ( carry )
+ regs->eflags |= X86_EFLAGS_CF;
+ break;
+#endif
+ }
+ }
+ else
+ {
+ union {
+ uint32_t u32[2];
+ uint64_t u64[2];
+ } *old, *aux;
+
+ /* cmpxchg8b/cmpxchg16b */
+ generate_exception_if((s->modrm_reg & 7) != 1, X86_EXC_UD);
+ fail_if(!ops->cmpxchg);
+ if ( s->rex_prefix & REX_W )
+ {
+ host_and_vcpu_must_have(cx16);
+ generate_exception_if(!is_aligned(s->ea.mem.seg, s->ea.mem.off, 16,
+ ctxt, ops),
+ X86_EXC_GP, 0);
+ s->op_bytes = 16;
+ }
+ else
+ {
+ vcpu_must_have(cx8);
+ s->op_bytes = 8;
+ }
+
+ old = container_of(&mmvalp->ymm[0], typeof(*old), u64[0]);
+ aux = container_of(&mmvalp->ymm[2], typeof(*aux), u64[0]);
+
+ /* Get actual old value. */
+ if ( (rc = ops->read(s->ea.mem.seg, s->ea.mem.off, old, s->op_bytes,
+ ctxt)) != X86EMUL_OKAY )
+ goto done;
+
+ /* Get expected value. */
+ if ( s->op_bytes == 8 )
+ {
+ aux->u32[0] = regs->eax;
+ aux->u32[1] = regs->edx;
+ }
+ else
+ {
+ aux->u64[0] = regs->r(ax);
+ aux->u64[1] = regs->r(dx);
+ }
+
+ if ( memcmp(old, aux, s->op_bytes) )
+ {
+ cmpxchgNb_failed:
+ /* Expected != actual: store actual to rDX:rAX and clear ZF. */
+ regs->r(ax) = s->op_bytes == 8 ? old->u32[0] : old->u64[0];
+ regs->r(dx) = s->op_bytes == 8 ? old->u32[1] : old->u64[1];
+ regs->eflags &= ~X86_EFLAGS_ZF;
+ }
+ else
+ {
+ /*
+ * Expected == actual: Get proposed value, attempt atomic cmpxchg
+ * and set ZF if successful.
+ */
+ if ( s->op_bytes == 8 )
+ {
+ aux->u32[0] = regs->ebx;
+ aux->u32[1] = regs->ecx;
+ }
+ else
+ {
+ aux->u64[0] = regs->r(bx);
+ aux->u64[1] = regs->r(cx);
+ }
+
+ switch ( rc = ops->cmpxchg(s->ea.mem.seg, s->ea.mem.off, old, aux,
+ s->op_bytes, s->lock_prefix, ctxt) )
+ {
+ case X86EMUL_OKAY:
+ regs->eflags |= X86_EFLAGS_ZF;
+ break;
+
+ case X86EMUL_CMPXCHG_FAILED:
+ rc = X86EMUL_OKAY;
+ goto cmpxchgNb_failed;
+
+ default:
+ goto done;
+ }
+ }
+ }
+
+ rc = X86EMUL_OKAY;
+
+ done:
+ return rc;
+}
--- a/xen/arch/x86/x86_emulate/Makefile
+++ b/xen/arch/x86/x86_emulate/Makefile
@@ -1,2 +1,3 @@
obj-y += 0f01.o
obj-y += 0fae.o
+obj-y += 0fc7.o
--- a/xen/arch/x86/x86_emulate/private.h
+++ b/xen/arch/x86/x86_emulate/private.h
@@ -308,6 +308,14 @@ struct x86_emulate_state {
#endif
};
+typedef union {
+ uint64_t mmx;
+ uint64_t __attribute__ ((aligned(16))) xmm[2];
+ uint64_t __attribute__ ((aligned(32))) ymm[4];
+ uint64_t __attribute__ ((aligned(64))) zmm[8];
+ uint32_t data32[16];
+} mmval_t;
+
struct x86_fxsr {
uint16_t fcw;
uint16_t fsw;
@@ -558,6 +566,12 @@ int x86emul_0fae(struct x86_emulate_stat
struct x86_emulate_ctxt *ctxt,
const struct x86_emulate_ops *ops,
enum x86_emulate_fpu_type *fpu_type);
+int x86emul_0fc7(struct x86_emulate_state *s,
+ struct cpu_user_regs *regs,
+ struct operand *dst,
+ struct x86_emulate_ctxt *ctxt,
+ const struct x86_emulate_ops *ops,
+ mmval_t *mmvalp);
static inline bool is_aligned(enum x86_segment seg, unsigned long offs,
unsigned int size, struct x86_emulate_ctxt *ctxt,
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -687,17 +687,9 @@ struct x87_env32 {
};
#endif
-typedef union {
- uint64_t mmx;
- uint64_t __attribute__ ((aligned(16))) xmm[2];
- uint64_t __attribute__ ((aligned(32))) ymm[4];
- uint64_t __attribute__ ((aligned(64))) zmm[8];
- uint32_t data32[16];
-} mmval_t;
-
/*
- * While proper alignment gets specified above, this doesn't get honored by
- * the compiler for automatic variables. Use this helper to instantiate a
+ * While proper alignment gets specified in mmval_t, this doesn't get honored
+ * by the compiler for automatic variables. Use this helper to instantiate a
* suitably aligned variable, producing a pointer to access it.
*/
#define DECLARE_ALIGNED(type, var) \
@@ -7681,174 +7673,8 @@ x86_emulate(
#endif /* X86EMUL_NO_SIMD */
case X86EMUL_OPC(0x0f, 0xc7): /* Grp9 */
- {
- union {
- uint32_t u32[2];
- uint64_t u64[2];
- } *old, *aux;
-
- if ( ea.type == OP_REG )
- {
- bool __maybe_unused carry;
-
- switch ( modrm_reg & 7 )
- {
- default:
- goto unrecognized_insn;
-
- case 6: /* rdrand */
-#ifdef HAVE_AS_RDRAND
- generate_exception_if(rep_prefix(), EXC_UD);
- host_and_vcpu_must_have(rdrand);
- dst = ea;
- switch ( op_bytes )
- {
- case 2:
- asm ( "rdrand %w0" ASM_FLAG_OUT(, "; setc %1")
- : "=r" (dst.val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) );
- break;
- default:
-# ifdef __x86_64__
- asm ( "rdrand %k0" ASM_FLAG_OUT(, "; setc %1")
- : "=r" (dst.val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) );
- break;
- case 8:
-# endif
- asm ( "rdrand %0" ASM_FLAG_OUT(, "; setc %1")
- : "=r" (dst.val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) );
- break;
- }
- _regs.eflags &= ~EFLAGS_MASK;
- if ( carry )
- _regs.eflags |= X86_EFLAGS_CF;
- break;
-#else
- goto unimplemented_insn;
-#endif
-
- case 7: /* rdseed / rdpid */
- if ( repe_prefix() ) /* rdpid */
- {
- generate_exception_if(ea.type != OP_REG, EXC_UD);
- vcpu_must_have(rdpid);
- fail_if(!ops->read_msr);
- if ( (rc = ops->read_msr(MSR_TSC_AUX, &msr_val,
- ctxt)) != X86EMUL_OKAY )
- goto done;
- dst = ea;
- dst.val = msr_val;
- dst.bytes = 4;
- break;
- }
-#ifdef HAVE_AS_RDSEED
- generate_exception_if(rep_prefix(), EXC_UD);
- host_and_vcpu_must_have(rdseed);
- dst = ea;
- switch ( op_bytes )
- {
- case 2:
- asm ( "rdseed %w0" ASM_FLAG_OUT(, "; setc %1")
- : "=r" (dst.val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) );
- break;
- default:
-# ifdef __x86_64__
- asm ( "rdseed %k0" ASM_FLAG_OUT(, "; setc %1")
- : "=r" (dst.val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) );
- break;
- case 8:
-# endif
- asm ( "rdseed %0" ASM_FLAG_OUT(, "; setc %1")
- : "=r" (dst.val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) );
- break;
- }
- _regs.eflags &= ~EFLAGS_MASK;
- if ( carry )
- _regs.eflags |= X86_EFLAGS_CF;
- break;
-#endif
- }
- break;
- }
-
- /* cmpxchg8b/cmpxchg16b */
- generate_exception_if((modrm_reg & 7) != 1, EXC_UD);
- fail_if(!ops->cmpxchg);
- if ( rex_prefix & REX_W )
- {
- host_and_vcpu_must_have(cx16);
- generate_exception_if(!is_aligned(ea.mem.seg, ea.mem.off, 16,
- ctxt, ops),
- EXC_GP, 0);
- op_bytes = 16;
- }
- else
- {
- vcpu_must_have(cx8);
- op_bytes = 8;
- }
-
- old = container_of(&mmvalp->ymm[0], typeof(*old), u64[0]);
- aux = container_of(&mmvalp->ymm[2], typeof(*aux), u64[0]);
-
- /* Get actual old value. */
- if ( (rc = ops->read(ea.mem.seg, ea.mem.off, old, op_bytes,
- ctxt)) != X86EMUL_OKAY )
- goto done;
-
- /* Get expected value. */
- if ( !(rex_prefix & REX_W) )
- {
- aux->u32[0] = _regs.eax;
- aux->u32[1] = _regs.edx;
- }
- else
- {
- aux->u64[0] = _regs.r(ax);
- aux->u64[1] = _regs.r(dx);
- }
-
- if ( memcmp(old, aux, op_bytes) )
- {
- cmpxchgNb_failed:
- /* Expected != actual: store actual to rDX:rAX and clear ZF. */
- _regs.r(ax) = !(rex_prefix & REX_W) ? old->u32[0] : old->u64[0];
- _regs.r(dx) = !(rex_prefix & REX_W) ? old->u32[1] : old->u64[1];
- _regs.eflags &= ~X86_EFLAGS_ZF;
- }
- else
- {
- /*
- * Expected == actual: Get proposed value, attempt atomic cmpxchg
- * and set ZF if successful.
- */
- if ( !(rex_prefix & REX_W) )
- {
- aux->u32[0] = _regs.ebx;
- aux->u32[1] = _regs.ecx;
- }
- else
- {
- aux->u64[0] = _regs.r(bx);
- aux->u64[1] = _regs.r(cx);
- }
-
- switch ( rc = ops->cmpxchg(ea.mem.seg, ea.mem.off, old, aux,
- op_bytes, lock_prefix, ctxt) )
- {
- case X86EMUL_OKAY:
- _regs.eflags |= X86_EFLAGS_ZF;
- break;
-
- case X86EMUL_CMPXCHG_FAILED:
- rc = X86EMUL_OKAY;
- goto cmpxchgNb_failed;
-
- default:
- goto done;
- }
- }
- break;
- }
+ rc = x86emul_0fc7(state, &_regs, &dst, ctxt, ops, mmvalp);
+ goto dispatch_from_helper;
case X86EMUL_OPC(0x0f, 0xc8) ... X86EMUL_OPC(0x0f, 0xcf): /* bswap */
dst.type = OP_REG;
next prev parent reply other threads:[~2021-08-11 12:24 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-08-11 12:21 [PATCH 0/7] x86emul: a few small steps towards disintegration Jan Beulich
2021-08-11 12:22 ` [PATCH 1/7] x86emul: split off opcode 0f01 handling Jan Beulich
2021-08-11 12:23 ` [PATCH 2/7] x86emul: split off opcode 0fae handling Jan Beulich
2021-08-11 12:23 ` Jan Beulich [this message]
2021-08-11 12:24 ` [PATCH 4/7] x86emul: split off FPU opcode handling Jan Beulich
2021-08-11 12:24 ` [PATCH 5/7] x86emul: split off insn decoding Jan Beulich
2021-08-11 12:25 ` [PATCH 6/7] x86emul: move x86_emul_blk() to separate source file Jan Beulich
2021-08-11 12:25 ` [PATCH 7/7] x86emul: move various utility functions to separate source files Jan Beulich
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=fd182db0-6f8d-2591-eab7-bbeeac93157b@suse.com \
--to=jbeulich@suse.com \
--cc=andrew.cooper3@citrix.com \
--cc=roger.pau@citrix.com \
--cc=wl@xen.org \
--cc=xen-devel@lists.xenproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).