|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH 21/30] x86emul: support APX+BMI1 insns
Promoted-from-VEX insns are again handled by transforming them to their
VEX counterparts, while the sole promoted-from-legacy insn has respective
logic re-used just like was done for other ones in the same category.
Common EVEX.NF handling takes care of the EFLAGS aspect, except for the
APX-NCI-NDD-NF checking when EVEX.nf is set: For insns in map 2 (0f38) and
subsequently map3 (0f3a), replicating the same logic in decode_0f{38,3a}()
feels undesirable.
Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>
---
RFC: In evex-disp8.c's ESZ_dq handling we special-case Map4 to append an
'l' suffix rather than a 'd' one. If we wanted this to be
cosmetically correct also for the promoted VEX encodings, further
special casing would need to happen there. I'm simply uncertain if
that's worth it.
--- a/tools/tests/x86_emulator/evex-disp8.c
+++ b/tools/tests/x86_emulator/evex-disp8.c
@@ -1126,6 +1126,16 @@ static const struct test avx512f[] = {
// kmovw, , 0f, 93, w
};
+static const struct test bmi1[] = {
+ INSN(andn, , 0f38, f2, el, dq, no),
+ INSN(bextr, , 0f38, f7, el, dq, no),
+ INSNX(blsi, , 0f38, f3, 3, el, dq, no),
+ INSNX(blsmsk, , 0f38, f3, 2, el, dq, no),
+ INSNX(blsr, , 0f38, f3, 1, el, dq, no),
+ INSN(tzcnt, , map4, f4, el, dq, no),
+ INSN(tzcntw, 66, map4, f4, el, w, no),
+};
+
static const unsigned char vl_all[] = { VL_512, VL_128, VL_256 };
static const unsigned char vl_128[] = { VL_128 };
static const unsigned char vl_no128[] = { VL_512, VL_256 };
@@ -1714,6 +1724,7 @@ void evex_disp8_test(void *instr, struct
RUN(avx512f);
RUN(avx512dq);
RUN(avx512bw);
+ RUN(bmi1);
#undef RUN
--- a/tools/tests/x86_emulator/predicates.c
+++ b/tools/tests/x86_emulator/predicates.c
@@ -2066,6 +2066,13 @@ static const struct evex {
{ { 0xdd }, 2, T, R, pfx_66, WIG, Ln }, /* vaesenclast */
{ { 0xde }, 2, T, R, pfx_66, WIG, Ln }, /* vaesdec */
{ { 0xdf }, 2, T, R, pfx_66, WIG, Ln }, /* vaesdeclast */
+#ifdef __x86_64__
+ { { 0xf2 }, 2, T, R, pfx_no, Wn, L0 }, /* andn */
+ { { 0xf3, 0x08 }, 2, T, R, pfx_no, Wn, L0 }, /* blsr */
+ { { 0xf3, 0x10 }, 2, T, R, pfx_no, Wn, L0 }, /* blsmsk */
+ { { 0xf3, 0x18 }, 2, T, R, pfx_no, Wn, L0 }, /* blsi */
+ { { 0xf7 }, 2, T, R, pfx_no, Wn, L0 }, /* bextr */
+#endif
}, evex_0f3a[] = {
{ { 0x00 }, 3, T, R, pfx_66, W1, L1|L2 }, /* vpermq */
{ { 0x01 }, 3, T, R, pfx_66, W1, L1|L2 }, /* vpermpd */
@@ -2339,6 +2346,8 @@ 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 */
+ { { 0xf4 }, 2, T, R, pfx_no, Wn, L0 }, /* tzcnt */
+ { { 0xf4 }, 2, T, R, pfx_66, Wn, L0 }, /* tzcnt */
{ { 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 */
--- a/tools/tests/x86_emulator/test_x86_emulator.c
+++ b/tools/tests/x86_emulator/test_x86_emulator.c
@@ -2687,6 +2687,30 @@ int main(int argc, char **argv)
(regs.eflags & 0xfeb) != 0x202 || !check_eip(andn) )
goto fail;
printf("okay\n");
+
+#if defined(__x86_64__) && \
+ (defined(__clang__) ? __clang_major__ >= 18 : __GNUC__ >= 14)
+ if ( cpu_has_apx_f )
+ {
+ decl_insn(andn_apx);
+
+ printf("%-40s", "Testing andn (%rdx),%r17d,%r19d...");
+
+ asm volatile ( put_insn(andn_apx, "andn (%0), %%r17d, %%r19d")
+ :: "d" (NULL) );
+ set_insn(andn_apx);
+
+ load_egpr(17, 0xcccc3333);
+ regs.eflags = EFLAGS_ALWAYS_SET | X86_EFLAGS(OF, SF, ZF, CF);
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) || !check_egpr(19, 0x32108888) ||
+ !check_egpr(17, 0xcccc3333) || *res != 0xfedcba98 ||
+ (regs.eflags & EFLAGS_MASK) != EFLAGS_ALWAYS_SET ||
+ !check_eip(andn_apx) )
+ goto fail;
+ printf("okay\n");
+ }
+#endif
}
else
printf("skipped\n");
@@ -2731,6 +2755,34 @@ int main(int argc, char **argv)
(regs.eflags & 0xf6b) != 0x202 || !check_eip(bextr64) )
goto fail;
printf("okay\n");
+
+# if defined(__clang__) ? __clang_major__ >= 18 : __GNUC__ >= 14
+ if ( cpu_has_apx_f )
+ {
+ decl_insn(bextr_apx);
+
+ printf("%-40s", "Testing bextr %r25,(%r26),%r27...");
+
+ asm volatile ( put_insn(bextr_apx, "bextr %r25, (%r26), %r27") );
+ set_insn(bextr_apx);
+
+ load_egpr(26, (unsigned long)res);
+ regs.r10 = 0;
+ load_egpr(25, 0x211e);
+ regs.r9 = 0;
+ regs.eflags = EFLAGS_ALWAYS_SET | X86_EFLAGS(OF, ZF, CF);
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) || !check_egpr(25, 0x211e) ||
+ !check_egpr(27,
+ (((unsigned long)(res[1] << 1) << 1) |
+ (res[0] >> 30))) ||
+ ((regs.eflags & EFLAGS_MASK & ~X86_EFLAGS(AF, PF)) !=
+ EFLAGS_ALWAYS_SET) ||
+ !check_eip(bextr_apx) )
+ goto fail;
+ printf("okay\n");
+ }
+# endif
#endif
}
else
@@ -2755,6 +2807,29 @@ int main(int argc, char **argv)
!check_eip(blsi) )
goto fail;
printf("okay\n");
+
+#if defined(__x86_64__) && \
+ (defined(__clang__) ? __clang_major__ >= 18 : __GNUC__ >= 14)
+ if ( cpu_has_apx_f )
+ {
+ decl_insn(blsi_apx);
+
+ printf("%-40s", "Testing blsi (%rdx),%r25d...");
+
+ asm volatile ( put_insn(blsi_apx, "blsi (%0), %%r25d")
+ :: "d" (NULL) );
+ set_insn(blsi_apx);
+
+ regs.eflags = EFLAGS_ALWAYS_SET | X86_EFLAGS(OF, SF, ZF);
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) || !check_egpr(25, 8) ||
+ (regs.eflags & (EFLAGS_MASK & ~X86_EFLAGS(AF, PF))) !=
+ (EFLAGS_ALWAYS_SET | X86_EFLAGS_CF) ||
+ !check_eip(blsi_apx) )
+ goto fail;
+ printf("okay\n");
+ }
+#endif
}
else
printf("skipped\n");
@@ -2776,6 +2851,29 @@ int main(int argc, char **argv)
!check_eip(blsmsk) )
goto fail;
printf("okay\n");
+
+#if defined(__x86_64__) && \
+ (defined(__clang__) ? __clang_major__ >= 18 : __GNUC__ >= 14)
+ if ( cpu_has_apx_f )
+ {
+ decl_insn(blsmsk_apx);
+
+ printf("%-40s", "Testing blsmsk (%rdx),%r17d...");
+
+ asm volatile ( put_insn(blsmsk_apx, "blsmsk (%0), %%r17d")
+ :: "d" (NULL) );
+ set_insn(blsmsk_apx);
+
+ regs.eflags = EFLAGS_ALWAYS_SET | X86_EFLAGS(OF, SF, ZF, CF);
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) || !check_egpr(17, 0xf) ||
+ (regs.eflags & (EFLAGS_MASK & ~X86_EFLAGS(AF, PF))) !=
+ EFLAGS_ALWAYS_SET ||
+ !check_eip(blsmsk_apx) )
+ goto fail;
+ printf("okay\n");
+ }
+#endif
}
else
printf("skipped\n");
@@ -2820,6 +2918,34 @@ int main(int argc, char **argv)
#endif
printf("okay\n");
+
+#if defined(__x86_64__) && \
+ (defined(__clang__) ? __clang_major__ >= 18 : __GNUC__ >= 14)
+ if ( true /* cpu_has_apx_nci_ndd_nf */ )
+ {
+ decl_insn(blsr_apx);
+
+ cpu_policy.apx.apx_nci_ndd_nf = true;
+
+ printf("%-40s", "Testing {nf}blsr (%rdx),%ecx...");
+
+ asm volatile ( put_insn(blsr_apx, "%{nf%} blsr (%0), %%ecx")
+ :: "d" (NULL) );
+ set_insn(blsr_apx);
+
+ regs.rcx = 0;
+ regs.eflags = EFLAGS_ALWAYS_SET | X86_EFLAGS(OF, SF, ZF, CF);
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) || regs.rcx != 0xfedcba90 ||
+ (regs.eflags !=
+ (EFLAGS_ALWAYS_SET | X86_EFLAGS(OF, SF, ZF, CF))) ||
+ !check_eip(blsr_apx) )
+ goto fail;
+ printf("okay\n");
+
+ cpu_policy.apx.apx_nci_ndd_nf = nci_ndd_nf;
+ }
+#endif
}
else
printf("skipped\n");
--- a/xen/arch/x86/x86_emulate/decode.c
+++ b/xen/arch/x86/x86_emulate/decode.c
@@ -788,6 +788,10 @@ static const struct map4_table {
[vex_np] = { },
[vex_66] = { },
},
+ [0xf4] = { /* TZCNT ({,d,q}word) */
+ [vex_np] = { .nf = true, .nci_ndd_nf = true },
+ [vex_66] = { .nf = true, .nci_ndd_nf = true },
+ },
[0xf6] = { /* Grp3 (byte); most attributes depend on modrm_reg */
[vex_np] = { .byte = true, .nci_ndd_nf = true },
},
@@ -1263,6 +1267,12 @@ decode_0f38(struct x86_emulate_state *s,
case X86EMUL_OPC_VEX_F2(0, 0xf7): /* shrx */
break;
+ case X86EMUL_OPC_EVEX(0, 0xf2): /* andn */
+ case X86EMUL_OPC_EVEX(0, 0xf3): /* Grp 17 */
+ case X86EMUL_OPC_EVEX(0, 0xf7): /* bextr */
+ s->promoted_evex = true;
+ break;
+
case 0xf8:
if ( s->modrm_mod == 3 ) /* u{rd,wr}msr */
{
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -5694,8 +5694,12 @@ x86_emulate(
emulate_2op_SrcV_nobyte("btc", src, dst, _regs.eflags);
break;
+ case X86EMUL_OPC_EVEX (4, 0xf4): /* tzcnt r/m,reg */
+ case X86EMUL_OPC_EVEX_66(4, 0xf4): /* tzcnt r/m,r16 */
+ vcpu_must_have(bmi1);
+ fallthrough;
case X86EMUL_OPC(0x0f, 0xbc): /* bsf or tzcnt */
- if ( vex.pfx == vex_f3 )
+ if ( vex.pfx == vex_f3 || state->promoted_evex )
emulate_2op_SrcV_srcmem("tzcnt", src, dst, _regs.eflags);
else
{
@@ -7386,6 +7390,12 @@ x86_emulate(
}
break;
+ case X86EMUL_OPC_EVEX(0x0f38, 0xf2): /* andn r/m,r,r */
+ case X86EMUL_OPC_EVEX(0x0f38, 0xf7): /* bextr r,r/m,r */
+ generate_exception_if((evex.z || evex.lr || evex.brs ||
+ (evex.opmsk & 3)),
+ X86_EXC_UD);
+ fallthrough;
case X86EMUL_OPC_VEX(0x0f38, 0xf2): /* andn r/m,r,r */
case X86EMUL_OPC_VEX(0x0f38, 0xf5): /* bzhi r,r/m,r */
case X86EMUL_OPC_VEX_F3(0x0f38, 0xf5): /* pext r/m,r,r */
@@ -7411,17 +7421,30 @@ x86_emulate(
if ( !mode_64bit() )
pvex->w = 0;
pvex->reg = 0xf; /* rAX */
+ if ( state->promoted_evex )
+ {
+ /* See copy_VEX(). */
+ pvex->opcx &= 7;
+ src.reg = decode_evex_gpr(evex.reg | (evex.RX << 4), &_regs,
+ &state->egpr, ctxt);
+ }
+ else
+ src.reg = decode_vex_gpr(vex.reg, &_regs, ctxt);
buf[3] = b;
buf[4] = 0x09; /* reg=rCX r/m=(%rCX) */
place_ret(&buf[5]);
- src.reg = decode_vex_gpr(vex.reg, &_regs, ctxt);
emulate_stub([dst] "=&c" (dst.val), "[dst]" (&src.val), "a"
(*src.reg));
put_stub(stub);
break;
}
+ case X86EMUL_OPC_EVEX(0x0f38, 0xf3): /* Grp 17 */
+ generate_exception_if((evex.z || evex.lr || evex.brs ||
+ (evex.opmsk & 3)),
+ X86_EXC_UD);
+ fallthrough;
case X86EMUL_OPC_VEX(0x0f38, 0xf3): /* Grp 17 */
{
uint8_t *buf = get_stub(stub);
@@ -7447,11 +7470,19 @@ x86_emulate(
if ( !mode_64bit() )
pvex->w = 0;
pvex->reg = 0xf; /* rAX */
+ if ( state->promoted_evex )
+ {
+ /* See copy_VEX(). */
+ pvex->opcx &= 7;
+ dst.reg = decode_evex_gpr(evex.reg | (evex.RX << 4), &_regs,
+ &state->egpr, ctxt);
+ }
+ else
+ dst.reg = decode_vex_gpr(vex.reg, &_regs, ctxt);
buf[3] = b;
buf[4] = (modrm & 0x38) | 0x01; /* r/m=(%rCX) */
place_ret(&buf[5]);
- dst.reg = decode_vex_gpr(vex.reg, &_regs, ctxt);
emulate_stub("=&a" (dst.val), "c" (&src.val));
put_stub(stub);
@@ -8976,6 +9007,14 @@ x86_emulate(
if ( state->promoted_evex )
{
+ /*
+ * This, while seemingly inapplicable to SCC insns, is simply
+ * benign there: They require the feature irrespective of the
+ * setting of what elsewhere is EVEX.nf.
+ */
+ if ( EVEX_NF(&evex) )
+ vcpu_must_have(apx_nci_ndd_nf);
+
place_ret(&opc[insn_bytes - PFX_BYTES]);
copy_VEX(opc, vex, true);
}
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |