|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH 17/30] x86emul: support APX SCC insns
Note that memory forms of these insns access memory even if the
condition evaluates to false, simplifying the handling for us.
Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>
--- a/tools/tests/x86_emulator/evex-disp8.c
+++ b/tools/tests/x86_emulator/evex-disp8.c
@@ -1068,11 +1068,28 @@ static const struct test apx_f[] = {
COND( o, 0), COND( p, 10), COND( s, 8), COND( z, 4)
static const struct test apx_nci[] = {
+ INSN(ccmps, , map4, 39, el, dq, no),
+ INSN(ccmps, , map4, 3b, el, dq, no),
+ INSNX(ccmps, , map4, 81, 7, el, dq, no),
+ INSNX(ccmps, , map4, 83, 7, el, dq, no),
+ INSN(ccmpsb, , map4, 38, el, b, no),
+ INSN(ccmpsb, , map4, 3a, el, b, no),
+ INSNX(ccmpsb, , map4, 80, 7, el, b, no),
+ INSN(ccmpsw, 66, map4, 39, el, w, no),
+ INSN(ccmpsw, 66, map4, 3b, el, w, no),
+ INSNX(ccmpsw, 66, map4, 81, 7, el, w, no),
+ INSNX(ccmpsw, 66, map4, 83, 7, el, w, no),
#define COND(c, n) \
INSN(cfcmov##c, , map4, 40 | (n), el, dq, no), \
INSN(cfcmov##c, 66, map4, 40 | (n), el, w, no)
CONDS(),
#undef COND
+ INSN(ctests, , map4, 85, el, dq, no),
+ INSNX(ctests, , map4, f7, 0, el, dq, no),
+ INSN(ctestsb, , map4, 84, el, b, no),
+ INSNX(ctestsb, , map4, f6, 0, el, b, no),
+ INSN(ctestsw, 66, map4, 85, el, w, no),
+ INSNX(ctestsw, 66, map4, f7, 0, el, w, no),
#define COND(c, n) \
INSN(set##c, f2, map4, 40 | (n), el, b, no)
CONDS(),
@@ -1350,6 +1367,16 @@ static void test_one(const struct test *
* provides as divisor. Just set the entire %rax to zero.
*/
ctxt->regs->eax = 0;
+ /* fall through */
+ case 0xf7:
+ if ( test->ext <= 1 )
+ {
+ /*
+ * Unlike other insns in this group CTESTscc also has an
+ * immediate operand.
+ */
+ imm = test->opc & 1 ? test->pfx != PFX_66 ? 4 : 2 : 1;
+ }
break;
}
}
--- a/tools/tests/x86_emulator/predicates.c
+++ b/tools/tests/x86_emulator/predicates.c
@@ -1642,6 +1642,7 @@ static const struct evex {
uint8_t l:3;
bool mask:1;
bool nd:1;
+ bool scc:1;
#define L2 4
#define Ln (L0 | L1 | L2)
} evex_0f[] = {
@@ -2130,6 +2131,7 @@ static const struct evex {
}, evex_map4[] = {
#ifdef __x86_64__
#define ND .nd = true
+#define SCC .scc = true
{ { 0x00 }, 2, T, W, pfx_no, WIG, L0, ND }, /* add */
{ { 0x01 }, 2, T, W, pfx_no, Wn, L0, ND }, /* add */
{ { 0x01 }, 2, T, W, pfx_66, Wn, L0, ND }, /* add */
@@ -2176,6 +2178,12 @@ static const struct evex {
{ { 0x32 }, 2, T, R, pfx_no, WIG, L0, ND }, /* xor */
{ { 0x33 }, 2, T, R, pfx_no, Wn, L0, ND }, /* xor */
{ { 0x33 }, 2, T, R, pfx_66, Wn, L0, ND }, /* xor */
+ { { 0x38 }, 2, T, R, pfx_no, WIG, L0, SCC }, /* ccmp<scc> */
+ { { 0x39 }, 2, T, R, pfx_no, Wn, L0, SCC }, /* ccmp<scc> */
+ { { 0x39 }, 2, T, R, pfx_66, Wn, L0, SCC }, /* ccmp<scc> */
+ { { 0x3a }, 2, T, R, pfx_no, WIG, L0, SCC }, /* ccmp<scc> */
+ { { 0x3b }, 2, T, R, pfx_no, Wn, L0, SCC }, /* ccmp<scc> */
+ { { 0x3b }, 2, T, R, pfx_66, Wn, L0, SCC }, /* ccmp<scc> */
CND(0x40, 2, T, R, pfx_no, Wn, L0, ND ), /* {,cf}cmov<cc> */ \
CND(0x40, 2, T, R, pfx_66, Wn, L0, ND ), /* {,cf}cmov<cc> */ \
CND(0x40, 2, T, W, pfx_f2, WIG, L0 ), /* set<cc> */
@@ -2191,6 +2199,7 @@ static const struct evex {
{ { 0x80, 0x20 }, 3, T, W, pfx_no, WIG, L0, ND }, /* and */
{ { 0x80, 0x28 }, 3, T, W, pfx_no, WIG, L0, ND }, /* sub */
{ { 0x80, 0x30 }, 3, T, W, pfx_no, WIG, L0, ND }, /* xor */
+ { { 0x80, 0x38 }, 3, T, R, pfx_no, WIG, L0, SCC }, /* ccmp<scc> */
{ { 0x81, 0x00 }, 6, T, W, pfx_no, Wn, L0, ND }, /* add */
{ { 0x81, 0x00 }, 4, T, W, pfx_66, W0, L0, ND }, /* add */
{ { 0x81, 0x00 }, 6, T, W, pfx_66, W1, L0, ND }, /* add */
@@ -2212,6 +2221,9 @@ static const struct evex {
{ { 0x81, 0x30 }, 6, T, W, pfx_no, Wn, L0, ND }, /* xor */
{ { 0x81, 0x30 }, 4, T, W, pfx_66, W0, L0, ND }, /* xor */
{ { 0x81, 0x30 }, 6, T, W, pfx_66, W1, L0, ND }, /* xor */
+ { { 0x81, 0x38 }, 6, T, R, pfx_no, Wn, L0, SCC }, /* ccmp<scc> */
+ { { 0x81, 0x38 }, 4, T, R, pfx_66, W0, L0, SCC }, /* ccmp<scc> */
+ { { 0x81, 0x38 }, 6, T, R, pfx_66, W1, L0, SCC }, /* ccmp<scc> */
{ { 0x83, 0x00 }, 3, T, W, pfx_no, Wn, L0, ND }, /* add */
{ { 0x83, 0x00 }, 3, T, W, pfx_66, Wn, L0, ND }, /* add */
{ { 0x83, 0x08 }, 3, T, W, pfx_no, Wn, L0, ND }, /* or */
@@ -2226,6 +2238,11 @@ static const struct evex {
{ { 0x83, 0x28 }, 3, T, W, pfx_66, Wn, L0, ND }, /* sub */
{ { 0x83, 0x30 }, 3, T, W, pfx_no, Wn, L0, ND }, /* xor */
{ { 0x83, 0x30 }, 3, T, W, pfx_66, Wn, L0, ND }, /* xor */
+ { { 0x83, 0x38 }, 3, T, R, pfx_no, Wn, L0, SCC }, /* ccmp<scc> */
+ { { 0x83, 0x38 }, 3, T, R, pfx_66, Wn, L0, SCC }, /* ccmp<scc> */
+ { { 0x84 }, 2, T, R, pfx_no, WIG, L0, SCC }, /* ctest<scc> */
+ { { 0x85 }, 2, T, R, pfx_no, Wn, L0, SCC }, /* ctest<scc> */
+ { { 0x85 }, 2, T, R, pfx_66, Wn, L0, SCC }, /* ctest<scc> */
{ { 0x8f, 0xc0 }, 2, F, R, pfx_no, Wn, L0, ND }, /* pop2 */
{ { 0xa5 }, 2, T, W, pfx_no, Wn, L0, ND }, /* shld */
{ { 0xa5 }, 2, T, W, pfx_66, Wn, L0, ND }, /* shld */
@@ -2308,12 +2325,16 @@ static const struct evex {
{ { 0xf0 }, 2, T, R, pfx_no, Wn, L0 }, /* crc32 */
{ { 0xf1 }, 2, T, R, pfx_no, Wn, L0 }, /* crc32 */
{ { 0xf1 }, 2, T, R, pfx_66, Wn, L0 }, /* crc32 */
+ { { 0xf6, 0x00 }, 3, T, R, pfx_no, WIG, L0, SCC }, /* ctest<scc> */
{ { 0xf6, 0x10 }, 2, T, W, pfx_no, WIG, L0, ND }, /* not */
{ { 0xf6, 0x18 }, 2, T, W, pfx_no, WIG, L0, ND }, /* neg */
{ { 0xf6, 0x20 }, 2, T, R, pfx_no, WIG, L0 }, /* mul */
{ { 0xf6, 0x28 }, 2, T, R, pfx_no, WIG, L0 }, /* imul */
{ { 0xf6, 0x30 }, 2, T, R, pfx_no, WIG, L0 }, /* div */
{ { 0xf6, 0x38 }, 2, T, R, pfx_no, WIG, L0 }, /* idiv */
+ { { 0xf7, 0x00 }, 6, T, R, pfx_no, Wn, L0, SCC }, /* ctest<scc> */
+ { { 0xf7, 0x00 }, 4, T, R, pfx_66, W0, L0, SCC }, /* ctest<scc> */
+ { { 0xf7, 0x00 }, 6, T, R, pfx_66, W1, L0, SCC }, /* ctest<scc> */
{ { 0xf7, 0x10 }, 2, T, W, pfx_no, Wn, L0, ND }, /* not */
{ { 0xf7, 0x10 }, 2, T, W, pfx_66, Wn, L0, ND }, /* not */
{ { 0xf7, 0x18 }, 2, T, W, pfx_no, Wn, L0, ND }, /* neg */
@@ -2333,6 +2354,7 @@ static const struct evex {
{ { 0xff, 0x08 }, 2, T, W, pfx_no, Wn, L0, ND }, /* dec */
{ { 0xff, 0x08 }, 2, T, W, pfx_66, Wn, L0, ND }, /* dec */
{ { 0xff, 0xf0 }, 2, F, W, pfx_no, Wn, L0, ND }, /* push2 */
+#undef SCC
#undef ND
#endif /* __x86_64__ */
}, evex_map5[] = {
@@ -2851,10 +2873,13 @@ void predicates_test(void *instr, struct
for ( x = 0; x < ARRAY_SIZE(evex); ++x )
{
+ unsigned int scc;
+
if ( evex[x].tbl == evex_map4 && !m )
continue;
- for ( t = 0; t < evex[x].num; ++t )
+ for ( t = 0, scc = 0; t < evex[x].num;
+ t += (!evex[x].tbl[t].scc || ++scc == 0x10) )
{
uint8_t *ptr = instr;
unsigned int l;
@@ -2864,7 +2889,8 @@ void predicates_test(void *instr, struct
*ptr++ = 0x62;
*ptr++ = 0xf1 + x;
*ptr++ = 0x7c | evex[x].tbl[t].pfx;
- *ptr++ = 0x08 | evex[x].tbl[t].mask;
+ *ptr++ = evex[x].tbl[t].scc ? (scc &= 0xf)
+ : 0x08 | evex[x].tbl[t].mask;
for ( l = 3; l--; )
{
--- a/tools/tests/x86_emulator/test_x86_emulator.c
+++ b/tools/tests/x86_emulator/test_x86_emulator.c
@@ -2296,6 +2296,56 @@ int main(int argc, char **argv)
cpu_policy.feat.movrs = movrs;
}
+ printf("%-40s", "Testing ccmpfl {dfv=cf} $1,(%r21)...");
+ if ( cpu_has_apx_nci_ndd_nf )
+ {
+ instr[0] = 0x62; instr[1] = 0xfc; instr[2] = 0x0c; instr[3] = 0x0b;
+ instr[4] = 0x83; instr[5] = 0x7d; instr[6] = 0x00; instr[7] = 0x01;
+ regs.rip = (unsigned long)&instr[0];
+ regs.eflags = EFLAGS_ALWAYS_SET | X86_EFLAGS(AF, SF);
+ load_egpr(21, (unsigned long)res);
+ res[0] = 1;
+ bytes_read = 0;
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rip != (unsigned long)&instr[8]) ||
+ ((regs.eflags & EFLAGS_MASK) !=
+ (EFLAGS_ALWAYS_SET | X86_EFLAGS(CF, PF))) ||
+ bytes_read != 4 )
+ goto fail;
+ printf("okay\n");
+ }
+ else
+ printf("skipped\n");
+
+ printf("%-40s", "Testing ctestzl {dfv=} %r21d,%esi...");
+ if ( cpu_has_apx_nci_ndd_nf )
+ {
+ instr[0] = 0x62; instr[1] = 0xe4; instr[2] = 0x04; instr[3] = 0x04;
+ instr[4] = 0x85; instr[5] = 0xee;
+ regs.rip = (unsigned long)&instr[0];
+ regs.eflags = EFLAGS_ALWAYS_SET | X86_EFLAGS(ZF, PF);
+ regs.esi = 0x87654321U;
+ load_egpr(21, 0x88776655U);
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rip != (unsigned long)&instr[6]) ||
+ ((regs.eflags & (EFLAGS_MASK & ~X86_EFLAGS_AF)) !=
+ (EFLAGS_ALWAYS_SET | X86_EFLAGS_SF)) ||
+ (regs.esi != 0x87654321) )
+ goto fail;
+ /* And again, now with ZF clear. */
+ regs.rip = (unsigned long)&instr[0];
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rip != (unsigned long)&instr[6]) ||
+ ((regs.eflags & EFLAGS_MASK) != EFLAGS_ALWAYS_SET) )
+ goto fail;
+ printf("okay\n");
+ }
+ else
+ printf("skipped\n");
+
printf("%-40s", "Testing jmpabs $0x123456789abc...");
if ( cpu_has_apx_f )
{
--- a/xen/arch/x86/x86_emulate/decode.c
+++ b/xen/arch/x86/x86_emulate/decode.c
@@ -671,6 +671,20 @@ static const struct map4_table {
[vex_np] = { .nd = true, .nf = true, .nci_ndd_nf = true },
[vex_66] = { .nd = true, .nf = true, .nci_ndd_nf = true },
},
+ [0x38] = { /* CCMP<scc> (byte) */
+ [vex_np] = { .byte = true },
+ },
+ [0x39] = { /* CCMP<scc> ({,d,q}word) */
+ [vex_np] = { },
+ [vex_66] = { },
+ },
+ [0x3a] = { /* CCMP<scc> (byte) */
+ [vex_np] = { .byte = true },
+ },
+ [0x3b] = { /* CCMP<scc> ({,d,q}word) */
+ [vex_np] = { },
+ [vex_66] = { },
+ },
[0x40 ... 0x4f] = { /* {,CF}CMOVcc, SETcc */
[vex_np] = { .nd = true, .nf = true, .nci_ndd_nf = true },
[vex_66] = { .nd = true, .nf = true, .nci_ndd_nf = true },
@@ -705,6 +719,13 @@ static const struct map4_table {
[vex_66] = { .to_mem = true, .imm8 = true, .nd = true,
.nci_ndd_nf = true },
},
+ [0x84] = { /* CTEST<scc> (byte) */
+ [vex_np] = { .byte = true },
+ },
+ [0x85] = { /* CTEST<scc> ({,d,q}word) */
+ [vex_np] = { },
+ [vex_66] = { },
+ },
[0x8f] = { /* Grp1a */
[vex_np] = { .nd = true },
},
@@ -1297,6 +1318,11 @@ decode_map4(struct x86_emulate_state *s,
switch ( ctxt->opcode & 0xff )
{
+ case 0x38 ... 0x3b: /* ccmp<scc> */
+ case 0x84 ... 0x85: /* ctest<scc> */
+ scc = true;
+ break;
+
case 0x40 ... 0x4f:
if ( s->evex.pfx <= vex_66 ) /* {,cf}cmov<cc> */
{
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -756,6 +756,40 @@ test_cc(
return (!!rc ^ (condition & 1));
}
+/*
+ * Besides testing EVEX.SCC against the incoming EFLAGS this function also
+ * takes care of setting EFLAGS to what EVEX.{OF,SF,ZF,CF} holds in case the
+ * condition evaluates to false.
+ */
+static bool
+test_scc(
+ const struct x86_emulate_state *state,
+ unsigned int *eflags)
+{
+ unsigned int scc = (state->evex.RX << 3) | state->evex.opmsk;
+
+ /* SCC == 0b1010 is special (true). */
+ if ( scc == 0xa )
+ return true;
+
+ /* SCC == 0b1011 is special (false). */
+ if ( scc != 0xb && test_cc(scc, *eflags) )
+ return true;
+
+ /* Set EFLAGS from the respective default bits in EVEX. */
+ *eflags &= ~EFLAGS_MASK;
+ if ( state->evex.reg & 8 )
+ *eflags |= X86_EFLAGS_OF;
+ if ( state->evex.reg & 4 )
+ *eflags |= X86_EFLAGS_SF;
+ if ( state->evex.reg & 2 )
+ *eflags |= X86_EFLAGS_ZF;
+ if ( state->evex.reg & 1 )
+ *eflags |= X86_EFLAGS(PF, CF);
+
+ return false;
+}
+
int x86emul_get_cpl(struct x86_emulate_ctxt *ctxt,
const struct x86_emulate_ops *ops)
{
@@ -1743,11 +1777,30 @@ x86_emulate(
}
break;
+ case X86EMUL_OPC_EVEX (4, 0x38): /* ccmp<scc> r8,r/m */
+ case X86EMUL_OPC_EVEX (4, 0x39): /* ccmp<scc> reg,r/m */
+ case X86EMUL_OPC_EVEX_66(4, 0x39): /* ccmp<scc> r16,r/m */
+ ccmp:
+ if ( !test_scc(state, &_regs.eflags) )
+ {
+ dst.type = OP_NONE;
+ break;
+ }
+ fallthrough;
case 0x38: case 0x39: cmp: /* cmp reg,mem */
emulate_2op_SrcV("cmp", dst, src, _regs.eflags);
dst.type = OP_NONE;
break;
+ case X86EMUL_OPC_EVEX (4, 0x3a): /* ccmp<scc> r/m,r8 */
+ case X86EMUL_OPC_EVEX (4, 0x3b): /* ccmp<scc> r/m,reg */
+ case X86EMUL_OPC_EVEX_66(4, 0x3b): /* ccmp<scc> r/m,r16 */
+ if ( !test_scc(state, &_regs.eflags) )
+ {
+ dst.type = OP_NONE;
+ break;
+ }
+ fallthrough;
case 0x3a ... 0x3d: /* cmp */
emulate_2op_SrcV("cmp", src, dst, _regs.eflags);
dst.type = OP_NONE;
@@ -2078,11 +2131,21 @@ x86_emulate(
case 7:
dst.val = imm1;
if ( evex_encoded() )
- goto unimplemented_insn;
+ goto ccmp;
goto cmp;
}
break;
+ case X86EMUL_OPC_EVEX (4, 0x84): /* ctest<scc> r/m,r8 */
+ case X86EMUL_OPC_EVEX (4, 0x85): /* ctest<scc> r/m,reg */
+ case X86EMUL_OPC_EVEX_66(4, 0x85): /* ctest<scc> r/m,r16 */
+ ctest:
+ if ( !test_scc(state, &_regs.eflags) )
+ {
+ dst.type = OP_NONE;
+ break;
+ }
+ fallthrough;
case 0xa8 ... 0xa9: /* test imm,%%eax */
case 0x84 ... 0x85: test: /* test */
emulate_2op_SrcV("test", src, dst, _regs.eflags);
@@ -2772,7 +2835,7 @@ x86_emulate(
dst.val = imm1;
dst.bytes = src.bytes;
if ( evex_encoded() )
- goto unimplemented_insn;
+ goto ctest;
goto test;
case 2: /* not */
if ( ops->rmw && dst.type == OP_MEM &&
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |