[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 21/30] x86emul: support APX+BMI1 insns


  • To: "xen-devel@xxxxxxxxxxxxxxxxxxxx" <xen-devel@xxxxxxxxxxxxxxxxxxxx>
  • From: Jan Beulich <jbeulich@xxxxxxxx>
  • Date: Wed, 11 Nov 2026 15:29:03 +0100
  • Authentication-results: eu.smtp.expurgate.cloud; dkim=pass header.s=google header.d=suse.com header.i="@suse.com" header.h="Content-Transfer-Encoding:Content-Type:In-Reply-To:Autocrypt:Content-Language:References:Cc:To:From:Subject:User-Agent:MIME-Version:Date:Message-ID"
  • Autocrypt: addr=jbeulich@xxxxxxxx; keydata= xsDiBFk3nEQRBADAEaSw6zC/EJkiwGPXbWtPxl2xCdSoeepS07jW8UgcHNurfHvUzogEq5xk hu507c3BarVjyWCJOylMNR98Yd8VqD9UfmX0Hb8/BrA+Hl6/DB/eqGptrf4BSRwcZQM32aZK 7Pj2XbGWIUrZrd70x1eAP9QE3P79Y2oLrsCgbZJfEwCgvz9JjGmQqQkRiTVzlZVCJYcyGGsD /0tbFCzD2h20ahe8rC1gbb3K3qk+LpBtvjBu1RY9drYk0NymiGbJWZgab6t1jM7sk2vuf0Py O9Hf9XBmK0uE9IgMaiCpc32XV9oASz6UJebwkX+zF2jG5I1BfnO9g7KlotcA/v5ClMjgo6Gl MDY4HxoSRu3i1cqqSDtVlt+AOVBJBACrZcnHAUSuCXBPy0jOlBhxPqRWv6ND4c9PH1xjQ3NP nxJuMBS8rnNg22uyfAgmBKNLpLgAGVRMZGaGoJObGf72s6TeIqKJo/LtggAS9qAUiuKVnygo 3wjfkS9A3DRO+SpU7JqWdsveeIQyeyEJ/8PTowmSQLakF+3fote9ybzd880fSmFuIEJldWxp Y2ggPGpiZXVsaWNoQHN1c2UuY29tPsJgBBMRAgAgBQJZN5xEAhsDBgsJCAcDAgQVAggDBBYC AwECHgECF4AACgkQoDSui/t3IH4J+wCfQ5jHdEjCRHj23O/5ttg9r9OIruwAn3103WUITZee e7Sbg12UgcQ5lv7SzsFNBFk3nEQQCACCuTjCjFOUdi5Nm244F+78kLghRcin/awv+IrTcIWF hUpSs1Y91iQQ7KItirz5uwCPlwejSJDQJLIS+QtJHaXDXeV6NI0Uef1hP20+y8qydDiVkv6l IreXjTb7DvksRgJNvCkWtYnlS3mYvQ9NzS9PhyALWbXnH6sIJd2O9lKS1Mrfq+y0IXCP10eS FFGg+Av3IQeFatkJAyju0PPthyTqxSI4lZYuJVPknzgaeuJv/2NccrPvmeDg6Coe7ZIeQ8Yj t0ARxu2xytAkkLCel1Lz1WLmwLstV30g80nkgZf/wr+/BXJW/oIvRlonUkxv+IbBM3dX2OV8 AmRv1ySWPTP7AAMFB/9PQK/VtlNUJvg8GXj9ootzrteGfVZVVT4XBJkfwBcpC/XcPzldjv+3 HYudvpdNK3lLujXeA5fLOH+Z/G9WBc5pFVSMocI71I8bT8lIAzreg0WvkWg5V2WZsUMlnDL9 mpwIGFhlbM3gfDMs7MPMu8YQRFVdUvtSpaAs8OFfGQ0ia3LGZcjA6Ik2+xcqscEJzNH+qh8V m5jjp28yZgaqTaRbg3M/+MTbMpicpZuqF4rnB0AQD12/3BNWDR6bmh+EkYSMcEIpQmBM51qM EKYTQGybRCjpnKHGOxG0rfFY1085mBDZCH5Kx0cl0HVJuQKC+dV2ZY5AqjcKwAxpE75MLFkr wkkEGBECAAkFAlk3nEQCGwwACgkQoDSui/t3IH7nnwCfcJWUDUFKdCsBH/E5d+0ZnMQi+G0A nAuWpQkjM1ASeQwSHEeAWPgskBQL
  • Cc: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>, Teddy Astie <teddy.astie@xxxxxxxxxx>, Roger Pau Monné <roger@xxxxxxxxxxxxxx>
  • Delivery-date: Wed, 11 Nov 2026 14:29:08 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>

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);
         }




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.