|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH 08/30] x86emul: support REX2-prefixed insns
This primarily involves folding the various bits into the register
number calculations, plus the handling of REX2.M (when set). And of
course eGPR values which have changed need writing back into their
respective registers.
Note that copy_REX_VEX() doesn't need any adjustment: REX2.{R,B}4 are
ignored when MMX/XMM registers are accessed, and we convert any GPR uses
(including base, with index never used) to fixed registers in the low 8
legacy set (clearing REX.{R,B} accordingly as well).
Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>
---
Perhaps the respective literal number uses in the reg/base/index
calculations should also be converted to use REX_{R,X,B}, to help
readability (sacrificing a tiny bit of code efficiency).
The recurring calculation of the register number when embedded in the
major opcode may want to have a helper function introduced?
While we don't support XSAVE et al yet, I wonder whether - alongside the
row exclusions - their special casing should be added here. Handling in
the actual emulation (when adding support) would likely be less extra
logic, though.
As to the row exclusions: The spec specifically mentions them, which
makes ambiguous what the situation is for certain other insns. From the
spec: "All instructions in legacy maps 0 and 1 that have explicit GPR or
memory operands can use the REX2 prefix to access the upper 16 GPRs
(namely, R16 to R31)." This statement alone would be sufficient to cover
any rows where no insns exist at all with explicit GPR/mem operands
(the first 4 insns in row 0xA are kind of a special case, though). With
the "forbidden" rows enumerated explicitly, it becomes ambiguous whether
such insns outside those rows (e.g. PUSH <imm>) would ignore REX2, or
whether they would instead #UD.
Like in "x86emul: replace earlier relaxation of decode_gpr()" the actual
eGPR accesses could also be done with less inline assembly, but the
readbility gains would likely be similarly marginal here.
--- a/xen/arch/x86/x86_emulate/decode.c
+++ b/xen/arch/x86/x86_emulate/decode.c
@@ -1111,6 +1111,27 @@ int x86emul_decode(struct x86_emulate_st
goto done_prefixes;
s->rex_prefix = b;
continue;
+ case 0xd5:
+ if ( (s->xcr0 & X86_XCR0_APX) && !s->rex_prefix )
+ {
+ b = insn_fetch_type(uint8_t);
+ /*
+ * The bits having a REX equivalent are supposed to be consumed
+ * from ->rex_prefix. Move the REX2 counterpart bits (R4, X4,
+ * and B4) to REX equivalent positions. To be usable in
boolean
+ * contexts as well as with copy_REX_VEX(), OR in REX_PREFIX.
+ */
+ s->rex_prefix = REX_PREFIX |
+ (b & (REX_W | REX_R | REX_X | REX_B));
+ s->rex2_prefix = REX_PREFIX | (b >> 4) | (b & REX2_M);
+
+ /*
+ * REX2 is always followed by the major opcode byte; no more
+ * prefix bytes follow.
+ */
+ b = insn_fetch_type(uint8_t);
+ }
+ fallthrough;
default:
goto done_prefixes;
}
@@ -1125,7 +1146,32 @@ int x86emul_decode(struct x86_emulate_st
/* Opcode byte(s). */
d = opcode_table[b];
- if ( b == 0x0f )
+ if ( s->rex2_prefix )
+ {
+ opcode = b;
+ if ( s->rex2_prefix & REX2_M )
+ {
+ d = twobyte_table[b].desc;
+ opcode |= MASK_INSR(0x0f, X86EMUL_OPC_EXT_MASK);
+ s->ext = ext_0f;
+
+ /* Rows 3 and 8 don't have their legacy meaning. */
+ if ( (0x0108 >> (b >> 4)) & 1 )
+ {
+ d = 0;
+ opcode |= X86EMUL_OPC_REX2;
+ }
+ else
+ s->simd_size = twobyte_table[b].size;
+ }
+ /* Rows 4, 7, A, and E don't have their legacy meaning. */
+ else if ( (0x4490 >> (b >> 4)) & 1 )
+ {
+ d = 0;
+ opcode |= X86EMUL_OPC_REX2;
+ }
+ }
+ else if ( b == 0x0f )
{
/* Two-byte opcode. */
b = insn_fetch_type(uint8_t);
@@ -1158,8 +1204,9 @@ int x86emul_decode(struct x86_emulate_st
s->modrm = insn_fetch_type(uint8_t);
s->modrm_mod = (s->modrm & 0xc0) >> 6;
- if ( !s->ext && ((b & ~1) == 0xc4 || (b == 0x8f && (s->modrm & 0x18))
||
- b == 0x62) )
+ if ( !s->ext && !s->rex2_prefix &&
+ ((b & ~1) == 0xc4 || (b == 0x8f && (s->modrm & 0x18)) ||
+ b == 0x62) )
switch ( def_ad_bytes )
{
default:
@@ -1340,7 +1387,8 @@ int x86emul_decode(struct x86_emulate_st
d &= ~ModRM;
#undef ModRM /* Only its aliases are valid to use from here on. */
s->modrm_reg = ((s->rex_prefix & 4) << 1) | ((s->modrm & 0x38) >> 3) |
- ((evex_encoded() && !s->evex.R) << 4);
+ ((evex_encoded() ? !s->evex.R
+ : !!(s->rex2_prefix & REX_R)) << 4);
s->modrm_rm = s->modrm & 0x07;
/*
@@ -1775,7 +1823,8 @@ int x86emul_decode(struct x86_emulate_st
{
generate_exception_if(d & vSIB, X86_EXC_UD);
s->modrm_rm |= ((s->rex_prefix & 1) << 3) |
- ((evex_encoded() && !s->evex.x) << 4);
+ ((evex_encoded() ? !s->evex.x
+ : !!(s->rex2_prefix & REX_B)) <<
4);
s->ea.type = OP_REG;
}
else if ( ad_bytes == 2 )
@@ -1836,9 +1885,11 @@ int x86emul_decode(struct x86_emulate_st
if ( s->modrm_rm == 4 )
{
uint8_t sib = insn_fetch_type(uint8_t);
- uint8_t sib_base = (sib & 7) | ((s->rex_prefix << 3) & 8);
+ uint8_t sib_base = (sib & 7) | ((s->rex_prefix << 3) & 8) |
+ (!!(s->rex2_prefix & REX_B) << 4);
- s->sib_index = ((sib >> 3) & 7) | ((s->rex_prefix << 2) & 8);
+ s->sib_index = ((sib >> 3) & 7) | ((s->rex_prefix << 2) & 8) |
+ (!!(s->rex2_prefix & REX_X) << 4);
s->sib_scale = (sib >> 6) & 3;
if ( unlikely(d & vSIB) )
s->sib_index |= (mode_64bit() && evex_encoded() &&
@@ -1870,7 +1921,8 @@ int x86emul_decode(struct x86_emulate_st
else
{
generate_exception_if(d & vSIB, X86_EXC_UD);
- s->modrm_rm |= (s->rex_prefix & 1) << 3;
+ s->modrm_rm |= ((s->rex_prefix & 1) << 3) |
+ (!!(s->rex2_prefix & REX_B) << 4);
s->ea.mem.off = *decode_gpr(ctxt->regs, &s->egpr, s->modrm_rm);
if ( (s->modrm_rm == 5) && (s->modrm_mod != 0) )
s->ea.mem.seg = x86_seg_ss;
--- a/xen/arch/x86/x86_emulate/private.h
+++ b/xen/arch/x86/x86_emulate/private.h
@@ -122,12 +122,14 @@ struct operand {
#define REX_X 0x02
#define REX_R 0x04
#define REX_W 0x08
+#define REX2_M 0x80
#elif defined(__i386__)
#define REX_PREFIX 0
#define REX_B 0
#define REX_X 0
#define REX_R 0
#define REX_W 0
+#define REX2_M 0
#endif
enum simd_opsize {
@@ -315,6 +317,7 @@ struct x86_emulate_state {
uint8_t modrm, modrm_mod, modrm_reg, modrm_rm;
uint8_t sib_index, sib_scale;
uint8_t rex_prefix;
+ uint8_t rex2_prefix;
bool lock_prefix;
bool not_64bit; /* Instruction not available in 64bit. */
bool fpu_ctrl; /* Instruction is an FPU control one. */
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -1754,13 +1754,15 @@ x86_emulate(
case 0x50 ... 0x57: /* push reg */
src.val = *decode_gpr(&_regs, &state->egpr,
- (b & 7) | ((rex_prefix & 1) << 3));
+ ((b & 7) | ((rex_prefix & 1) << 3) |
+ (!!(state->rex2_prefix & REX_B) << 4)));
goto push;
case 0x58 ... 0x5f: /* pop reg */
dst.type = OP_REG;
dst.reg = decode_gpr(&_regs, &state->egpr,
- (b & 7) | ((rex_prefix & 1) << 3));
+ ((b & 7) | ((rex_prefix & 1) << 3) |
+ (!!(state->rex2_prefix & REX_B) << 4)));
dst.bytes = op_bytes;
if ( mode_64bit() && (dst.bytes == 4) )
dst.bytes = 8;
@@ -2078,7 +2080,7 @@ x86_emulate(
case 0x90: /* nop / xchg %%r8,%%rax */
case X86EMUL_OPC_F3(0, 0x90): /* pause / xchg %%r8,%%rax */
- if ( !(rex_prefix & REX_B) )
+ if ( !((rex_prefix | state->rex2_prefix) & REX_B) )
break; /* nop / pause */
/* fall through */
@@ -2086,7 +2088,8 @@ x86_emulate(
dst.type = OP_REG;
dst.bytes = op_bytes;
dst.reg = decode_gpr(&_regs, &state->egpr,
- (b & 7) | ((rex_prefix & 1) << 3));
+ ((b & 7) | ((rex_prefix & 1) << 3) |
+ (!!(state->rex2_prefix & REX_B) << 4)));
dst.val = *dst.reg;
goto xchg;
@@ -2333,14 +2336,16 @@ x86_emulate(
case 0xb0 ... 0xb7: /* mov imm8,r8 */
dst.reg = _decode_gpr(&_regs, &state->egpr,
- (b & 7) | ((rex_prefix & 1) << 3),
+ ((b & 7) | ((rex_prefix & 1) << 3) |
+ (!!(state->rex2_prefix & REX_B) << 4)),
!rex_prefix);
dst.val = src.val;
break;
case 0xb8 ... 0xbf: /* mov imm{16,32,64},r{16,32,64} */
dst.reg = decode_gpr(&_regs, &state->egpr,
- (b & 7) | ((rex_prefix & 1) << 3));
+ ((b & 7) | ((rex_prefix & 1) << 3) |
+ (!!(state->rex2_prefix & REX_B) << 4)));
dst.val = src.val;
break;
@@ -5534,7 +5539,8 @@ x86_emulate(
case X86EMUL_OPC(0x0f, 0xc8) ... X86EMUL_OPC(0x0f, 0xcf): /* bswap */
dst.type = OP_REG;
dst.reg = decode_gpr(&_regs, &state->egpr,
- (b & 7) | ((rex_prefix & 1) << 3));
+ ((b & 7) | ((rex_prefix & 1) << 3)) |
+ (!!(state->rex2_prefix & REX_B) << 4));
switch ( dst.bytes = op_bytes )
{
default: /* case 2: */
@@ -8800,7 +8806,49 @@ x86_emulate(
}
if ( rc != X86EMUL_DONE )
+ {
+ unsigned int i;
+
*ctxt->regs = _regs;
+
+ /* Put changed eGPR values back into the registers themselves. */
+ for ( i = 0; i < ARRAY_SIZE(state->egpr.regno); ++i )
+ {
+ unsigned int reg = state->egpr.regno[i];
+
+ if ( !reg )
+ break;
+
+ if ( state->egpr.val[i] == state->egpr.orig[i] )
+ continue;
+
+ asm volatile ( "sub $17, %[reg]\n\t"
+
+ /* cmovc (%rdi), %r16 */
+ ".byte 0xd5, 0xc8, 0x42, 0x07\n\t"
+
+ /* cmovz (%rdi), %r17 */
+ ".byte 0xd5, 0xc8, 0x44, 0x0f\n\t"
+
+ ".irp n, 18, 19, 20, 21, 22, 23,"
+ " 24, 25, 26, 27, 28, 29, 30\n\t"
+
+ "dec %[reg]\n\t"
+
+ /* cmovz (%rdi), %r\n */
+ ".byte 0xd5, 0xc8 + ((\\n & 8) >> 1)\n\t"
+ ".byte 0x44, 0x07 + ((\\n & 7) << 3)\n\t"
+
+ ".endr\n\t"
+
+ /* cmovg (%rdi), %r31 */
+ ".byte 0xd5, 0xcc, 0x4f, 0x3f"
+
+ : [reg] "+r" (reg)
+ : "m" (state->egpr.val[i]),
+ "D" (&state->egpr.val[i]) );
+ }
+ }
else
{
ctxt->regs->r(ip) = _regs.r(ip);
--- a/xen/arch/x86/x86_emulate/x86_emulate.h
+++ b/xen/arch/x86/x86_emulate/x86_emulate.h
@@ -586,6 +586,11 @@ struct x86_emulate_ctxt
(X86EMUL_OPC_F3(ext, byte) | X86EMUL_OPC_EVEX_)
# define X86EMUL_OPC_EVEX_F2(ext, byte) \
(X86EMUL_OPC_F2(ext, byte) | X86EMUL_OPC_EVEX_)
+/*
+ * This is used only for opcode rows that don't have their legacy meaning when
+ * used with REX2.
+ */
+#define X86EMUL_OPC_REX2 0x00003000
#define X86EMUL_OPC_XOP(ext, byte) X86EMUL_OPC(0x8f##ext, byte)
#define X86EMUL_OPC_XOP_66(ext, byte) X86EMUL_OPC_66(0x8f##ext, byte)
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |