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

[PATCH 26/30] x86emul: support APX+MOVDIR/ENQCMD insns


  • To: "xen-devel@xxxxxxxxxxxxxxxxxxxx" <xen-devel@xxxxxxxxxxxxxxxxxxxx>
  • From: Jan Beulich <jbeulich@xxxxxxxx>
  • Date: Wed, 11 Nov 2026 15:31:04 +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:31:10 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>

Besides adding table entries and case labels, generic map 4 handling
takes care of things here. The only special aspect is dealing with the
512-bit memory operands in EVEX Disp8 testing, including the need for a
->blk() hook and for MSR_PASID to be readable.

Treating MOVDIR64B and ENQCMD{,S} as WIG is speculative; the spec is
unclear in this regard, so GNU binutils are used as reference.

Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>

--- a/tools/tests/x86_emulator/evex-disp8.c
+++ b/tools/tests/x86_emulator/evex-disp8.c
@@ -1154,6 +1154,12 @@ static const struct test bmi2[] = {
     INSN(shrx,      f2, 0f38, f7,    el,   dq, no),
 };
 
+static const struct test enqcmd[] = {
+    /* Element size is set arbitrarily here. See test_one(). */
+    INSN(enqcmd,    f2, map4, f8,    el,    b, no),
+    INSN(enqcmds,   f3, map4, f8,    el,    b, no),
+};
+
 static const struct test lzcnt[] = {
     INSN(lzcnt,       , map4, f5,    el,   dq, no),
     INSN(lzcntw,    66, map4, f5,    el,    w, no),
@@ -1166,6 +1172,15 @@ static const struct test movbe[] = {
     INSN(movbew,    66, map4, 61,    el,    w, no),
 };
 
+static const struct test movdir64b[] = {
+    /* Element size is set arbitrarily here. See test_one(). */
+    INSN(movdir64b, 66, map4, f8,    el,    b, no),
+};
+
+static const struct test movdiri[] = {
+    INSN(movdiri,     , map4, f9,    el,   dq, no),
+};
+
 static const struct test popcnt[] = {
     INSN(popcnt,      , map4, 88,    el,   dq, no),
     INSN(popcntw,   66, map4, 88,    el,    w, no),
@@ -1270,6 +1285,18 @@ static int rmw(enum x86_segment seg, uns
     return X86EMUL_OKAY;
 }
 
+static int blk(enum x86_segment seg, unsigned long offset, void *p_data,
+               unsigned int bytes, uint32_t *eflags,
+               struct x86_emulate_state *state,
+               struct x86_emulate_ctxt *ctxt)
+{
+    /* For MOVDIRI ModR/M.rm specifies the sole memory operand. */
+    if ( ctxt->opcode == X86EMUL_OPC_EVEX(4, 0xf9) &&
+         !record_access(seg, offset, bytes) )
+        return X86EMUL_UNHANDLEABLE;
+    return X86EMUL_OKAY;
+}
+
 static void test_one(const struct test *test, enum vl vl,
                      unsigned char *instr, struct x86_emulate_ctxt *ctxt)
 {
@@ -1461,6 +1488,12 @@ static void test_one(const struct test *
                 imm = test->opc & 1 ? test->pfx != PFX_66 ? 4 : 2 : 1;
             }
             break;
+
+        case 0xf8:
+            /* (Fake) address for the special-type store. */
+            ctxt->regs->eax = 0;
+            vsz = 64;
+            break;
         }
     }
 
@@ -1726,6 +1759,7 @@ void evex_disp8_test(void *instr, struct
         bool nci_ndd_nf = cpu_policy.apx.apx_nci_ndd_nf;
 
         emulops.rmw = rmw;
+        emulops.blk = blk;
 
         ctxt->force_writeback = true;
 
@@ -1762,8 +1796,11 @@ void evex_disp8_test(void *instr, struct
         RUN(bmi1);
         RUN(bmi2);
         RUN(cmpccxadd);
+        RUN(enqcmd);
         RUN(lzcnt);
         RUN(movbe);
+        RUN(movdir64b);
+        RUN(movdiri);
         RUN(popcnt);
 
 #undef RUN
--- a/tools/tests/x86_emulator/predicates.c
+++ b/tools/tests/x86_emulator/predicates.c
@@ -2374,6 +2374,10 @@ static const struct evex {
     { { 0xf7, 0x30 }, 2, T, R, pfx_66, Wn, L0 }, /* div */
     { { 0xf7, 0x38 }, 2, T, R, pfx_no, Wn, L0 }, /* idiv */
     { { 0xf7, 0x38 }, 2, T, R, pfx_66, Wn, L0 }, /* idiv */
+    { { 0xf8 }, 2, F, W, pfx_66, WIG, L0 }, /* movdir64b */
+    { { 0xf8 }, 2, F, W, pfx_f3, WIG, L0 }, /* enqcmds */
+    { { 0xf8 }, 2, F, W, pfx_f2, WIG, L0 }, /* enqcmd */
+    { { 0xf9 }, 2, F, W, pfx_no, Wn, L0 }, /* movdiri */
     { { 0xfe, 0x00 }, 2, T, W, pfx_no, WIG, L0, ND }, /* inc */
     { { 0xfe, 0x08 }, 2, T, W, pfx_no, WIG, L0, ND }, /* dec */
     { { 0xff, 0x00 }, 2, T, W, pfx_no, Wn, L0, ND }, /* inc */
--- a/tools/tests/x86_emulator/test_x86_emulator.c
+++ b/tools/tests/x86_emulator/test_x86_emulator.c
@@ -872,6 +872,10 @@ static int read_msr(
         *val = ((uint64_t)APERF_HI_VALUE << 32) | APERF_LO_VALUE;
         return X86EMUL_OKAY;
 
+    case MSR_PASID:
+        *val = PASID_VALID;
+        return X86EMUL_OKAY;
+
     case MSR_EFER:
         *val = ctxt->addr_size > 32 ? EFER_LME | EFER_LMA : 0;
         return X86EMUL_OKAY;
@@ -3773,6 +3777,27 @@ int main(int argc, char **argv)
              res[0] != 0x44332211 || ~res[1] )
             goto fail;
         printf("okay\n");
+
+#ifdef __x86_64__
+        if ( cpu_has_apx_f )
+        {
+            printf("%-40s", "Testing movdiri %r16,4(%rcx)...");
+
+            instr[0] = 0x62; instr[1] = 0xe4; instr[2] = 0xfc; instr[3] = 0x08;
+            instr[4] = 0xf9; instr[5] = 0x41; instr[6] = 0x04;
+
+            regs.rip = (unsigned long)&instr[0];
+            load_egpr(16, 0x88776655U);
+            memset(res, -1, 16);
+
+            rc = x86_emulate(&ctxt, &emulops);
+            if ( (rc != X86EMUL_OKAY) ||
+                 (regs.rip != (unsigned long)&instr[7]) ||
+                 ~res[0] || (res[1] != 0x88776655U) || res[2] || ~res[3] )
+                goto fail;
+            printf("okay\n");
+        }
+#endif
     }
     else
         printf("skipped\n");
@@ -3798,6 +3823,32 @@ int main(int argc, char **argv)
             if ( res[i] != i )
                 goto fail;
         printf("okay\n");
+
+#ifdef __x86_64__
+        if ( cpu_has_apx_f )
+        {
+            printf("%-40s", "Testing movdir64b -128(%r20),%rdx...");
+
+            instr[0] = 0x62; instr[1] = 0xfc; instr[2] = 0x7d; instr[3] = 0x08;
+            instr[4] = 0xf8; instr[5] = 0x54; instr[6] = 0x24; instr[7] = 0x80;
+
+            regs.rip = (unsigned long)&instr[0];
+            for ( i = 0; i < 64; ++i )
+                res[i] = i + 32;
+            regs.rdx = (unsigned long)(res + 32);
+            load_egpr(20, regs.rdx);
+
+            rc = x86_emulate(&ctxt, &emulops);
+            if ( (rc != X86EMUL_OKAY) ||
+                 (regs.rip != (unsigned long)&instr[8]) ||
+                 (res[31] != 63)  )
+                goto fail;
+            for ( i = 32; i < 48; ++i )
+                if ( res[i] != i )
+                    goto fail;
+            printf("okay\n");
+        }
+#endif
     }
     else
         printf("skipped\n");
--- a/tools/tests/x86_emulator/x86-emulate.h
+++ b/tools/tests/x86_emulator/x86-emulate.h
@@ -204,6 +204,7 @@ void wrpkru(unsigned int val);
                                      xcr0_mask(0xe6))
 #define cpu_has_movdiri              cpu_policy.feat.movdiri
 #define cpu_has_movdir64b            cpu_policy.feat.movdir64b
+#define cpu_has_enqcmd               cpu_policy.feat.enqcmd
 #define cpu_has_avx512_vp2intersect (cpu_policy.feat.avx512_vp2intersect && \
                                      xcr0_mask(0xe6))
 #define cpu_has_serialize            cpu_policy.feat.serialize
--- a/xen/arch/x86/x86_emulate/decode.c
+++ b/xen/arch/x86/x86_emulate/decode.c
@@ -815,6 +815,14 @@ static const struct map4_table {
         [vex_np] = { .nci_ndd_nf = true },
         [vex_66] = { .nci_ndd_nf = true },
     },
+    [0xf8] = {
+        [vex_66] = { .to_mem = true }, /* MOVDIR64B */
+        [vex_f3] = { .to_mem = true }, /* ENQCMDS */
+        [vex_f2] = { .to_mem = true }, /* ENQCMD */
+    },
+    [0xf9] = { /* MOVDIRI */
+        [vex_np] = { .to_mem = true },
+    },
     [0xfe] = { /* Grp4 */
         [vex_np] = { .to_mem = true, .byte = true, .nd = true, .nf = true,
                      .nci_ndd_nf = true },
@@ -1449,6 +1457,16 @@ decode_map4(struct x86_emulate_state *s,
         }
         break;
 
+    case 0xf8: /* movdir64b / enqcmd{,s} */
+        /* Make these match their legacy forms. */
+        s->desc = DstReg | Mov;
+        s->op_bytes = 0;
+        break;
+
+    case 0xf9: /* movdiri */
+        s->desc |= Mov;
+        break;
+
     case 0xff: /* Grp5 */
         switch ( s->modrm_reg & 7 )
         {
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -7569,6 +7569,7 @@ x86_emulate(
         break;
 
     case X86EMUL_OPC_66(0x0f38, 0xf8): /* movdir64b r,m512 */
+    case X86EMUL_OPC_EVEX_66(4, 0xf8): /* movdir64b r,m512 */
         vcpu_must_have(movdir64b);
         generate_exception_if(ea.type != OP_MEM, X86_EXC_UD);
         src.val = truncate_ea(*dst.reg);
@@ -7680,6 +7681,9 @@ x86_emulate(
         }
         break;
 
+    case X86EMUL_OPC_EVEX_F3(4, 0xf8): /* enqcmds r,m512 */
+    case X86EMUL_OPC_EVEX_F2(4, 0xf8): /* enqcmd r,m512 */
+        generate_exception_if(ea.type != OP_MEM, X86_EXC_UD);
     enqcmd:
         vcpu_must_have(enqcmd);
         generate_exception_if(vex.pfx != vex_f2 && !mode_ring0(), X86_EXC_GP, 
0);
@@ -7710,7 +7714,8 @@ x86_emulate(
         state->simd_size = simd_none;
         break;
 
-    case X86EMUL_OPC(0x0f38, 0xf9): /* movdiri mem,r */
+    case X86EMUL_OPC(0x0f38, 0xf9): /* movdiri r,mem */
+    case X86EMUL_OPC_EVEX(4, 0xf9): /* movdiri r,mem */
         vcpu_must_have(movdiri);
         generate_exception_if(dst.type != OP_MEM, X86_EXC_UD);
         fail_if(!ops->blk);
--- a/xen/arch/x86/x86_emulate/util.c
+++ b/xen/arch/x86/x86_emulate/util.c
@@ -215,8 +215,11 @@ bool cf_check x86_insn_is_mem_write(cons
             return !mode_64bit();
 
         case X86EMUL_OPC_66(0x0f38, 0xf8): /* MOVDIR64B */
-        case X86EMUL_OPC_F2(0x0f38, 0xf8): /* ENQCMD */
         case X86EMUL_OPC_F3(0x0f38, 0xf8): /* ENQCMDS */
+        case X86EMUL_OPC_F2(0x0f38, 0xf8): /* ENQCMD */
+        case X86EMUL_OPC_EVEX_66(4, 0xf8): /* MOVDIR64B */
+        case X86EMUL_OPC_EVEX_F3(4, 0xf8): /* ENQCMDS */
+        case X86EMUL_OPC_EVEX_F2(4, 0xf8): /* ENQCMD */
             return true;
 
         case X86EMUL_OPC_EVEX_F3(0x0f38, 0x10) ...




 


Rackspace

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