* [PATCH 00/30] x86: permit guests to use APX
@ 2026-11-11 14:19 Jan Beulich
2026-11-11 14:20 ` [PATCH 01/30] x86/CPUID: introduce APX_F Jan Beulich
` (29 more replies)
0 siblings, 30 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:19 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
This initial posting is long overdue, but the series depends on various
other stalled (lack of review) work:
- x86: support AVX10
- vVMX: VMX insn handling
- (test harness) x86emul: lift macro machinery from XTF to aid EFLAGS handling
01: x86/CPUID: introduce APX_F
02: x86/CPUID: enable APX sub-leaf
03: x86/xstate: enable APX_F
04: x86emul: add eGPR parameter to decode_gpr()
05: x86emul: replace earlier relaxation of decode_gpr()
06: VMX: support APX
07: x86emul: latch XCR0 early during decode
08: x86emul: support REX2-prefixed insns
09: x86emul: APX_F REX2 testing
10: x86emul: support JMPABS
11: x86emul: switch to EVEX.R{3,4} nomenclature
12: x86emul: switch to EVEX.B{3,4} nomenclature
13: x86emul: switch to EVEX.X3 nomenclature
14: x86emul: support basic APX EVEX-encoded ALU insns
15: x86emul: support APX EVEX-encoded shift/rotate insns
16: x86emul: support remaining basic APX EVEX-Map4 insns
17: x86emul: support APX SCC insns
18: x86emul: support APX+ADX insns
19: x86emul: extend copy_VEX() for APX use
20: x86emul: support APX+AVX512 insns
21: x86emul: support APX+BMI1 insns
22: x86emul: support APX+BMI2 insns
23: x86emul: support APX+CMPccXADD insns
24: x86emul: support APX+{LZ,POP}CNT insns
25: x86emul: support APX+MOVBE insn
26: x86emul: support APX+MOVDIR/ENQCMD insns
27: x86emul: support APX+MOVRS insn
28: x86emul: support APX MSR access insns
29: x86: permit guests to use APX
30: x86emul: use latched XCR0 in x86emul_get_fpu()
Jan
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 01/30] x86/CPUID: introduce APX_F
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
@ 2026-11-11 14:20 ` Jan Beulich
2026-11-11 14:21 ` [PATCH 02/30] x86/CPUID: enable APX sub-leaf Jan Beulich
` (28 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:20 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
Add basic definitions and the dependencies of the feature.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
--- a/xen/include/public/arch-x86/cpufeatureset.h
+++ b/xen/include/public/arch-x86/cpufeatureset.h
@@ -367,6 +367,7 @@ XEN_CPUFEATURE(USER_MSR, 15*32
XEN_CPUFEATURE(UIRET_UIF, 15*32+17) /* UIRET updates UIF */
XEN_CPUFEATURE(CET_SSS, 15*32+18) /* CET Supervisor Shadow Stacks safe to use */
XEN_CPUFEATURE(AVX10, 15*32+19) /*a AVX10 Converged Vector ISA */
+XEN_CPUFEATURE(APX_F, 15*32+21) /* APX Foundation Instructions */
XEN_CPUFEATURE(SLSM, 15*32+24) /* Static Lockstep Mode */
/* Intel-defined CPU features, MSR_ARCH_CAPS 0x10a.eax, word 16 */
--- a/xen/tools/gen-cpuid.py
+++ b/xen/tools/gen-cpuid.py
@@ -260,7 +260,7 @@ def crunch_numbers(state):
# instruction groups which are specified to require XSAVE for state
# management.
XSAVE: [XSAVEOPT, XSAVEC, XGETBV1, XSAVES,
- AVX, MPX, PKU, AMX_TILE, LWP],
+ AVX, MPX, PKU, AMX_TILE, APX_F, LWP],
# AVX is taken to mean hardware support for 256bit registers (which in
# practice depends on the VEX prefix to encode), and the instructions
@@ -283,7 +283,7 @@ def crunch_numbers(state):
# NO_LMSL indicates the absense of Long Mode Segment Limits, which
# have been dropped in hardware.
LM: [CX16, PCID, LAHF_LM, PAGE1GB, PKU, NO_LMSL, AMX_TILE, CMPCCXADD,
- LKGS, MOVRS, MSRLIST, USER_MSR, MSR_IMM],
+ LKGS, MOVRS, MSRLIST, USER_MSR, MSR_IMM, APX_F],
# AMD K6-2+ and K6-III processors shipped with 3DNow+, beyond the
# standard 3DNow in the earlier K6 processors.
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 02/30] x86/CPUID: enable APX sub-leaf
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
2026-11-11 14:20 ` [PATCH 01/30] x86/CPUID: introduce APX_F Jan Beulich
@ 2026-11-11 14:21 ` Jan Beulich
2026-11-11 14:21 ` [PATCH 03/30] x86/xstate: enable APX_F Jan Beulich
` (27 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:21 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
The logic is modeled as closely as possible after that of leaf 7 and AVX10
sub-leaf handling.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
--- a/tools/libs/light/libxl_cpuid.c
+++ b/tools/libs/light/libxl_cpuid.c
@@ -344,6 +344,7 @@ int libxl_cpuid_parse_config(libxl_cpuid
MSR_ENTRY(0x10a, CPUID_REG_EDX),
CPUID_ENTRY(0x80000021, NA, CPUID_REG_ECX),
CPUID_ENTRY(0x00000024, 1, CPUID_REG_ECX),
+ CPUID_ENTRY(0x00000029, 0, CPUID_REG_EBX),
#undef MSR_ENTRY
#undef CPUID_ENTRY
};
--- a/tools/misc/xen-cpuid.c
+++ b/tools/misc/xen-cpuid.c
@@ -39,6 +39,7 @@ static const struct {
{ "MSR_ARCH_CAPS.hi", "m10Ah" },
{ "CPUID 0x80000021.ecx", "e21c" },
{ "CPUID 0x00000024:1.ecx", "24c1" },
+ { "CPUID 0x00000029:0.ebx", "29b0" },
};
#define COL_ALIGN "24"
--- a/xen/arch/x86/cpu/common.c
+++ b/xen/arch/x86/cpu/common.c
@@ -605,6 +605,11 @@ void identify_cpu(struct cpuinfo_x86 *c)
&tmp);
}
+ if (cpu_has(c, X86_FEATURE_APX_F) && c->cpuid_level >= 0x29)
+ cpuid_count(0x29, 0, &tmp,
+ &c->x86_capability[FEATURESET_29b0],
+ &tmp, &tmp);
+
if (test_bit(X86_FEATURE_ARCH_CAPS, c->x86_capability)) {
val = rdmsr(MSR_ARCH_CAPABILITIES);
c->x86_capability[FEATURESET_m10Al] = val;
--- a/xen/arch/x86/cpu-policy.c
+++ b/xen/arch/x86/cpu-policy.c
@@ -287,6 +287,13 @@ static void recalculate_misc(struct cpu_
memset(p->avx10.raw, 0, sizeof(p->avx10.raw));
}
+ zero_leaves(p->basic.raw, 0x25, 0x28);
+
+ p->apx.raw[0].c = 0;
+ p->apx.raw[0].d = 0;
+ if ( !p->feat.apx_f )
+ memset(p->apx.raw, 0, sizeof(p->apx.raw));
+
p->extd.e1d &= ~CPUID_COMMON_1D_FEATURES;
/* Most of Power/RAS hidden from guests. */
@@ -423,6 +430,7 @@ static void __init guest_common_max_leav
p->basic.max_leaf = ARRAY_SIZE(p->basic.raw) - 1;
p->feat.max_subleaf = ARRAY_SIZE(p->feat.raw) - 1;
p->avx10.max_subleaf = ARRAY_SIZE(p->avx10.raw) - 1;
+ p->apx.max_subleaf = ARRAY_SIZE(p->apx.raw) - 1;
p->extd.max_leaf = 0x80000000U + ARRAY_SIZE(p->extd.raw) - 1;
}
@@ -432,6 +440,7 @@ static void __init guest_common_default_
p->basic.max_leaf = host_cpu_policy.basic.max_leaf;
p->feat.max_subleaf = host_cpu_policy.feat.max_subleaf;
p->avx10.max_subleaf = host_cpu_policy.avx10.max_subleaf;
+ p->apx.max_subleaf = host_cpu_policy.apx.max_subleaf;
p->extd.max_leaf = host_cpu_policy.extd.max_leaf;
}
@@ -1043,6 +1052,7 @@ void recalculate_cpuid_policy(struct dom
p->basic.max_leaf = min(p->basic.max_leaf, max->basic.max_leaf);
p->feat.max_subleaf = min(p->feat.max_subleaf, max->feat.max_subleaf);
p->avx10.max_subleaf = min(p->avx10.max_subleaf, max->avx10.max_subleaf);
+ p->apx.max_subleaf = min(p->apx.max_subleaf, max->apx.max_subleaf);
p->extd.max_leaf = 0x80000000U | min(p->extd.max_leaf & 0xffff,
((p->x86_vendor & (X86_VENDOR_AMD |
X86_VENDOR_HYGON))
@@ -1091,6 +1101,8 @@ void recalculate_cpuid_policy(struct dom
__clear_bit(X86_FEATURE_XSAVE, fs);
if ( p->basic.max_leaf < 0x24 )
__clear_bit(X86_FEATURE_AVX10, fs);
+ if ( p->basic.max_leaf < 0x29 )
+ __clear_bit(X86_FEATURE_APX_F, fs);
sanitise_featureset(fs);
@@ -1188,6 +1200,8 @@ void __init init_dom0_cpuid_policy(struc
*/
if ( p->feat.avx10 )
p->avx10 = max->avx10;
+ if ( p->feat.apx_f )
+ p->apx = max->apx;
}
/*
@@ -1222,6 +1236,8 @@ static void __init __maybe_unused build_
sizeof(raw_cpu_policy.xstate.raw));
BUILD_BUG_ON(sizeof(raw_cpu_policy.avx10) !=
sizeof(raw_cpu_policy.avx10.raw));
+ BUILD_BUG_ON(sizeof(raw_cpu_policy.apx) !=
+ sizeof(raw_cpu_policy.apx.raw));
BUILD_BUG_ON(sizeof(raw_cpu_policy.extd) !=
sizeof(raw_cpu_policy.extd.raw));
}
--- a/xen/arch/x86/cpuid.c
+++ b/xen/arch/x86/cpuid.c
@@ -235,6 +235,15 @@ void guest_cpuid(const struct vcpu *v, u
*res = array_access_nospec(p->avx10.raw, subleaf);
break;
+ case 0x29:
+ ASSERT(p->apx.max_subleaf < ARRAY_SIZE(p->apx.raw));
+ if ( subleaf > min_t(uint32_t, p->apx.max_subleaf,
+ ARRAY_SIZE(p->apx.raw) - 1) )
+ return;
+
+ *res = array_access_nospec(p->apx.raw, subleaf);
+ break;
+
default:
*res = array_access_nospec(p->basic.raw, leaf);
break;
--- a/xen/arch/x86/lib/cpu-policy/copy-to-buffer.c
+++ b/xen/arch/x86/lib/cpu-policy/copy-to-buffer.c
@@ -130,6 +130,13 @@ int x86_cpuid_copy_to_buffer(const struc
COPY_LEAF(leaf, subleaf, &p->avx10.raw[subleaf]);
break;
+ case 0x29:
+ for ( subleaf = 0;
+ subleaf <= MIN(p->apx.max_subleaf,
+ ARRAY_SIZE(p->apx.raw) - 1); ++subleaf )
+ COPY_LEAF(leaf, subleaf, &p->apx.raw[subleaf]);
+ break;
+
default:
COPY_LEAF(leaf, XEN_CPUID_NO_SUBLEAF, &p->basic.raw[leaf]);
break;
--- a/xen/arch/x86/lib/cpu-policy/copy-from-buffer.c
+++ b/xen/arch/x86/lib/cpu-policy/copy-from-buffer.c
@@ -115,6 +115,13 @@ int x86_cpuid_copy_from_buffer(struct cp
array_access_nospec(p->avx10.raw, data.subleaf) = l;
break;
+ case 0x29:
+ if ( data.subleaf >= ARRAY_SIZE(p->apx.raw) )
+ goto out_of_range;
+
+ array_access_nospec(p->apx.raw, data.subleaf) = l;
+ break;
+
default:
if ( data.subleaf != XEN_CPUID_NO_SUBLEAF )
goto out_of_range;
--- a/xen/arch/x86/lib/cpu-policy/cpuid.c
+++ b/xen/arch/x86/lib/cpu-policy/cpuid.c
@@ -83,6 +83,7 @@ void x86_cpu_policy_to_featureset(
fs[FEATURESET_m10Ah] = p->arch_caps.hi;
fs[FEATURESET_e21c] = p->extd.e21c;
fs[FEATURESET_24c1] = p->avx10._24c1;
+ fs[FEATURESET_29b0] = p->apx._29b0;
}
void x86_cpu_featureset_to_policy(
@@ -108,6 +109,7 @@ void x86_cpu_featureset_to_policy(
p->arch_caps.hi = fs[FEATURESET_m10Ah];
p->extd.e21c = fs[FEATURESET_e21c];
p->avx10._24c1 = fs[FEATURESET_24c1];
+ p->apx._29b0 = fs[FEATURESET_29b0];
}
void x86_cpu_policy_recalc_synth(struct cpu_policy *p)
@@ -127,7 +129,7 @@ void x86_cpu_policy_fill_native(struct c
switch ( i )
{
case 0x4: case 0x7: case 0xb: case 0xd:
- case 0x24:
+ case 0x24: case 0x29:
/* Multi-invocation leaves. Deferred. */
continue;
}
@@ -230,6 +232,15 @@ void x86_cpu_policy_fill_native(struct c
cpuid_count_leaf(0x24, i, &p->avx10.raw[i]);
}
+ if ( p->basic.max_leaf >= 0x29 )
+ {
+ cpuid_count_leaf(0x29, 0, &p->apx.raw[0]);
+
+ for ( i = 1; i <= MIN(p->apx.max_subleaf,
+ ARRAY_SIZE(p->apx.raw) - 1); ++i )
+ cpuid_count_leaf(0x24, i, &p->apx.raw[i]);
+ }
+
/* Extended leaves. */
cpuid_leaf(0x80000000U, &p->extd.raw[0]);
for ( i = 1; i <= MIN(p->extd.max_leaf & 0xffffU,
@@ -302,6 +313,9 @@ void x86_cpu_policy_clear_out_of_range_l
if ( p->basic.max_leaf < 0x24 )
memset(p->avx10.raw, 0, sizeof(p->avx10.raw));
+ if ( p->basic.max_leaf < 0x29 )
+ memset(p->apx.raw, 0, sizeof(p->apx.raw));
+
zero_leaves(p->extd.raw,
((p->extd.max_leaf >> 16) == 0x8000
? (p->extd.max_leaf & 0xffff) + 1 : 0),
@@ -316,6 +330,8 @@ void __init x86_cpu_policy_bound_max_lea
min_t(uint32_t, p->feat.max_subleaf, ARRAY_SIZE(p->feat.raw) - 1);
p->avx10.max_subleaf =
min_t(uint32_t, p->avx10.max_subleaf, ARRAY_SIZE(p->avx10.raw) - 1);
+ p->apx.max_subleaf =
+ min_t(uint32_t, p->apx.max_subleaf, ARRAY_SIZE(p->apx.raw) - 1);
p->extd.max_leaf = 0x80000000U | min_t(uint32_t, p->extd.max_leaf & 0xffff,
ARRAY_SIZE(p->extd.raw) - 1);
}
@@ -345,6 +361,8 @@ void x86_cpu_policy_shrink_max_leaves(st
p->basic.raw[0x24] = p->avx10.raw[0];
+ p->basic.raw[0x29] = p->apx.raw[0];
+
for ( i = p->basic.max_leaf; i; --i )
if ( p->basic.raw[i].a | p->basic.raw[i].b |
p->basic.raw[i].c | p->basic.raw[i].d )
--- a/xen/arch/x86/lib/cpu-policy/policy.c
+++ b/xen/arch/x86/lib/cpu-policy/policy.c
@@ -28,6 +28,9 @@ int x86_cpu_policies_are_compatible(cons
guest->avx10.max_subleaf > host->avx10.max_subleaf )
FAIL_CPUID(0x24, 0);
+ if ( guest->apx.max_subleaf > host->apx.max_subleaf )
+ FAIL_CPUID(0x29, 0);
+
if ( guest->extd.max_leaf > host->extd.max_leaf )
FAIL_CPUID(0x80000000U, NA);
--- a/xen/include/public/arch-x86/cpufeatureset.h
+++ b/xen/include/public/arch-x86/cpufeatureset.h
@@ -416,6 +416,9 @@ XEN_CPUFEATURE(AVX10_BMM, 19*32
XEN_CPUFEATURE(AVX10_V1_AUX, 19*32+ 2) /*a AVX10 V1 Auxiliary Instructions */
XEN_CPUFEATURE(AVX10_V2_AUX, 19*32+ 3) /*a AVX10 V2 Auxiliary Instructions */
+/* Intel-defined CPU features, CPUID level 0x00000029:0.ebx, word 20 */
+XEN_CPUFEATURE(APX_NCI_NDD_NF, 20*32+ 0) /*a APX NCI/NDD/NF instruction forms */
+
#endif /* XEN_CPUFEATURE */
/* Clean up from a default include. Close the enum (for C). */
--- a/xen/include/xen/lib/x86/cpu-policy.h
+++ b/xen/include/xen/lib/x86/cpu-policy.h
@@ -24,6 +24,7 @@
#define FEATURESET_m10Ah 17 /* 0x0000010a.edx */
#define FEATURESET_e21c 18 /* 0x80000021.ecx */
#define FEATURESET_24c1 19 /* 0x00000024:1.ecx */
+#define FEATURESET_29b0 20 /* 0x00000029:0.ebx */
struct cpuid_leaf
{
@@ -60,12 +61,13 @@ unsigned int x86_cpuid_lookup_vendor(uin
*/
const char *x86_cpuid_vendor_to_str(unsigned int vendor);
-#define CPUID_GUEST_NR_BASIC (0x24u + 1)
+#define CPUID_GUEST_NR_BASIC (0x29u + 1)
#define CPUID_GUEST_NR_CACHE (5u + 1)
#define CPUID_GUEST_NR_FEAT (2u + 1)
#define CPUID_GUEST_NR_TOPO (1u + 1)
#define CPUID_GUEST_NR_XSTATE (62u + 1)
#define CPUID_GUEST_NR_AVX10 (1u + 1)
+#define CPUID_GUEST_NR_APX (0u + 1)
#define CPUID_GUEST_NR_EXTD_INTEL (0x8u + 1)
#define CPUID_GUEST_NR_EXTD_AMD (0x21u + 1)
#define CPUID_GUEST_NR_EXTD MAX(CPUID_GUEST_NR_EXTD_INTEL, \
@@ -287,6 +289,20 @@ struct cpu_policy
};
} avx10;
+ /* Structured APX information leaf: 0x000000029[xx] */
+ union {
+ struct cpuid_leaf raw[CPUID_GUEST_NR_APX];
+ struct {
+ /* Subleaf 0. */
+ uint32_t max_subleaf;
+ union {
+ uint32_t _29b0;
+ struct { DECL_BITFIELD(29b0); };
+ };
+ uint32_t /* c */:32, /* d */:32;
+ };
+ } apx;
+
/* Extended leaves: 0x800000xx */
union {
struct cpuid_leaf raw[CPUID_GUEST_NR_EXTD];
--- a/xen/tools/gen-cpuid.py
+++ b/xen/tools/gen-cpuid.py
@@ -355,6 +355,9 @@ def crunch_numbers(state):
# computational instructions. All further AMX features are built on top
# of AMX-TILE.
AMX_TILE: [AMX_BF16, AMX_INT8, AMX_FP16, AMX_COMPLEX],
+
+ # APX discrete features.
+ APX_F: [APX_NCI_NDD_NF],
}
deep_features = tuple(sorted(deps.keys()))
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 03/30] x86/xstate: enable APX_F
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
2026-11-11 14:20 ` [PATCH 01/30] x86/CPUID: introduce APX_F Jan Beulich
2026-11-11 14:21 ` [PATCH 02/30] x86/CPUID: enable APX sub-leaf Jan Beulich
@ 2026-11-11 14:21 ` Jan Beulich
2026-11-11 14:22 ` [PATCH 04/30] x86emul: add eGPR parameter to decode_gpr() Jan Beulich
` (26 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:21 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
Add / adjust basic definitions. Adjust recalculate_xstate() and
xstate_check_sizes() accordingly.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
--- a/xen/arch/x86/cpu-policy.c
+++ b/xen/arch/x86/cpu-policy.c
@@ -220,6 +220,9 @@ static void recalculate_xstate(struct cp
if ( p->feat.amx_tile )
xstates |= X86_XCR0_TILE_CFG | X86_XCR0_TILE_DATA;
+ if ( p->feat.apx_f )
+ xstates |= X86_XCR0_APX;
+
/* Subleaf 0 */
p->xstate.max_size =
xstate_uncompressed_size(xstates & ~XSTATE_XSAVES_ONLY);
--- a/xen/arch/x86/include/asm/cpufeature.h
+++ b/xen/arch/x86/include/asm/cpufeature.h
@@ -220,6 +220,9 @@ static inline bool boot_cpu_has(unsigned
#define cpu_has_verw_clear boot_cpu_has(X86_FEATURE_VERW_CLEAR)
#define cpu_has_nscb boot_cpu_has(X86_FEATURE_NSCB)
+/* CPUID level 0x00000007:1.edx */
+#define cpu_has_apx_f boot_cpu_has(X86_FEATURE_APX_F)
+
/* MSR_ARCH_CAPS */
#define cpu_has_rdcl_no boot_cpu_has(X86_FEATURE_RDCL_NO)
#define cpu_has_eibrs boot_cpu_has(X86_FEATURE_EIBRS)
--- a/xen/arch/x86/include/asm/x86-defns.h
+++ b/xen/arch/x86/include/asm/x86-defns.h
@@ -109,13 +109,14 @@
#define X86_XSS_HWP (_AC(1, ULL) << 16)
#define X86_XCR0_TILE_CFG (_AC(1, ULL) << 17)
#define X86_XCR0_TILE_DATA (_AC(1, ULL) << 18)
+#define X86_XCR0_APX (_AC(1, ULL) << 19)
#define X86_XCR0_LWP (_AC(1, ULL) << 62)
#define X86_XCR0_STATES \
(X86_XCR0_X87 | X86_XCR0_SSE | X86_XCR0_YMM | X86_XCR0_BNDREGS | \
X86_XCR0_BNDCSR | X86_XCR0_OPMASK | X86_XCR0_ZMM | \
X86_XCR0_HI_ZMM | X86_XCR0_PKRU | X86_XCR0_TILE_CFG | \
- X86_XCR0_TILE_DATA | \
+ X86_XCR0_TILE_DATA | X86_XCR0_APX | \
X86_XCR0_LWP)
#define X86_XSS_STATES \
--- a/xen/arch/x86/include/asm/xstate.h
+++ b/xen/arch/x86/include/asm/xstate.h
@@ -34,7 +34,7 @@ extern uint32_t mxcsr_mask;
#define XCNTXT_MASK (X86_XCR0_X87 | X86_XCR0_SSE | X86_XCR0_YMM | \
X86_XCR0_OPMASK | X86_XCR0_ZMM | X86_XCR0_HI_ZMM | \
X86_XCR0_BNDREGS | X86_XCR0_BNDCSR | X86_XCR0_PKRU | \
- X86_XCR0_TILE_CFG | X86_XCR0_TILE_DATA)
+ X86_XCR0_TILE_CFG | X86_XCR0_TILE_DATA | X86_XCR0_APX)
#define XSTATE_ALL (~(1ULL << 63))
#define XSTATE_XSAVES_ONLY 0
--- a/xen/arch/x86/xstate.c
+++ b/xen/arch/x86/xstate.c
@@ -847,6 +847,9 @@ static void __init noinline xstate_check
if ( boot_cpu_has(X86_FEATURE_AMX_TILE) )
check_new_xstate(&s, X86_XCR0_TILE_DATA | X86_XCR0_TILE_CFG);
+ if ( cpu_has_apx_f )
+ check_new_xstate(&s, X86_XCR0_APX);
+
if ( boot_cpu_has(X86_FEATURE_LWP) )
check_new_xstate(&s, X86_XCR0_LWP);
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 04/30] x86emul: add eGPR parameter to decode_gpr()
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (2 preceding siblings ...)
2026-11-11 14:21 ` [PATCH 03/30] x86/xstate: enable APX_F Jan Beulich
@ 2026-11-11 14:22 ` Jan Beulich
2026-11-11 14:22 ` [PATCH 05/30] x86emul: replace earlier relaxation of decode_gpr() Jan Beulich
` (25 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:22 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
The respective structure is introduced empty here, for this to be a
purely mechanical change. Note however that NULL is passed in a few
cases, when eGPR-s can't come into play (e.g. for insns not valid in
64-bit mode, or for encodings [e.g. VEX] which cannot specify eGPR-s).
While doing this rename decode_gpr()'s last parameter, though: "modrm"
clearly has been misleading at best.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -2179,7 +2179,8 @@ int hvm_set_efer(uint64_t value)
int hvm_mov_to_cr(unsigned int cr, unsigned int gpr)
{
struct vcpu *curr = current;
- unsigned long val = *decode_gpr(guest_cpu_user_regs(), gpr);
+ struct egpr egpr = {};
+ unsigned long val = *decode_gpr(guest_cpu_user_regs(), &egpr, gpr);
int rc;
TRACE(TRC_HVM_CR_WRITE64, cr, val, val >> 32);
@@ -2228,7 +2229,8 @@ int hvm_mov_to_cr(unsigned int cr, unsig
int hvm_mov_from_cr(unsigned int cr, unsigned int gpr)
{
struct vcpu *curr = current;
- unsigned long val = 0, *reg = decode_gpr(guest_cpu_user_regs(), gpr);
+ struct egpr egpr = {};
+ unsigned long val = 0, *reg = decode_gpr(guest_cpu_user_regs(), &egpr, gpr);
switch ( cr )
{
--- a/xen/arch/x86/hvm/vmx/vmx.c
+++ b/xen/arch/x86/hvm/vmx/vmx.c
@@ -4718,12 +4718,13 @@ void asmlinkage vmx_vmexit_handler(struc
case EXIT_REASON_URDMSR: /* NB: User-MSR bitmap was checked by the CPU. */
{
uint64_t msr_content = 0;
+ struct egpr egpr = {};
switch ( hvm_msr_read_intercept(vmread(EXIT_QUALIFICATION),
&msr_content) )
{
case X86EMUL_OKAY:
- *decode_gpr(regs, msr_imm_gpr()) = msr_content;
+ *decode_gpr(regs, &egpr, msr_imm_gpr()) = msr_content;
update_guest_eip(); /* Safe: URDMSR / RDMSR <imm> */
break;
@@ -4743,9 +4744,13 @@ void asmlinkage vmx_vmexit_handler(struc
}
fallthrough;
case EXIT_REASON_UWRMSR: /* NB: User-MSR bitmap was checked by the CPU. */
+ {
+ struct egpr egpr = {};
+
exit_qualification = vmread(EXIT_QUALIFICATION);
switch ( hvm_msr_write_intercept(exit_qualification,
- *decode_gpr(regs, msr_imm_gpr()),
+ *decode_gpr(regs, &egpr,
+ msr_imm_gpr()),
true) )
{
case X86EMUL_OKAY:
@@ -4757,6 +4762,7 @@ void asmlinkage vmx_vmexit_handler(struc
break;
}
break;
+ }
case EXIT_REASON_VMXOFF:
case EXIT_REASON_VMXON:
--- a/xen/arch/x86/hvm/vmx/vvmx.c
+++ b/xen/arch/x86/hvm/vmx/vvmx.c
@@ -360,10 +360,10 @@ enum vmx_insn_errno set_vvmcs_real_safe(
return virtual_vmcs_vmwrite_safe(v, encoding, val);
}
-static unsigned long reg_read(struct cpu_user_regs *regs,
+static unsigned long reg_read(struct cpu_user_regs *regs, struct egpr *egpr,
unsigned int index)
{
- unsigned long val = *decode_gpr(regs, index);
+ unsigned long val = *decode_gpr(regs, egpr, index);
/*
* Outside of 64-bit mode, zero-extend the result from 32 bits, like
@@ -377,7 +377,7 @@ static unsigned long reg_read(struct cpu
return val;
}
-static void reg_write(struct cpu_user_regs *regs,
+static void reg_write(struct cpu_user_regs *regs, struct egpr *egpr,
unsigned int index,
unsigned long value)
{
@@ -389,7 +389,7 @@ static void reg_write(struct cpu_user_re
*/
if ( !hvm_long_mode_active(current) )
value = (uint32_t)value;
- *decode_gpr(regs, index) = value;
+ *decode_gpr(regs, egpr, index) = value;
}
static inline u32 __n2_pin_exec_control(struct vcpu *v)
@@ -413,6 +413,7 @@ static inline u32 __n2_secondary_exec_co
}
static int decode_vmx_inst(struct cpu_user_regs *regs,
+ struct egpr *egpr,
struct vmx_inst_decoded *decode,
unsigned long *poperandS, unsigned int size)
{
@@ -429,7 +430,7 @@ static int decode_vmx_inst(struct cpu_us
decode->type = VMX_INST_MEMREG_TYPE_REG;
decode->reg1 = info.fields.reg1;
if ( poperandS != NULL )
- *poperandS = reg_read(regs, decode->reg1);
+ *poperandS = reg_read(regs, egpr, decode->reg1);
}
else
{
@@ -443,10 +444,10 @@ static int decode_vmx_inst(struct cpu_us
seg_base = seg.base;
base = info.fields.base_reg_invalid ? 0 :
- reg_read(regs, info.fields.base_reg);
+ reg_read(regs, egpr, info.fields.base_reg);
index = info.fields.index_reg_invalid ? 0 :
- reg_read(regs, info.fields.index_reg);
+ reg_read(regs, egpr, info.fields.index_reg);
scale = 1 << info.fields.scaling;
@@ -1558,7 +1559,7 @@ void asmlinkage nvmx_switch_guest(void)
* VMX instructions handling
*/
-static int nvmx_handle_vmxon(struct cpu_user_regs *regs)
+static int nvmx_handle_vmxon(struct cpu_user_regs *regs, struct egpr *egpr)
{
struct vcpu *v=current;
struct nestedvmx *nvmx = &vcpu_2_nvmx(v);
@@ -1568,7 +1569,7 @@ static int nvmx_handle_vmxon(struct cpu_
uint32_t nvmcs_revid;
int rc;
- rc = decode_vmx_inst(regs, &decode, &gpa, sizeof(gpa));
+ rc = decode_vmx_inst(regs, egpr, &decode, &gpa, sizeof(gpa));
if ( rc != X86EMUL_OKAY )
return rc;
@@ -1787,7 +1788,7 @@ static int nvmx_handle_vmlaunch(struct c
return rc;
}
-static int nvmx_handle_vmptrld(struct cpu_user_regs *regs)
+static int nvmx_handle_vmptrld(struct cpu_user_regs *regs, struct egpr *egpr)
{
struct vcpu *v = current;
struct vmx_inst_decoded decode;
@@ -1795,7 +1796,7 @@ static int nvmx_handle_vmptrld(struct cp
unsigned long gpa = 0;
int rc;
- rc = decode_vmx_inst(regs, &decode, &gpa, sizeof(gpa));
+ rc = decode_vmx_inst(regs, egpr, &decode, &gpa, sizeof(gpa));
if ( rc != X86EMUL_OKAY )
return rc;
@@ -1863,7 +1864,7 @@ out:
return X86EMUL_OKAY;
}
-static int nvmx_handle_vmptrst(struct cpu_user_regs *regs)
+static int nvmx_handle_vmptrst(struct cpu_user_regs *regs, struct egpr *egpr)
{
struct vcpu *v = current;
struct vmx_inst_decoded decode;
@@ -1872,7 +1873,7 @@ static int nvmx_handle_vmptrst(struct cp
unsigned long gpa = 0;
int rc;
- rc = decode_vmx_inst(regs, &decode, &gpa, sizeof(gpa));
+ rc = decode_vmx_inst(regs, egpr, &decode, &gpa, sizeof(gpa));
if ( rc != X86EMUL_OKAY )
return rc;
@@ -1888,7 +1889,7 @@ static int nvmx_handle_vmptrst(struct cp
return X86EMUL_OKAY;
}
-static int nvmx_handle_vmclear(struct cpu_user_regs *regs)
+static int nvmx_handle_vmclear(struct cpu_user_regs *regs, struct egpr *egpr)
{
struct vcpu *v = current;
struct vmx_inst_decoded decode;
@@ -1898,7 +1899,7 @@ static int nvmx_handle_vmclear(struct cp
void *vvmcs;
int rc;
- rc = decode_vmx_inst(regs, &decode, &gpa, sizeof(gpa));
+ rc = decode_vmx_inst(regs, egpr, &decode, &gpa, sizeof(gpa));
if ( rc != X86EMUL_OKAY )
return rc;
@@ -1952,7 +1953,7 @@ out:
return X86EMUL_OKAY;
}
-static int nvmx_handle_vmread(struct cpu_user_regs *regs)
+static int nvmx_handle_vmread(struct cpu_user_regs *regs, struct egpr *egpr)
{
struct vcpu *v = current;
struct vmx_inst_decoded decode;
@@ -1960,7 +1961,7 @@ static int nvmx_handle_vmread(struct cpu
u64 value = 0;
int rc;
- rc = decode_vmx_inst(regs, &decode, NULL, 0);
+ rc = decode_vmx_inst(regs, egpr, &decode, NULL, 0);
if ( rc != X86EMUL_OKAY )
return rc;
@@ -1970,7 +1971,7 @@ static int nvmx_handle_vmread(struct cpu
return X86EMUL_OKAY;
}
- rc = get_vvmcs_safe(v, reg_read(regs, decode.reg2), &value);
+ rc = get_vvmcs_safe(v, reg_read(regs, egpr, decode.reg2), &value);
if ( rc != VMX_INSN_SUCCEED )
{
vmfail(regs, rc);
@@ -1986,7 +1987,7 @@ static int nvmx_handle_vmread(struct cpu
return X86EMUL_EXCEPTION;
break;
case VMX_INST_MEMREG_TYPE_REG:
- reg_write(regs, decode.reg1, value);
+ reg_write(regs, egpr, decode.reg1, value);
break;
}
@@ -1994,7 +1995,7 @@ static int nvmx_handle_vmread(struct cpu
return X86EMUL_OKAY;
}
-static int nvmx_handle_vmwrite(struct cpu_user_regs *regs)
+static int nvmx_handle_vmwrite(struct cpu_user_regs *regs, struct egpr *egpr)
{
struct vcpu *v = current;
struct vmx_inst_decoded decode;
@@ -2003,7 +2004,7 @@ static int nvmx_handle_vmwrite(struct cp
enum vmx_insn_errno err;
int rc;
- rc = decode_vmx_inst(regs, &decode, &operand, 0);
+ rc = decode_vmx_inst(regs, egpr, &decode, &operand, 0);
if ( rc != X86EMUL_OKAY )
return rc;
@@ -2013,7 +2014,7 @@ static int nvmx_handle_vmwrite(struct cp
return X86EMUL_OKAY;
}
- vmcs_encoding = reg_read(regs, decode.reg2);
+ vmcs_encoding = reg_read(regs, egpr, decode.reg2);
err = set_vvmcs_safe(v, vmcs_encoding, operand);
if ( err != VMX_INSN_SUCCEED )
{
@@ -2039,16 +2040,16 @@ static int nvmx_handle_vmwrite(struct cp
return X86EMUL_OKAY;
}
-static int nvmx_handle_invept(struct cpu_user_regs *regs)
+static int nvmx_handle_invept(struct cpu_user_regs *regs, struct egpr *egpr)
{
struct vmx_inst_decoded decode;
unsigned long eptp;
int ret;
- if ( (ret = decode_vmx_inst(regs, &decode, &eptp, 16)) != X86EMUL_OKAY )
+ if ( (ret = decode_vmx_inst(regs, egpr, &decode, &eptp, 16)) != X86EMUL_OKAY )
return ret;
- switch ( reg_read(regs, decode.reg2) )
+ switch ( reg_read(regs, egpr, decode.reg2) )
{
case INVEPT_SINGLE_CONTEXT:
{
@@ -2067,16 +2068,16 @@ static int nvmx_handle_invept(struct cpu
return X86EMUL_OKAY;
}
-static int nvmx_handle_invvpid(struct cpu_user_regs *regs)
+static int nvmx_handle_invvpid(struct cpu_user_regs *regs, struct egpr *egpr)
{
struct vmx_inst_decoded decode;
unsigned long vpid;
int ret;
- if ( (ret = decode_vmx_inst(regs, &decode, &vpid, 16)) != X86EMUL_OKAY )
+ if ( (ret = decode_vmx_inst(regs, egpr, &decode, &vpid, 16)) != X86EMUL_OKAY )
return ret;
- switch ( reg_read(regs, decode.reg2) )
+ switch ( reg_read(regs, egpr, decode.reg2) )
{
/* Just invalidate all tlb entries for all types! */
case INVVPID_INDIVIDUAL_ADDR:
@@ -2094,9 +2095,11 @@ static int nvmx_handle_invvpid(struct cp
return X86EMUL_OKAY;
}
-int nvmx_handle_vmx_insn(struct cpu_user_regs *regs, unsigned int exit_reason)
+int nvmx_handle_vmx_insn(struct cpu_user_regs *regs,
+ unsigned int exit_reason)
{
struct vcpu *curr = current;
+ struct egpr egpr = {};
int ret;
if ( !(curr->arch.hvm.guest_cr[4] & X86_CR4_VMXE) ||
@@ -2130,27 +2133,27 @@ int nvmx_handle_vmx_insn(struct cpu_user
break;
case EXIT_REASON_VMXON:
- ret = nvmx_handle_vmxon(regs);
+ ret = nvmx_handle_vmxon(regs, &egpr);
break;
case EXIT_REASON_VMCLEAR:
- ret = nvmx_handle_vmclear(regs);
+ ret = nvmx_handle_vmclear(regs, &egpr);
break;
case EXIT_REASON_VMPTRLD:
- ret = nvmx_handle_vmptrld(regs);
+ ret = nvmx_handle_vmptrld(regs, &egpr);
break;
case EXIT_REASON_VMPTRST:
- ret = nvmx_handle_vmptrst(regs);
+ ret = nvmx_handle_vmptrst(regs, &egpr);
break;
case EXIT_REASON_VMREAD:
- ret = nvmx_handle_vmread(regs);
+ ret = nvmx_handle_vmread(regs, &egpr);
break;
case EXIT_REASON_VMWRITE:
- ret = nvmx_handle_vmwrite(regs);
+ ret = nvmx_handle_vmwrite(regs, &egpr);
break;
case EXIT_REASON_VMLAUNCH:
@@ -2162,11 +2165,11 @@ int nvmx_handle_vmx_insn(struct cpu_user
break;
case EXIT_REASON_INVEPT:
- ret = nvmx_handle_invept(regs);
+ ret = nvmx_handle_invept(regs, &egpr);
break;
case EXIT_REASON_INVVPID:
- ret = nvmx_handle_invvpid(regs);
+ ret = nvmx_handle_invvpid(regs, &egpr);
break;
default:
@@ -2699,7 +2702,9 @@ int nvmx_n2_vmexit_handler(struct cpu_us
{
case VMX_CR_ACCESS_TYPE_MOV_TO_CR:
{
- val = reg_read(regs, qual.gpr);
+ struct egpr egpr = {};
+
+ val = reg_read(regs, &egpr, qual.gpr);
if ( qual.cr == 0 )
{
--- a/xen/arch/x86/x86_emulate/0fae.c
+++ b/xen/arch/x86/x86_emulate/0fae.c
@@ -176,7 +176,7 @@ int x86emul_0fae(struct x86_emulate_stat
fail_if(!ops->read_segment);
if ( (rc = ops->read_segment(seg, &sreg, ctxt)) != X86EMUL_OKAY )
goto done;
- dst->reg = decode_gpr(regs, s->modrm_rm);
+ dst->reg = decode_gpr(regs, &s->egpr, s->modrm_rm);
if ( !(s->modrm_reg & 2) )
{
/* rd{f,g}sbase */
--- a/xen/arch/x86/x86_emulate/decode.c
+++ b/xen/arch/x86/x86_emulate/decode.c
@@ -1827,7 +1827,7 @@ int x86emul_decode(struct x86_emulate_st
!s->evex.RX) << 4;
else if ( s->sib_index != 4 )
{
- s->ea.mem.off = *decode_gpr(ctxt->regs, s->sib_index);
+ s->ea.mem.off = *decode_gpr(ctxt->regs, &s->egpr, s->sib_index);
s->ea.mem.off <<= s->sib_scale;
}
if ( (s->modrm_mod == 0) && ((sib_base & 7) == 5) )
@@ -1847,13 +1847,13 @@ int x86emul_decode(struct x86_emulate_st
s->ea.mem.off += ctxt->regs->r(bp);
}
else
- s->ea.mem.off += *decode_gpr(ctxt->regs, sib_base);
+ s->ea.mem.off += *decode_gpr(ctxt->regs, &s->egpr, sib_base);
}
else
{
generate_exception_if(d & vSIB, X86_EXC_UD);
s->modrm_rm |= (s->rex_prefix & 1) << 3;
- s->ea.mem.off = *decode_gpr(ctxt->regs, s->modrm_rm);
+ 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
@@ -336,6 +336,8 @@ struct x86_emulate_state {
unsigned long ip;
+ struct egpr egpr;
+
struct stub_exn *stub_exn;
#ifndef NDEBUG
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -1168,7 +1168,8 @@ const uint8_t cpu_user_regs_gpr_offsets[
};
static void *_decode_gpr(
- struct cpu_user_regs *regs, unsigned int modrm_reg, bool legacy)
+ struct cpu_user_regs *regs, struct egpr *egpr, unsigned int modrm_reg,
+ bool legacy)
{
static const uint8_t byte_reg_offsets[] = {
offsetof(struct cpu_user_regs, al),
@@ -1182,7 +1183,7 @@ static void *_decode_gpr(
};
if ( !legacy )
- return decode_gpr(regs, modrm_reg);
+ return decode_gpr(regs, egpr, modrm_reg);
/* Check that the array is a power of two. */
BUILD_BUG_ON(ARRAY_SIZE(byte_reg_offsets) &
@@ -1200,7 +1201,7 @@ static unsigned long *decode_vex_gpr(
unsigned int vex_reg, struct cpu_user_regs *regs,
const struct x86_emulate_ctxt *ctxt)
{
- return decode_gpr(regs, ~vex_reg & (mode_64bit() ? 0xf : 7));
+ return decode_gpr(regs, NULL, ~vex_reg & (mode_64bit() ? 0xf : 7));
}
#define visa_check(subfeat) \
@@ -1370,7 +1371,8 @@ x86_emulate(
generate_exception_if(state->not_64bit && mode_64bit(), X86_EXC_UD);
if ( ea.type == OP_REG )
- ea.reg = _decode_gpr(&_regs, modrm_rm, (d & ByteOp) && !rex_prefix && !vex.opcx);
+ ea.reg = _decode_gpr(&_regs, &state->egpr, modrm_rm,
+ (d & ByteOp) && !rex_prefix && !vex.opcx);
memset(mmvalp, 0xaa /* arbitrary */, sizeof(*mmvalp));
@@ -1384,13 +1386,14 @@ x86_emulate(
src.type = OP_REG;
if ( d & ByteOp )
{
- src.reg = _decode_gpr(&_regs, modrm_reg, !rex_prefix && !vex.opcx);
+ src.reg = _decode_gpr(&_regs, &state->egpr, modrm_reg,
+ !rex_prefix && !vex.opcx);
src.val = *(uint8_t *)src.reg;
src.bytes = 1;
}
else
{
- src.reg = decode_gpr(&_regs, modrm_reg);
+ src.reg = decode_gpr(&_regs, &state->egpr, modrm_reg);
switch ( (src.bytes = op_bytes) )
{
case 2: src.val = *(uint16_t *)src.reg; break;
@@ -1496,13 +1499,14 @@ x86_emulate(
dst.type = OP_REG;
if ( d & ByteOp )
{
- dst.reg = _decode_gpr(&_regs, modrm_reg, !rex_prefix && !vex.opcx);
+ dst.reg = _decode_gpr(&_regs, &state->egpr, modrm_reg,
+ !rex_prefix && !vex.opcx);
dst.val = *(uint8_t *)dst.reg;
dst.bytes = 1;
}
else
{
- dst.reg = decode_gpr(&_regs, modrm_reg);
+ dst.reg = decode_gpr(&_regs, &state->egpr, modrm_reg);
switch ( (dst.bytes = op_bytes) )
{
case 2: dst.val = *(uint16_t *)dst.reg; break;
@@ -1739,7 +1743,7 @@ x86_emulate(
case 0x40 ... 0x4f: /* inc/dec reg */
dst.type = OP_REG;
- dst.reg = decode_gpr(&_regs, b & 7);
+ dst.reg = decode_gpr(&_regs, NULL, b & 7);
dst.bytes = op_bytes;
dst.val = *dst.reg;
if ( b & 8 )
@@ -1749,12 +1753,14 @@ x86_emulate(
break;
case 0x50 ... 0x57: /* push reg */
- src.val = *decode_gpr(&_regs, (b & 7) | ((rex_prefix & 1) << 3));
+ src.val = *decode_gpr(&_regs, &state->egpr,
+ (b & 7) | ((rex_prefix & 1) << 3));
goto push;
case 0x58 ... 0x5f: /* pop reg */
dst.type = OP_REG;
- dst.reg = decode_gpr(&_regs, (b & 7) | ((rex_prefix & 1) << 3));
+ dst.reg = decode_gpr(&_regs, &state->egpr,
+ (b & 7) | ((rex_prefix & 1) << 3));
dst.bytes = op_bytes;
if ( mode_64bit() && (dst.bytes == 4) )
dst.bytes = 8;
@@ -1768,7 +1774,7 @@ x86_emulate(
ea.val = _regs.esp;
for ( i = 0; i < 8; i++ )
{
- void *reg = decode_gpr(&_regs, i);
+ void *reg = decode_gpr(&_regs, NULL, i);
if ( (rc = ops->write(x86_seg_ss, sp_pre_dec(op_bytes),
reg != &_regs.esp ? reg : &ea.val,
@@ -1780,7 +1786,7 @@ x86_emulate(
case 0x61: /* popa */
for ( i = 0; i < 8; i++ )
{
- void *reg = decode_gpr(&_regs, 7 - i);
+ void *reg = decode_gpr(&_regs, NULL, 7 - i);
if ( (rc = read_ulong(x86_seg_ss, sp_post_inc(op_bytes),
&dst.val, op_bytes, ctxt, ops)) != 0 )
@@ -2079,7 +2085,8 @@ x86_emulate(
case 0x91 ... 0x97: /* xchg reg,%%rax */
dst.type = OP_REG;
dst.bytes = op_bytes;
- dst.reg = decode_gpr(&_regs, (b & 7) | ((rex_prefix & 1) << 3));
+ dst.reg = decode_gpr(&_regs, &state->egpr,
+ (b & 7) | ((rex_prefix & 1) << 3));
dst.val = *dst.reg;
goto xchg;
@@ -2325,13 +2332,15 @@ x86_emulate(
}
case 0xb0 ... 0xb7: /* mov imm8,r8 */
- dst.reg = _decode_gpr(&_regs, (b & 7) | ((rex_prefix & 1) << 3),
+ dst.reg = _decode_gpr(&_regs, &state->egpr,
+ (b & 7) | ((rex_prefix & 1) << 3),
!rex_prefix);
dst.val = src.val;
break;
case 0xb8 ... 0xbf: /* mov imm{16,32,64},r{16,32,64} */
- dst.reg = decode_gpr(&_regs, (b & 7) | ((rex_prefix & 1) << 3));
+ dst.reg = decode_gpr(&_regs, &state->egpr,
+ (b & 7) | ((rex_prefix & 1) << 3));
dst.val = src.val;
break;
@@ -3787,7 +3796,7 @@ x86_emulate(
}
place_ret(&opc[2]);
- ea.reg = decode_gpr(&_regs, modrm_reg);
+ ea.reg = decode_gpr(&_regs, &state->egpr, modrm_reg);
invoke_stub("", "", "=a" (*ea.reg) : "c" (mmvalp), "m" (*mmvalp));
put_stub(stub);
@@ -5079,7 +5088,7 @@ x86_emulate(
place_ret(&opc[2]);
copy_VEX(opc, vex);
- ea.reg = decode_gpr(&_regs, modrm_rm);
+ ea.reg = decode_gpr(&_regs, NULL, modrm_rm);
invoke_stub("", "", "=m" (dummy) : "a" (*ea.reg));
put_stub(stub);
@@ -5094,7 +5103,7 @@ x86_emulate(
generate_exception_if(vex.l || vex.reg != 0xf || ea.type != OP_REG,
X86_EXC_UD);
dst = ea;
- dst.reg = decode_gpr(&_regs, modrm_reg);
+ dst.reg = decode_gpr(&_regs, NULL, modrm_reg);
if ( vex.pfx == vex_f2 )
{
@@ -5219,7 +5228,7 @@ x86_emulate(
else
{
shift = src.val;
- src.reg = decode_gpr(&_regs, modrm_reg);
+ src.reg = decode_gpr(&_regs, &state->egpr, modrm_reg);
src.val = truncate_word(*src.reg, dst.bytes);
}
@@ -5321,7 +5330,7 @@ x86_emulate(
case X86EMUL_OPC(0x0f, 0xb6): /* movzx rm8,r{16,32,64} */
/* Recompute DstReg as we may have decoded AH/BH/CH/DH. */
- dst.reg = decode_gpr(&_regs, modrm_reg);
+ dst.reg = decode_gpr(&_regs, &state->egpr, modrm_reg);
dst.bytes = op_bytes;
dst.val = (uint8_t)src.val;
break;
@@ -5380,7 +5389,7 @@ x86_emulate(
case X86EMUL_OPC(0x0f, 0xbe): /* movsx rm8,r{16,32,64} */
/* Recompute DstReg as we may have decoded AH/BH/CH/DH. */
- dst.reg = decode_gpr(&_regs, modrm_reg);
+ dst.reg = decode_gpr(&_regs, &state->egpr, modrm_reg);
dst.bytes = op_bytes;
dst.val = (int8_t)src.val;
break;
@@ -5524,7 +5533,8 @@ x86_emulate(
case X86EMUL_OPC(0x0f, 0xc8) ... X86EMUL_OPC(0x0f, 0xcf): /* bswap */
dst.type = OP_REG;
- dst.reg = decode_gpr(&_regs, (b & 7) | ((rex_prefix & 1) << 3));
+ dst.reg = decode_gpr(&_regs, &state->egpr,
+ (b & 7) | ((rex_prefix & 1) << 3));
switch ( dst.bytes = op_bytes )
{
default: /* case 2: */
--- a/xen/arch/x86/x86_emulate/x86_emulate.h
+++ b/xen/arch/x86/x86_emulate/x86_emulate.h
@@ -634,12 +634,16 @@ int x86_emulate_wrapper(
/* Map GPRs by ModRM encoding to their offset within struct cpu_user_regs. */
extern const uint8_t cpu_user_regs_gpr_offsets[X86_NR_GPRS];
+struct egpr {
+};
+
/*
* Given the 'reg' portion of a ModRM byte, and a register block, return a
* pointer into the block that addresses the relevant register.
*/
static inline unsigned long *decode_gpr(struct cpu_user_regs *regs,
- unsigned int modrm)
+ struct egpr *egpr,
+ unsigned int reg)
{
/* Check that the array is a power of two. */
BUILD_BUG_ON(ARRAY_SIZE(cpu_user_regs_gpr_offsets) &
@@ -651,9 +655,9 @@ static inline unsigned long *decode_gpr(
* hence truncation is what we want (bits not ignored will get checked
* elsewhere).
*/
- modrm &= ARRAY_SIZE(cpu_user_regs_gpr_offsets) - 1;
+ reg &= ARRAY_SIZE(cpu_user_regs_gpr_offsets) - 1;
- return (void *)regs + cpu_user_regs_gpr_offsets[modrm];
+ return (void *)regs + cpu_user_regs_gpr_offsets[reg];
}
/* Unhandleable read, write or instruction fetch */
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 05/30] x86emul: replace earlier relaxation of decode_gpr()
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (3 preceding siblings ...)
2026-11-11 14:22 ` [PATCH 04/30] x86emul: add eGPR parameter to decode_gpr() Jan Beulich
@ 2026-11-11 14:22 ` Jan Beulich
2026-11-11 14:22 ` [PATCH 06/30] VMX: support APX Jan Beulich
` (24 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:22 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
Handle eGPR-s in a new out-of-line helper, while restoring some code to
the state before commit e8af03bf9a32 ("x86emul: prepare for AVX512F S/G
insns"). Some of the function invocations are "just in case"; to allow
these to continue to work without adding logic to tell whether for a
particular insn a particular operand is a GPR or another knd of
register, record register numbers in the new structure in all cases,
while fetching register values only when eGPR-s are actually enabled.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
---
The almost-pure C variant of the register latching would be something
like
switch ( reg )
{
#define CASE(regno, basereg) \
case regno: \
/* REX2 prefixes with W, R4 and possibly R3 set. */ \
asm ( ".byte 0xd5, 0x48 | %c1; mov %%" #basereg ", %0" \
: "+m" (egpr->val[i]) \
: "i" ((regno & 8) >> 1) )
CASE(16, eax); break;
CASE(17, ecx); break;
CASE(18, edx); break;
CASE(19, ebx); break;
CASE(20, esp); break;
CASE(21, ebp); break;
CASE(22, esi); break;
CASE(23, edi); break;
CASE(24, eax); break;
CASE(25, ecx); break;
CASE(26, edx); break;
CASE(27, ebx); break;
CASE(28, esp); break;
CASE(29, ebp); break;
CASE(30, esi); break;
CASE(31, edi); break;
#undef CASE
default:
ASSERT_UNREACHABLE();
break;
}
which, because of the way the compiler translates the switch(), ends up
larger without (imo) being that much easier to read.
--- a/tools/fuzz/x86_instruction_emulator/Makefile
+++ b/tools/fuzz/x86_instruction_emulator/Makefile
@@ -32,7 +32,7 @@ GCOV_FLAGS := --coverage
OBJS := fuzz-emul.o x86-emulate.o
OBJS += x86_emulate/0f01.o x86_emulate/0fae.o x86_emulate/0fc7.o
-OBJS += x86_emulate/decode.o x86_emulate/fpu.o
+OBJS += x86_emulate/decode.o x86_emulate/fpu.o x86_emulate/util.o
WRAPPED = $(shell sed -n 's,^ *WRAP(\([[:alnum:]_]*\));,\1,p' \
../../tests/x86_emulator/x86-emulate.h)
--- a/tools/tests/x86_emulator/x86-emulate.h
+++ b/tools/tests/x86_emulator/x86-emulate.h
@@ -143,6 +143,8 @@ static inline uint64_t xgetbv(uint32_t x
return ((uint64_t)hi << 32) | lo;
}
+#define get_xcr0() xgetbv(0)
+
/* Intentionally checking OSXSAVE here. */
#define cpu_has_xsave (cpu_policy.basic.raw[1].c & (1u << 27))
@@ -225,6 +227,8 @@ void wrpkru(unsigned int val);
xcr0_mask(0xe6))
#define cpu_has_avx10_2_aux (cpu_policy.avx10.avx10_v2_aux && \
xcr0_mask(0xe6))
+#define cpu_has_apx_f (cpu_policy.feat.apx_f && \
+ xcr0_mask(X86_XCR0_APX))
#define cpu_has_xgetbv1 (cpu_has_xsave && cpu_policy.xstate.xgetbv1)
--- a/xen/arch/x86/x86_emulate/util.c
+++ b/xen/arch/x86/x86_emulate/util.c
@@ -7,6 +7,74 @@
*/
#include "private.h"
+#ifdef __XEN__
+#include <asm/xstate.h>
+#endif
+
+unsigned long *decode_egpr(struct egpr *egpr, unsigned int reg)
+{
+ unsigned int i;
+
+ for ( i = 0; i < ARRAY_SIZE(egpr->regno); ++i )
+ {
+ unsigned int xcr0 = 0;
+
+ if ( egpr->regno[i] == reg )
+ break;
+
+ if ( egpr->regno[i] )
+ continue;
+
+ egpr->regno[i] = reg;
+
+ /*
+ * Obtain the active XCR0 here, for not having emulation context or an
+ * ops structure available, as this is a utility function used also
+ * from outside of the emulator.
+ */
+ if ( cpu_has_apx_f )
+ xcr0 = get_xcr0();
+
+ /* Fetch the register value if accessible. */
+ if ( xcr0 & X86_XCR0_APX )
+ {
+#ifdef __x86_64__
+ asm ( "sub $17, %[reg]\n\t"
+
+ /* mov %r16, (%rdi) */
+ ".byte 0xd5, 0x48; mov %%eax, (%%rdi)\n\t"
+
+ /* cfcmovz %r17, (%rdi) */
+ ".byte 0x62, 0xe4, 0xfc, 0x0c, 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"
+
+ /* cfcmovz %r\n, (%rdi) */
+ ".byte 0x62, 0x64 + ((~\\n & 8) << 4), 0xfc, 0x0c\n\t"
+ ".byte 0x44, 0x07 + ((\\n & 7) << 3)\n\t"
+
+ ".endr\n\t"
+
+ /* cfcmovg %r31, (%rdi) */
+ ".byte 0x62, 0x64, 0xfc, 0x0c, 0x4f, 0x3f"
+
+ : [reg] "+r" (reg), "=m" (egpr->val[i])
+ : "D" (&egpr->val[i]) );
+
+ egpr->orig[i] = egpr->val[i];
+#endif
+ }
+
+ break;
+ }
+
+ ASSERT(i < ARRAY_SIZE(egpr->val));
+
+ /* Note that this also acts as array_access_nospec() stand-in. */
+ return &egpr->val[i & (ARRAY_SIZE(egpr->val) - 1)];
+}
unsigned int x86_insn_length(const struct x86_emulate_state *s,
const struct x86_emulate_ctxt *ctxt)
--- a/xen/arch/x86/x86_emulate/x86_emulate.h
+++ b/xen/arch/x86/x86_emulate/x86_emulate.h
@@ -634,27 +634,36 @@ int x86_emulate_wrapper(
/* Map GPRs by ModRM encoding to their offset within struct cpu_user_regs. */
extern const uint8_t cpu_user_regs_gpr_offsets[X86_NR_GPRS];
+/*
+ * APX insn can reference up to 4 GPRs: Either up to three register operands
+ * or up to two register operands and one memory operand, the latter using
+ * potentially both base and index register.
+ */
struct egpr {
+ unsigned char regno[4];
+ unsigned long val[4], orig[4];
};
+unsigned long *decode_egpr(struct egpr *egpr, unsigned int reg);
+
/*
- * Given the 'reg' portion of a ModRM byte, and a register block, return a
- * pointer into the block that addresses the relevant register.
+ * Given the 'reg' portion of a ModRM byte, and a pair of register blocks,
+ * return a pointer into either block that addresses the relevant register.
*/
static inline unsigned long *decode_gpr(struct cpu_user_regs *regs,
struct egpr *egpr,
unsigned int reg)
{
+ if ( reg & 0x10 )
+ return decode_egpr(egpr, reg);
+
/* Check that the array is a power of two. */
BUILD_BUG_ON(ARRAY_SIZE(cpu_user_regs_gpr_offsets) &
(ARRAY_SIZE(cpu_user_regs_gpr_offsets) - 1));
- /*
- * Note that this also acts as array_access_nospec() stand-in. Higher
- * bits may legitimately come in set here, from EVEX decoding, and
- * hence truncation is what we want (bits not ignored will get checked
- * elsewhere).
- */
+ ASSERT(reg < ARRAY_SIZE(cpu_user_regs_gpr_offsets));
+
+ /* Note that this also acts as array_access_nospec() stand-in. */
reg &= ARRAY_SIZE(cpu_user_regs_gpr_offsets) - 1;
return (void *)regs + cpu_user_regs_gpr_offsets[reg];
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 06/30] VMX: support APX
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (4 preceding siblings ...)
2026-11-11 14:22 ` [PATCH 05/30] x86emul: replace earlier relaxation of decode_gpr() Jan Beulich
@ 2026-11-11 14:22 ` Jan Beulich
2026-11-11 14:23 ` [PATCH 07/30] x86emul: latch XCR0 early during decode Jan Beulich
` (23 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:22 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
While the CR-access exit qualification is extended in place (with the
new bit previously having been reserved/zero), a new Extended
Instruction Information VMCS field is being introduced.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
---
hvm_monitor_descriptor_access() would need passing the extended
instruction info, too, but that then further requires an interface
change to also get that additional piece of data out to the monitor.
While for the descriptor exit insn info the existing fields don't allow
easy extension, the USER_MSR one in principle would. Version 003 of the
specification, having gained URDMSR/UWRMSR as insns, says nothing either
way, so the extended structure is used as necessary for the time being.
Sadly whether it's reg1 or reg2 that ought to be used also doesn't
really become clear. For now I'm assuming that reg1 represents
ModR/M.rm while reg2 represents ModR/M.reg, deduced from instr-info
definitions for descriptor access insns plus that for VMREAD/VMWRITE.
--- a/xen/arch/x86/hvm/vmx/vmx.c
+++ b/xen/arch/x86/hvm/vmx/vmx.c
@@ -4204,7 +4204,18 @@ static int vmx_handle_apic_write(void)
static unsigned int msr_imm_gpr(void)
{
- msr_imm_instr_info_t info = { .raw = vmread(VMX_INSTRUCTION_INFO) };
+ msr_imm_instr_info_t info;
+
+ if ( cpu_has_apx_f )
+ {
+ extended_instr_info_t xinfo =
+ { .raw = vmread(EXTENDED_INSTRUCTION_INFO) };
+
+ ASSERT(xinfo.mem_reg);
+ return xinfo.reg1;
+ }
+
+ info.raw = vmread(VMX_INSTRUCTION_INFO);
return info.gpr;
}
--- a/xen/arch/x86/hvm/vmx/vvmx.c
+++ b/xen/arch/x86/hvm/vmx/vvmx.c
@@ -418,17 +418,34 @@ static int decode_vmx_inst(struct cpu_us
unsigned long *poperandS, unsigned int size)
{
struct vcpu *v = current;
- union vmx_inst_info info;
+ union extended_instr_info info;
struct segment_register seg;
unsigned long base, index, seg_base, disp, offset;
unsigned int scale;
- __vmread(VMX_INSTRUCTION_INFO, &offset);
- info.word = offset;
+ if ( cpu_has_apx_f )
+ info.raw = vmread(EXTENDED_INSTRUCTION_INFO);
+ else
+ {
+ union vmx_inst_info iinfo = { .word = vmread(VMX_INSTRUCTION_INFO) };
- if ( info.fields.memreg ) {
+ info.raw = 0;
+ info.scaling = iinfo.fields.scaling;
+ info.addr_size = iinfo.fields.addr_size;
+ info.mem_reg = iinfo.fields.memreg;
+ info.segment_reg = iinfo.fields.segment;
+ info.index_reg_invalid = iinfo.fields.index_reg_invalid;
+ info.base_reg_invalid = iinfo.fields.base_reg_invalid;
+ info.reg1 = iinfo.fields.reg1;
+ info.index_reg = iinfo.fields.index_reg;
+ info.base_reg = iinfo.fields.base_reg;
+ info.reg2 = iinfo.fields.reg2;
+ }
+
+ if ( info.mem_reg )
+ {
decode->type = VMX_INST_MEMREG_TYPE_REG;
- decode->reg1 = info.fields.reg1;
+ decode->reg1 = info.reg1;
if ( poperandS != NULL )
*poperandS = reg_read(regs, egpr, decode->reg1);
}
@@ -438,18 +455,17 @@ static int decode_vmx_inst(struct cpu_us
decode->type = VMX_INST_MEMREG_TYPE_MEMORY;
- if ( info.fields.segment > x86_seg_gs )
+ if ( info.segment_reg > x86_seg_gs )
goto gp_fault;
- hvm_get_segment_register(v, info.fields.segment, &seg);
+ hvm_get_segment_register(v, info.segment_reg, &seg);
seg_base = seg.base;
- base = info.fields.base_reg_invalid ? 0 :
- reg_read(regs, egpr, info.fields.base_reg);
+ base = info.base_reg_invalid ? 0 : reg_read(regs, egpr, info.base_reg);
- index = info.fields.index_reg_invalid ? 0 :
- reg_read(regs, egpr, info.fields.index_reg);
+ index = info.index_reg_invalid ? 0 :
+ reg_read(regs, egpr, info.index_reg);
- scale = 1 << info.fields.scaling;
+ scale = 1 << info.scaling;
__vmread(EXIT_QUALIFICATION, &disp);
@@ -457,14 +473,14 @@ static int decode_vmx_inst(struct cpu_us
size = 4 << mode_64bit;
offset = base + index * scale + disp;
- switch ( info.fields.addr_size )
+ switch ( info.addr_size )
{
case 0: offset = (uint16_t)offset; break;
case 1: offset = (uint32_t)offset; break;
default: break;
}
- base = !mode_64bit || info.fields.segment >= x86_seg_fs ?
+ base = !mode_64bit || info.segment_reg >= x86_seg_fs ?
seg_base + offset : offset;
if ( offset + size - 1 < offset ||
(mode_64bit ?
@@ -490,7 +506,7 @@ static int decode_vmx_inst(struct cpu_us
decode->len = size;
}
- decode->reg2 = info.fields.reg2;
+ decode->reg2 = info.reg2;
return X86EMUL_OKAY;
--- a/xen/arch/x86/include/asm/hvm/vmx/vmcs.h
+++ b/xen/arch/x86/include/asm/hvm/vmx/vmcs.h
@@ -505,6 +505,7 @@ enum vmcs_field {
SPEC_CTRL_MASK = 0x0000204a,
SPEC_CTRL_SHADOW = 0x0000204c,
GUEST_PHYSICAL_ADDRESS = 0x00002400,
+ EXTENDED_INSTRUCTION_INFO = 0x00002406,
VMCS_LINK_POINTER = 0x00002800,
GUEST_IA32_DEBUGCTL = 0x00002802,
GUEST_PAT = 0x00002804,
--- a/xen/arch/x86/include/asm/hvm/vmx/vmx.h
+++ b/xen/arch/x86/include/asm/hvm/vmx/vmx.h
@@ -245,8 +245,8 @@ typedef union cr_access_qual {
access_type:2, /* VMX_CR_ACCESS_TYPE_* */
lmsw_op_type:1, /* 0 => reg, 1 => mem */
:1,
- gpr:4,
- :4;
+ gpr:5,
+ :3;
uint16_t lmsw_data;
uint32_t :32;
};
@@ -598,4 +598,27 @@ typedef union msr_imm_instr_info {
};
} msr_imm_instr_info_t;
+/* VM-Exit extended instruction info for APX */
+typedef union extended_instr_info {
+ unsigned long raw;
+ struct {
+ unsigned int scaling :2, /* bits 0:1 - Scaling */
+ addr_size :2; /* bits 2:3 - Address size */
+ bool mem_reg :1; /* bit 4 - Mem/Reg */
+ unsigned int operand_size :2, /* bits 5:6 - Operand size */
+ segment_reg :3; /* bits 7:9 - Segment register */
+ bool index_reg_invalid:1, /* bit 10 - Index register invalid */
+ base_reg_invalid :1; /* bit 11 - Base register invalid */
+ unsigned int :4, /* bits 12:15 - Undefined */
+ reg1 :5, /* bits 16:20 - Reg1 */
+ :3, /* bits 21:23 - Undefined */
+ index_reg :5, /* bits 24:28 - Index register */
+ :3, /* bits 29:31 - Undefined */
+ base_reg :5, /* bits 32:36 - Base register */
+ :3, /* bits 37:39 - Undefined */
+ reg2 :5, /* bits 40:44 - Reg2 */
+ :19; /* bits 45:63 - Undefined */
+ };
+} extended_instr_info_t;
+
#endif /* __ASM_X86_HVM_VMX_VMX_H__ */
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 07/30] x86emul: latch XCR0 early during decode
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (5 preceding siblings ...)
2026-11-11 14:22 ` [PATCH 06/30] VMX: support APX Jan Beulich
@ 2026-11-11 14:23 ` Jan Beulich
2026-11-11 14:23 ` [PATCH 08/30] x86emul: support REX2-prefixed insns Jan Beulich
` (22 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:23 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
We'll need to consult it to decide whether to treat 0xD5 as a REX2
prefix, and whether to recognize extended EVEX encodings.
Utilize this in adjust_bnd() right away, but leave leveraging in
x86emul_get_fpu() for a separate change.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
---
While not directly relevant in this series, should we perhaps latch CR4
into the state structure as well right away, since we need to read it
here anyway?
--- a/xen/arch/x86/x86_emulate/decode.c
+++ b/xen/arch/x86/x86_emulate/decode.c
@@ -1053,6 +1053,24 @@ int x86emul_decode(struct x86_emulate_st
#endif
}
+ /* Latch XCR0, if available. */
+ if ( ops->read_cr && ops->read_xcr )
+ {
+ unsigned long cr4;
+
+ rc = ops->read_cr(4, &cr4, ctxt);
+ if ( rc == X86EMUL_OKAY && (cr4 & X86_CR4_OSXSAVE) &&
+ ops->read_xcr(0, &s->xcr0, ctxt) != X86EMUL_OKAY )
+ s->xcr0 = 0;
+
+ /*
+ * To ease consuming, strip 64-bit-only state right away for non-64-bit
+ * environments.
+ */
+ if ( !mode_64bit() )
+ s->xcr0 &= ~(X86_XCR0_TILE_CFG | X86_XCR0_TILE_DATA | X86_XCR0_APX);
+ }
+
/* Prefix bytes. */
for ( ; ; )
{
--- a/xen/arch/x86/x86_emulate/private.h
+++ b/xen/arch/x86/x86_emulate/private.h
@@ -336,6 +336,8 @@ struct x86_emulate_state {
unsigned long ip;
+ uint64_t xcr0;
+
struct egpr egpr;
struct stub_exn *stub_exn;
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -1240,17 +1240,17 @@ static bool is_branch_step(struct x86_em
return debugctl & IA32_DEBUGCTLMSR_BTF;
}
-static void adjust_bnd(struct x86_emulate_ctxt *ctxt,
+static void adjust_bnd(const struct x86_emulate_state *s,
+ struct x86_emulate_ctxt *ctxt,
const struct x86_emulate_ops *ops, enum vex_pfx pfx)
{
- uint64_t xcr0, bndcfg;
+ uint64_t bndcfg;
int rc;
if ( pfx == vex_f2 || !cpu_has_mpx || !vcpu_has_mpx() )
return;
- if ( !ops->read_xcr || ops->read_xcr(0, &xcr0, ctxt) != X86EMUL_OKAY ||
- !(xcr0 & X86_XCR0_BNDREGS) || !(xcr0 & X86_XCR0_BNDCSR) )
+ if ( !(s->xcr0 & X86_XCR0_BNDREGS) || !(s->xcr0 & X86_XCR0_BNDCSR) )
{
ASSERT(!ctxt->event_pending);
return;
@@ -1964,7 +1964,7 @@ x86_emulate(
case 0x70 ... 0x7f: /* jcc (short) */
if ( test_cc(b, _regs.eflags) )
jmp_rel((int32_t)src.val);
- adjust_bnd(ctxt, ops, vex.pfx);
+ adjust_bnd(state, ctxt, ops, vex.pfx);
break;
case 0x80: case 0x81: case 0x82: case 0x83: /* Grp1 */
@@ -2378,7 +2378,7 @@ x86_emulate(
(rc = ops->insn_fetch(dst.val, NULL, 0, ctxt)) )
goto done;
_regs.r(ip) = dst.val;
- adjust_bnd(ctxt, ops, vex.pfx);
+ adjust_bnd(state, ctxt, ops, vex.pfx);
break;
case 0xc4: /* les */
@@ -2609,7 +2609,7 @@ x86_emulate(
op_bytes = ((op_bytes == 4) && mode_64bit()) ? 8 : op_bytes;
src.val = _regs.r(ip);
jmp_rel(rel);
- adjust_bnd(ctxt, ops, vex.pfx);
+ adjust_bnd(state, ctxt, ops, vex.pfx);
goto push;
}
@@ -2617,7 +2617,7 @@ x86_emulate(
case 0xeb: /* jmp (short) */
jmp_rel((int32_t)src.val);
if ( !(b & 2) )
- adjust_bnd(ctxt, ops, vex.pfx);
+ adjust_bnd(state, ctxt, ops, vex.pfx);
break;
case 0xea: /* jmp (far, absolute) */
@@ -2900,14 +2900,14 @@ x86_emulate(
goto done;
_regs.r(ip) = src.val;
src.val = dst.val;
- adjust_bnd(ctxt, ops, vex.pfx);
+ adjust_bnd(state, ctxt, ops, vex.pfx);
goto push;
case 4: /* jmp (near) */
if ( (rc = ops->insn_fetch(src.val, NULL, 0, ctxt)) )
goto done;
_regs.r(ip) = src.val;
dst.type = OP_NONE;
- adjust_bnd(ctxt, ops, vex.pfx);
+ adjust_bnd(state, ctxt, ops, vex.pfx);
break;
case 3: /* call (far, absolute indirect) */
case 5: /* jmp (far, absolute indirect) */
@@ -5013,7 +5013,7 @@ x86_emulate(
case X86EMUL_OPC(0x0f, 0x80) ... X86EMUL_OPC(0x0f, 0x8f): /* jcc (near) */
if ( test_cc(b, _regs.eflags) )
jmp_rel((int32_t)src.val);
- adjust_bnd(ctxt, ops, vex.pfx);
+ adjust_bnd(state, ctxt, ops, vex.pfx);
break;
case X86EMUL_OPC(0x0f, 0x90) ... X86EMUL_OPC(0x0f, 0x9f): /* setcc */
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 08/30] x86emul: support REX2-prefixed insns
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (6 preceding siblings ...)
2026-11-11 14:23 ` [PATCH 07/30] x86emul: latch XCR0 early during decode Jan Beulich
@ 2026-11-11 14:23 ` Jan Beulich
2026-11-11 14:24 ` [PATCH 09/30] x86emul: APX_F REX2 testing Jan Beulich
` (21 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:23 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
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@suse.com>
---
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)
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 09/30] x86emul: APX_F REX2 testing
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (7 preceding siblings ...)
2026-11-11 14:23 ` [PATCH 08/30] x86emul: support REX2-prefixed insns Jan Beulich
@ 2026-11-11 14:24 ` Jan Beulich
2026-11-11 14:24 ` [PATCH 10/30] x86emul: support JMPABS Jan Beulich
` (20 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:24 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
Re-use the blowfish code for this, by replacing uses of %r8, %r9, and
%r10-%r15 with %r18, %r19, and %r20-%r25 respectively.
At the same time consume fuzzing input to prefill the eGPR-s when
accessible.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
---
SDE: -future (9.38.0 or later)
---
load_egpr() doesn't strictly need the __builtin_constant_p() dependency.
I coded it up to make sure previous open-coding of the logic would be
replaced by binary-identical code. If we want to keep it, check_egpr()
may want to gain an equivalent alternative.
--- a/tools/fuzz/x86_instruction_emulator/fuzz-emul.c
+++ b/tools/fuzz/x86_instruction_emulator/fuzz-emul.c
@@ -1031,6 +1031,56 @@ int LLVMFuzzerTestOneInput(const uint8_t
disable_hooks(&ctxt);
+ if ( cpu_has_apx_f )
+ {
+ uint64_t egpr[16];
+
+ /* Pre-seed the eGPR-s if enough input data is available. */
+ if ( input_avail(&state, sizeof(egpr)) )
+ {
+ input_read(&state, egpr, sizeof(egpr));
+
+ /*
+ * These are "mov %0, %%r<N>" encoded with hand-crafted REX2
+ * prefixes. The arrangement is such that no REX prefix would
+ * be emitted, as that would conflict with the REX2 we want.
+ */
+ asm volatile ( ".byte 0xd5, 0x48; mov (%0), %%eax"
+ :: "R" (egpr), "m" (egpr[0]) );
+ asm volatile ( ".byte 0xd5, 0x48; mov 8(%0), %%ecx"
+ :: "R" (egpr), "m" (egpr[1]) );
+ asm volatile ( ".byte 0xd5, 0x48; mov 0x2*8(%0), %%edx"
+ :: "R" (egpr), "m" (egpr[2]) );
+ asm volatile ( ".byte 0xd5, 0x48; mov 0x3*8(%0), %%ebx"
+ :: "R" (egpr), "m" (egpr[3]) );
+ asm volatile ( ".byte 0xd5, 0x48; mov 0x4*8(%0), %%esp"
+ :: "R" (egpr), "m" (egpr[4]) );
+ asm volatile ( ".byte 0xd5, 0x48; mov 0x5*8(%0), %%ebp"
+ :: "R" (egpr), "m" (egpr[5]) );
+ asm volatile ( ".byte 0xd5, 0x48; mov 0x6*8(%0), %%esi"
+ :: "R" (egpr), "m" (egpr[6]) );
+ asm volatile ( ".byte 0xd5, 0x48; mov 0x7*8(%0), %%edi"
+ :: "R" (egpr), "m" (egpr[7]) );
+
+ asm volatile ( ".byte 0xd5, 0x4c; mov 0x8*8(%0), %%eax"
+ :: "R" (egpr), "m" (egpr[8]) );
+ asm volatile ( ".byte 0xd5, 0x4c; mov 0x9*8(%0), %%ecx"
+ :: "R" (egpr), "m" (egpr[9]) );
+ asm volatile ( ".byte 0xd5, 0x4c; mov 0xa*8(%0), %%edx"
+ :: "R" (egpr), "m" (egpr[10]) );
+ asm volatile ( ".byte 0xd5, 0x4c; mov 0xb*8(%0), %%ebx"
+ :: "R" (egpr), "m" (egpr[11]) );
+ asm volatile ( ".byte 0xd5, 0x4c; mov 0xc*8(%0), %%esp"
+ :: "R" (egpr), "m" (egpr[12]) );
+ asm volatile ( ".byte 0xd5, 0x4c; mov 0xd*8(%0), %%ebp"
+ :: "R" (egpr), "m" (egpr[13]) );
+ asm volatile ( ".byte 0xd5, 0x4c; mov 0xe*8(%0), %%esi"
+ :: "R" (egpr), "m" (egpr[14]) );
+ asm volatile ( ".byte 0xd5, 0x4c; mov 0xf*8(%0), %%edi"
+ :: "R" (egpr), "m" (egpr[15]) );
+ }
+ }
+
do {
/* FIXME: Until we actually implement SIGFPE handling properly */
setup_fpu_exception_handler();
--- a/tools/tests/x86_emulator/Makefile
+++ b/tools/tests/x86_emulator/Makefile
@@ -40,6 +40,7 @@ else
blowfish-cflags := ""
blowfish-cflags-x86_32 := "-mno-accumulate-outgoing-args -Dstatic="
+blowfish-cflags-x86_64 := "-DREX2 -Dstatic="
3dnow-vecs := 8
3dnow-ints :=
@@ -265,7 +266,7 @@ $(TARGET): $(OBJS)
.PHONY: clean
clean:
- rm -rf $(TARGET) *.o *~ core *.bin x86_emulate
+ rm -rf $(TARGET) *.o *~ core *.bin blowfish.s x86_emulate
rm -rf $(addsuffix .h,$(TESTCASES)) $(addsuffix -opmask.h,$(OPMASK))
rm -rf $(addsuffix .c,$(filter-out blowfish,$(TESTCASES)))
--- a/tools/tests/x86_emulator/test_x86_emulator.c
+++ b/tools/tests/x86_emulator/test_x86_emulator.c
@@ -57,6 +57,80 @@ static bool blowfish_check_regs(const st
return regs->eax == 2 && regs->edx == 1;
}
+#ifdef __x86_64__
+# ifdef __APX_F__
+# error "Needs re-work with APX_F available to the compiler"
+# endif
+# define load_egpr(reg, val) do { \
+ BUILD_BUG_ON(!((reg) & 0x10)); \
+ if ( !__builtin_constant_p(val) ) \
+ /* mov %rax, %r\reg */ \
+ asm volatile ( ".byte 0xd5, 0x18 | %c[rex_b]\n\t" \
+ ".byte 0x89, 0xc0 | %c[rm]" \
+ :: [rm] "i" ((reg) & 7), \
+ [rex_b] "i" ((bool)((reg) & 8)), \
+ "a" ((val) + 0UL) ); \
+ else if ( (val) <= 0xffffffffUL ) \
+ /* mov $ui32, %r\reg */ \
+ asm volatile ( ".byte 0xd5, 0x10 | %c[rex_b]\n\t" \
+ ".byte 0xb8 | %c[r]\n\t" \
+ ".long %P[v]" \
+ :: [r] "i" ((reg) & 7), \
+ [rex_b] "i" ((bool)((reg) & 8)), \
+ [v] "i" ((val) + 0UL) ); \
+ else \
+ /* mov $ui64, %r\reg */ \
+ asm volatile ( ".byte 0xd5, 0x18 | %c[rex_b]\n\t" \
+ ".byte 0xb8 | %c[r]\n\t" \
+ ".quad %P[v]" \
+ :: [r] "i" ((reg) & 7), \
+ [rex_b] "i" ((bool)((reg) & 8)), \
+ [v] "i" ((val) + 0UL) ); \
+} while ( false )
+
+# define check_egpr(reg, val) ({ \
+ bool zf; \
+ BUILD_BUG_ON(!((reg) & 0x10)); \
+ /* cmp %rax, %r\reg */ \
+ asm ( ".byte 0xd5, 0x18 | %c[rex_b]\n\t" \
+ ".byte 0x39, 0xc0 | %c[rm]\n\t" \
+ "sete %[res]" \
+ : [res] "=qm" (zf) \
+ : [rm] "i" ((reg) & 7), \
+ [rex_b] "i" ((bool)((reg) & 8)), \
+ "a" ((val) + 0UL) ); \
+ zf; \
+})
+
+static void blowfish_rex2_set_regs(struct cpu_user_regs *regs)
+{
+ /*
+ * %r12...%r15 are preserved as per the ABI. The REX2 test blob has them
+ * replaced by %r22...%r25, which therefore take the same (preserved) role.
+ * Seed them with recognizable values, for blowfish_rex2_check_regs() to
+ * check.
+ */
+ load_egpr(22, 0x16000016);
+ load_egpr(23, 0x17000017);
+ load_egpr(24, 0x18000018);
+ load_egpr(25, 0x19000019);
+}
+
+static bool blowfish_rex2_check_regs(const struct cpu_user_regs *regs)
+{
+ return blowfish_check_regs(regs) &&
+ check_egpr(22, 0x16000016) &&
+ check_egpr(23, 0x17000017) &&
+ check_egpr(24, 0x18000018) &&
+ check_egpr(25, 0x19000019);
+}
+
+static bool check_apx_f(void)
+{
+ return cpu_has_apx_f;
+}
+#endif /* __x86_64__ */
+
static bool simd_check__3dnow(void)
{
return cpu_has_3dnow_ext && cpu_has_sse;
@@ -303,6 +377,13 @@ static const struct {
.check_regs = blowfish_check_regs }
#ifdef __x86_64__
BLOWFISH(64, blowfish, ),
+ { .code = blowfish_x86_64_DREX2,
+ .size = sizeof(blowfish_x86_64_DREX2),
+ .bitness = 64,
+ .name = "blowfish (REX2)",
+ .check_cpu = check_apx_f,
+ .set_regs = blowfish_rex2_set_regs,
+ .check_regs = blowfish_rex2_check_regs },
#endif
BLOWFISH(32, blowfish, ),
BLOWFISH(32, blowfish (push), _mno_accumulate_outgoing_args),
--- a/tools/tests/x86_emulator/testcase.mk
+++ b/tools/tests/x86_emulator/testcase.mk
@@ -23,13 +23,29 @@ endif
endif
+# For now don't assume an APX_F-capable compiler. Hand-craft eGPR uses instead.
+# In an attempt to have the compiler make more use of %r8 ... %r15, prevent it
+# from using %rbx and %rbp. We can't also disallow %rax, %rcx, %rdx, %rsi, and
+# %rdi, as those are the hard-wired operands of certain insns. Since the blob
+# is self-contained, we can then exchange %r8 ... %r15 to become %r18 ... %r25.
+CFLAGS-REX2 := -fomit-frame-pointer -ffixed-rbx -ffixed-rbp
+SED-REX2 := -e 's,%r1,%r2,g' -e 's,%r8,%r18,g' -e 's,%r9,%r19,g'
+
LDFLAGS_DIRECT += $(shell { $(LD) -v --warn-rwx-segments; } >/dev/null 2>&1 && echo --no-warn-rwx-segments)
.PHONY: all
all: $(TESTCASE).bin
%.bin: %.c
+ifeq ($(filter -DREX2,$($(TESTCASE)-cflags)),)
$(CC) $(filter-out -M% .%,$(CFLAGS)) -c $<
+else
+ rm -f $*.o
+ $(CC) $(filter-out -M% .%,$(CFLAGS)) $(CFLAGS-REX2) -S $< -o $*.s
+ sed $(SED-REX2) -i $*.s
+ $(AS) -o $*.o $*.s
+ rm -f $*.s
+endif
$(LD) $(LDFLAGS_DIRECT) -N -Ttext 0x100000 -o $*.tmp $*.o
$(OBJCOPY) -O binary -R .note.gnu.property $*.tmp $@
rm -f $*.tmp
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 10/30] x86emul: support JMPABS
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (8 preceding siblings ...)
2026-11-11 14:24 ` [PATCH 09/30] x86emul: APX_F REX2 testing Jan Beulich
@ 2026-11-11 14:24 ` Jan Beulich
2026-11-11 14:25 ` [PATCH 11/30] x86emul: switch to EVEX.R{3,4} nomenclature Jan Beulich
` (19 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:24 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
Signed-off-by: Jan Beulich <jbeulich@suse.com>
--- a/tools/tests/x86_emulator/test_x86_emulator.c
+++ b/tools/tests/x86_emulator/test_x86_emulator.c
@@ -1979,6 +1979,22 @@ int main(int argc, char **argv)
cpu_policy.feat.movrs = movrs;
}
+
+ printf("%-40s", "Testing jmpabs $0x123456789abc...");
+ if ( cpu_has_apx_f )
+ {
+ instr[0] = 0xd5; instr[1] = 0x00; instr[2] = 0xa1;
+ instr[3] = 0xbc; instr[4] = 0x9a; instr[5] = 0x78; instr[6] = 0x56;
+ instr[7] = 0x34; instr[8] = 0x12; instr[9] = 0; instr[10] = 0;
+ regs.rip = (unsigned long)&instr[0];
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rip != 0x123456789abcUL) )
+ goto fail;
+ printf("okay\n");
+ }
+ else
+ printf("skipped\n");
#endif /* x86-64 */
printf("%-40s", "Testing shld $1,%ecx,(%edx)...");
--- a/xen/arch/x86/x86_emulate/decode.c
+++ b/xen/arch/x86/x86_emulate/decode.c
@@ -689,6 +689,13 @@ decode_onebyte(struct x86_emulate_state
s->ea.mem.off = insn_fetch_bytes(s->ad_bytes);
break;
+ case X86EMUL_OPC_REX2 | 0xa1: /* jmpabs */
+ if ( s->ad_bytes == 8 ) /* Fetch more bytes to obtain imm64. */
+ s->imm1 = ((uint32_t)s->imm1 |
+ ((uint64_t)insn_fetch_type(uint32_t) << 32));
+ ctxt->opcode |= MASK_INSR(s->vex.pfx, X86EMUL_OPC_PFX_MASK);
+ break;
+
case 0xb8 ... 0xbf: /* mov imm{16,32,64},r{16,32,64} */
if ( s->op_bytes == 8 ) /* Fetch more bytes to obtain imm64. */
s->imm1 = ((uint32_t)s->imm1 |
@@ -1948,6 +1955,16 @@ int x86emul_decode(struct x86_emulate_st
{
s->modrm_mod = 0xff;
s->modrm_reg = s->modrm_rm = s->modrm = 0;
+
+ switch ( opcode )
+ {
+ case X86EMUL_OPC_REX2 | 0xa1:
+ d = DstImplicit | SrcImm;
+ break;
+
+ default:
+ break;
+ }
}
if ( override_seg != x86_seg_none )
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -2237,6 +2237,13 @@ x86_emulate(
_regs.ah = (_regs.eflags & EFLAGS_MASK) | X86_EFLAGS_MBS;
break;
+ case X86EMUL_OPC_REX2 | 0xa1: /* jmpabs */
+ generate_exception_if(ad_bytes != 8 || op_bytes != 4, X86_EXC_UD);
+ generate_exception_if(!is_canonical_address(imm1), X86_EXC_GP, 0);
+ _regs.r(ip) = imm1;
+ singlestep = _regs.eflags & X86_EFLAGS_TF;
+ break;
+
case 0xa4 ... 0xa5: /* movs */ {
unsigned long nr_reps = get_rep_prefix(true, true);
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 11/30] x86emul: switch to EVEX.R{3,4} nomenclature
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (9 preceding siblings ...)
2026-11-11 14:24 ` [PATCH 10/30] x86emul: support JMPABS Jan Beulich
@ 2026-11-11 14:25 ` Jan Beulich
2026-11-11 14:25 ` [PATCH 12/30] x86emul: switch to EVEX.B{3,4} nomenclature Jan Beulich
` (18 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:25 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
The APX spec renames the R and R' fields, and it can be expected for the
SDM to follow once APX stuff is folded in. The new naming is also better
to reflect in our structure field identifiers.
Like we represent VEX.R and EVEX.R3 in rex_prefix, also represent
EVEX.R4 in rex2_prefix, simplifying the modrm_reg calculation.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
--- a/tools/tests/x86_emulator/evex-disp8.c
+++ b/tools/tests/x86_emulator/evex-disp8.c
@@ -939,10 +939,10 @@ static void test_one(const struct test *
struct {
uint8_t opcx:3;
uint8_t mbz:1;
- uint8_t R:1;
+ uint8_t r4:1;
uint8_t b:1;
uint8_t x:1;
- uint8_t r:1;
+ uint8_t r3:1;
uint8_t pfx:2;
uint8_t mbs:1;
uint8_t reg:4;
@@ -955,7 +955,7 @@ static void test_one(const struct test *
};
} evex = {
.opcx = test->spc, .pfx = test->pfx, .lr = vl,
- .R = 1, .b = 1, .x = 1, .r = 1, .mbs = 1,
+ .r4 = 1, .b = 1, .x = 1, .r3 = 1, .mbs = 1,
.reg = 0xf, .RX = 1, .opmsk = sg,
};
--- a/xen/arch/x86/x86_emulate/decode.c
+++ b/xen/arch/x86/x86_emulate/decode.c
@@ -1280,7 +1280,12 @@ int x86emul_decode(struct x86_emulate_st
generate_exception_if(!s->evex.opmsk && s->evex.z, X86_EXC_UD);
if ( !mode_64bit() )
- s->evex.R = 1;
+ s->evex.r4 = 1;
+ else if ( s->xcr0 & X86_XCR0_APX )
+ {
+ if ( !s->evex.r4 )
+ s->rex2_prefix |= REX_R;
+ }
s->vex.opcx = s->evex.opcx;
break;
@@ -1394,8 +1399,7 @@ 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
- : !!(s->rex2_prefix & REX_R)) << 4);
+ (!!(s->rex2_prefix & REX_R) << 4);
s->modrm_rm = s->modrm & 0x07;
/*
--- a/xen/arch/x86/x86_emulate/private.h
+++ b/xen/arch/x86/x86_emulate/private.h
@@ -236,10 +236,10 @@ union evex {
struct { /* SDM names */
uint8_t opcx:3; /* mmm */
uint8_t mbz:1;
- uint8_t R:1; /* R' */
+ uint8_t r4:1; /* R4 (aka R') */
uint8_t b:1; /* B */
uint8_t x:1; /* X */
- uint8_t r:1; /* R */
+ uint8_t r3:1; /* R3 (aka R) */
uint8_t pfx:2; /* pp */
uint8_t mbs:1;
uint8_t reg:4; /* vvvv */
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -3774,7 +3774,8 @@ x86_emulate(
/* Convert GPR destination to %rAX and memory operand to (%rCX). */
rex_prefix &= ~REX_R;
vex.r = 1;
- evex.r = 1;
+ evex.r3 = 1;
+ evex.r4 = 1;
if ( ea.type == OP_MEM )
{
rex_prefix &= ~REX_B;
@@ -3825,7 +3826,8 @@ x86_emulate(
CASE_SIMD_SCALAR_FP(_EVEX, 0x0f, 0x2d): /* vcvts{s,d}2si xmm/mem,reg */
CASE_SIMD_SCALAR_FP(_EVEX, 0x0f, 0x78): /* vcvtts{s,d}2usi xmm/mem,reg */
CASE_SIMD_SCALAR_FP(_EVEX, 0x0f, 0x79): /* vcvts{s,d}2usi xmm/mem,reg */
- generate_exception_if((evex.reg != 0xf || !evex.RX || !evex.R ||
+ generate_exception_if((evex.reg != 0xf || !evex.RX ||
+ (!(state->xcr0 & X86_XCR0_APX) && !evex.r4) ||
evex.opmsk ||
(ea.type != OP_REG && evex.brs)),
X86_EXC_UD);
@@ -4465,7 +4467,7 @@ x86_emulate(
case X86EMUL_OPC_EVEX_66(0x0f38, 0x27): /* vptestm{d,q} [xyz]mm/mem,[xyz]mm,k{k} */
case X86EMUL_OPC_EVEX_66(0x0f38, 0x29): /* vpcmpeqq [xyz]mm/mem,[xyz]mm,k{k} */
case X86EMUL_OPC_EVEX_66(0x0f38, 0x37): /* vpcmpgtq [xyz]mm/mem,[xyz]mm,k{k} */
- generate_exception_if(!evex.r || !evex.R || evex.z, X86_EXC_UD);
+ generate_exception_if(!evex.r3 || !evex.r4 || evex.z, X86_EXC_UD);
if ( b & (ext == ext_0f38 ? 1 : 2) )
{
generate_exception_if(b != 0x27 && evex.w != (b & 1), X86_EXC_UD);
@@ -5447,7 +5449,7 @@ x86_emulate(
generate_exception_if((evex.w != (evex.pfx & VEX_PREFIX_DOUBLE_MASK) ||
(ea.type != OP_REG && evex.brs &&
(evex.pfx & VEX_PREFIX_SCALAR_MASK)) ||
- !evex.r || !evex.R || evex.z),
+ !evex.r3 || !evex.r4 || evex.z),
X86_EXC_UD);
visa_check(f);
if ( ea.type != OP_REG || !evex.brs )
@@ -6003,7 +6005,7 @@ x86_emulate(
case X86EMUL_OPC_EVEX_F3(0x0f38, 0x29): /* vpmov{b,w}2m [xyz]mm,k */
case X86EMUL_OPC_EVEX_F3(0x0f38, 0x39): /* vpmov{d,q}2m [xyz]mm,k */
- generate_exception_if(!evex.r || !evex.R, X86_EXC_UD);
+ generate_exception_if(!evex.r3 || !evex.r4, X86_EXC_UD);
/* fall through */
case X86EMUL_OPC_EVEX_F3(0x0f38, 0x28): /* vpmovm2{b,w} k,[xyz]mm */
case X86EMUL_OPC_EVEX_F3(0x0f38, 0x38): /* vpmovm2{d,q} k,[xyz]mm */
@@ -6268,7 +6270,7 @@ x86_emulate(
goto avx512f_no_sae;
case X86EMUL_OPC_EVEX_66(0x0f38, 0x8f): /* vpshufbitqmb [xyz]mm/mem,[xyz]mm,k{k} */
- generate_exception_if(evex.w || !evex.r || !evex.R || evex.z, X86_EXC_UD);
+ generate_exception_if(evex.w || !evex.r3 || !evex.r4 || evex.z, X86_EXC_UD);
/* fall through */
case X86EMUL_OPC_EVEX_66(0x0f38, 0x54): /* vpopcnt{b,w} [xyz]mm/mem,[xyz]mm{k} */
visa_check(_bitalg);
@@ -6345,7 +6347,7 @@ x86_emulate(
case X86EMUL_OPC_EVEX_F2(0x0f38, 0x68): /* vp2intersect{d,q} [xyz]mm/mem,[xyz]mm,k+1 */
vcpu_must_have(avx512_vp2intersect);
- generate_exception_if(evex.opmsk || !evex.r || !evex.R, X86_EXC_UD);
+ generate_exception_if(evex.opmsk || !evex.r3 || !evex.r4, X86_EXC_UD);
op_bytes = 16 << evex.lr;
goto avx512f_no_sae;
@@ -6655,8 +6657,8 @@ x86_emulate(
pevex->pfx = vex_f3; /* vmovdqu{32,64} */
pevex->w = b & 1;
/* Switch to sib_index as source. */
- pevex->r = !mode_64bit() || !(state->sib_index & 0x08);
- pevex->R = !mode_64bit() || !(state->sib_index & 0x10);
+ pevex->r3 = !mode_64bit() || !(state->sib_index & 0x08);
+ pevex->r4 = !mode_64bit() || !(state->sib_index & 0x10);
opc[1] = (state->sib_index & 7) << 3;
invoke_stub("", "", "=m" (index) : "a" (&index));
@@ -6810,8 +6812,8 @@ x86_emulate(
pevex->pfx = vex_f3; /* vmovdqu{32,64} */
pevex->w = b & 1;
/* Switch to sib_index as source. */
- pevex->r = !mode_64bit() || !(state->sib_index & 0x08);
- pevex->R = !mode_64bit() || !(state->sib_index & 0x10);
+ pevex->r3 = !mode_64bit() || !(state->sib_index & 0x08);
+ pevex->r4 = !mode_64bit() || !(state->sib_index & 0x10);
opc[1] = (state->sib_index & 7) << 3;
invoke_stub("", "", "=m" (index) : "a" (&index));
@@ -7403,7 +7405,7 @@ x86_emulate(
case X86EMUL_OPC_EVEX_F2(0x0f3a, 0x66): /* vfpclassbf16 $imm8,[xyz]mm/mem,k{k} */
case X86EMUL_OPC_EVEX_F2(0x0f3a, 0xc2): /* vcmpbf16 $imm8,[xyz]mm/mem,[xyz]mm,k{k} */
- generate_exception_if(!evex.r || !evex.R || evex.z, X86_EXC_UD);
+ generate_exception_if(!evex.r3 || !evex.r4 || evex.z, X86_EXC_UD);
fallthrough;
case X86EMUL_OPC_EVEX_F2(0x0f3a, 0x08): /* vrndscalebf16 $imm8,[xyz]mm/mem,[xyz]mm{k} */
case X86EMUL_OPC_EVEX_F2(0x0f3a, 0x26): /* vgetmantbf16 $imm8,[xyz]mm/mem,[xyz]mm{k} */
@@ -7507,13 +7509,15 @@ x86_emulate(
goto pextr;
case X86EMUL_OPC_EVEX_66(0x0f, 0xc5): /* vpextrw $imm8,xmm,reg */
- generate_exception_if(ea.type != OP_REG || !evex.R, X86_EXC_UD);
+ generate_exception_if(ea.type != OP_REG ||
+ (!(state->xcr0 & X86_XCR0_APX) && !evex.r4),
+ X86_EXC_UD);
/* Convert to alternative encoding: We want to use a memory operand. */
evex.opcx = ext_0f3a;
b = 0x15;
modrm <<= 3;
- evex.r = evex.b;
- evex.R = evex.x;
+ evex.r3 = evex.b;
+ evex.r4 = evex.x;
/* fall through */
case X86EMUL_OPC_EVEX_66(0x0f3a, 0x14): /* vpextrb $imm8,xmm,r/m */
case X86EMUL_OPC_EVEX_66(0x0f3a, 0x15): /* vpextrw $imm8,xmm,r/m */
@@ -7665,7 +7669,7 @@ x86_emulate(
case X86EMUL_OPC_EVEX_66(0x0f3a, 0x1f): /* vpcmp{d,q} $imm8,[xyz]mm/mem,[xyz]mm,k{k} */
case X86EMUL_OPC_EVEX_66(0x0f3a, 0x3e): /* vpcmpu{b,w} $imm8,[xyz]mm/mem,[xyz]mm,k{k} */
case X86EMUL_OPC_EVEX_66(0x0f3a, 0x3f): /* vpcmp{b,w} $imm8,[xyz]mm/mem,[xyz]mm,k{k} */
- generate_exception_if(!evex.r || !evex.R || evex.z, X86_EXC_UD);
+ generate_exception_if(!evex.r3 || !evex.r4 || evex.z, X86_EXC_UD);
if ( !(b & 0x20) )
goto avx512f_imm8_no_sae;
avx512bw_imm:
@@ -7942,7 +7946,7 @@ x86_emulate(
case X86EMUL_OPC_EVEX_66(0x0f3a, 0x66): /* vfpclassp{s,d} $imm8,[xyz]mm/mem,k{k} */
case X86EMUL_OPC_EVEX_66(0x0f3a, 0x67): /* vfpclasss{s,d} $imm8,xmm/mem,k{k} */
visa_check(dq);
- generate_exception_if(!evex.r || !evex.R || evex.z, X86_EXC_UD);
+ generate_exception_if(!evex.r3 || !evex.r4 || evex.z, X86_EXC_UD);
if ( !(b & 1) )
goto avx512f_imm8_no_sae;
generate_exception_if(evex.brs, X86_EXC_UD);
@@ -7952,7 +7956,7 @@ x86_emulate(
case X86EMUL_OPC_EVEX(0x0f3a, 0x66): /* vfpclassph $imm8,[xyz]mm/mem,k{k} */
case X86EMUL_OPC_EVEX(0x0f3a, 0x67): /* vfpclasssh $imm8,xmm/mem,k{k} */
visa_check(_fp16);
- generate_exception_if(evex.w || !evex.r || !evex.R || evex.z, X86_EXC_UD);
+ generate_exception_if(evex.w || !evex.r3 || !evex.r4 || evex.z, X86_EXC_UD);
if ( !(b & 1) )
goto avx512f_imm8_no_sae;
generate_exception_if(evex.brs, X86_EXC_UD);
@@ -7974,7 +7978,7 @@ x86_emulate(
/* fall through */
case X86EMUL_OPC_EVEX(0x0f3a, 0xc2): /* vcmpph $imm8,[xyz]mm/mem,[xyz]mm,k{k} */
visa_check(_fp16);
- generate_exception_if(evex.w || !evex.r || !evex.R || evex.z, X86_EXC_UD);
+ generate_exception_if(evex.w || !evex.r3 || !evex.r4 || evex.z, X86_EXC_UD);
if ( ea.type != OP_REG || !evex.brs )
avx512_vlen_check(evex.pfx & VEX_PREFIX_SCALAR_MASK);
goto simd_imm8_zmm;
@@ -8206,7 +8210,8 @@ x86_emulate(
CASE_SIMD_SCALAR_FP(_EVEX, 5, 0x6c): /* vcvtts{s,d}2usis xmm/mem,reg */
CASE_SIMD_SCALAR_FP(_EVEX, 5, 0x6d): /* vcvtts{s,d}2sis xmm/mem,reg */
- generate_exception_if((evex.reg != 0xf || !evex.RX || !evex.R ||
+ generate_exception_if((evex.reg != 0xf || !evex.RX ||
+ (!(state->xcr0 & X86_XCR0_APX) && !evex.r4) ||
evex.opmsk),
X86_EXC_UD);
vcpu_must_have(avx10, 2);
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 12/30] x86emul: switch to EVEX.B{3,4} nomenclature
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (10 preceding siblings ...)
2026-11-11 14:25 ` [PATCH 11/30] x86emul: switch to EVEX.R{3,4} nomenclature Jan Beulich
@ 2026-11-11 14:25 ` Jan Beulich
2026-11-11 14:25 ` [PATCH 13/30] x86emul: switch to EVEX.X3 nomenclature Jan Beulich
` (17 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:25 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
The APX spec renames the B field and adds a new one for the 5th base
address register bit; it can be expected for the SDM to follow once APX
stuff is folded in. The new naming is also better to reflect in our
structure field identifiers.
Like we represent VEX.B and EVEX.B3 in rex_prefix, also represent
EVEX.B4 in rex2_prefix, allowing to leave various decode_reg()
invocations untouched.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
---
Raising of #UD on an unduly set EVEX.B4 follows what the spec says: It's
ignored. While that matches EVEX.B3 (and VEX.B) treatment, what the spec
says is apparently wrong for EVEX.R{3,4}, which aren't ignored as per
what the SDM specifies. Hence the overall situation isn't really clear.
--- a/tools/tests/x86_emulator/evex-disp8.c
+++ b/tools/tests/x86_emulator/evex-disp8.c
@@ -938,9 +938,9 @@ static void test_one(const struct test *
uint8_t raw[3];
struct {
uint8_t opcx:3;
- uint8_t mbz:1;
+ uint8_t b4:1;
uint8_t r4:1;
- uint8_t b:1;
+ uint8_t b3:1;
uint8_t x:1;
uint8_t r3:1;
uint8_t pfx:2;
@@ -955,7 +955,7 @@ static void test_one(const struct test *
};
} evex = {
.opcx = test->spc, .pfx = test->pfx, .lr = vl,
- .r4 = 1, .b = 1, .x = 1, .r3 = 1, .mbs = 1,
+ .r4 = 1, .b3 = 1, .x = 1, .r3 = 1, .mbs = 1,
.reg = 0xf, .RX = 1, .opmsk = sg,
};
--- a/xen/arch/x86/x86_emulate/decode.c
+++ b/xen/arch/x86/x86_emulate/decode.c
@@ -1276,15 +1276,23 @@ int x86emul_decode(struct x86_emulate_st
s->evex.raw[1] = s->vex.raw[1];
s->evex.raw[2] = insn_fetch_type(uint8_t);
- generate_exception_if(!s->evex.mbs || s->evex.mbz, X86_EXC_UD);
+ generate_exception_if(!s->evex.mbs, X86_EXC_UD);
+ generate_exception_if((!(s->xcr0 & X86_XCR0_APX) &&
+ s->evex.b4),
+ X86_EXC_UD);
generate_exception_if(!s->evex.opmsk && s->evex.z, X86_EXC_UD);
if ( !mode_64bit() )
+ {
s->evex.r4 = 1;
+ s->evex.b4 = 0;
+ }
else if ( s->xcr0 & X86_XCR0_APX )
{
if ( !s->evex.r4 )
s->rex2_prefix |= REX_R;
+ if ( s->evex.b4 )
+ s->rex2_prefix |= REX_B;
}
s->vex.opcx = s->evex.opcx;
@@ -1320,6 +1328,16 @@ int x86emul_decode(struct x86_emulate_st
d = twobyte_table[0x3a].desc;
break;
+ case evex_map4:
+ if ( !mode_64bit() || !evex_encoded() )
+ {
+ rc = X86EMUL_UNRECOGNIZED;
+ goto done;
+ }
+
+ s->promoted_evex = true;
+ break;
+
case evex_map5:
if ( !evex_encoded() )
{
@@ -1832,10 +1850,56 @@ int x86emul_decode(struct x86_emulate_st
if ( s->modrm_mod == 3 )
{
+ bool rm_is_gpr = s->promoted_evex;
+
+ /* Originally VEX- (now EVEX-)encoded GPR insns. */
+ if ( (s->evex.opcx == vex_0f38 || s->evex.opcx == vex_0f3a ||
+ s->evex.opcx == vex_map7) &&
+ b >= 0xf0 )
+ rm_is_gpr = true;
+
+ /* A few other insns also encode a GPR in ModR/M.rm. */
+ switch ( opcode )
+ {
+ case X86EMUL_OPC_EVEX(0x0f, 0x92): /* kmovw r,k */
+ case X86EMUL_OPC_EVEX_66(0x0f, 0x92): /* kmovb r,k */
+ case X86EMUL_OPC_EVEX_F2(0x0f, 0x92): /* kmov{d,q} r,k */
+
+ case X86EMUL_OPC_EVEX_F3(0x0f, 0x2a): /* vcvtsi2ss */
+ case X86EMUL_OPC_EVEX_F2(0x0f, 0x2a): /* vcvtsi2sd */
+ case X86EMUL_OPC_EVEX_F3(5, 0x2a): /* vcvtsi2sh */
+
+ case X86EMUL_OPC_EVEX_F3(0x0f, 0x7b): /* vcvtusi2ss */
+ case X86EMUL_OPC_EVEX_F2(0x0f, 0x7b): /* vcvtusi2sd */
+ case X86EMUL_OPC_EVEX_F3(5, 0x7b): /* vcvtusi2sh */
+
+ case X86EMUL_OPC_EVEX_66(0x0f, 0x6e): /* vmov{d,q} r/m,xmm */
+ case X86EMUL_OPC_EVEX_66(5, 0x6e): /* vmovw r/m16,xmm */
+
+ case X86EMUL_OPC_EVEX_66(0x0f, 0x7e): /* vmov{d,q} xmm,r/m */
+ case X86EMUL_OPC_EVEX_66(5, 0x7e): /* vmovw xmm,r/m16 */
+
+ case X86EMUL_OPC_EVEX_66(0x0f38, 0x7a): /* vpbroadcastb */
+ case X86EMUL_OPC_EVEX_66(0x0f38, 0x7b): /* vpbroadcastw */
+ case X86EMUL_OPC_EVEX_66(0x0f38, 0x7c): /* vpbroadcast{d,q} */
+
+ case X86EMUL_OPC_EVEX_66(0x0f3a, 0x14): /* vpextrb */
+ case X86EMUL_OPC_EVEX_66(0x0f3a, 0x15): /* vpextrw */
+ case X86EMUL_OPC_EVEX_66(0x0f3a, 0x16): /* vpextr{d,q} */
+ case X86EMUL_OPC_EVEX_66(0x0f3a, 0x17): /* vextractps */
+
+ case X86EMUL_OPC_EVEX_66(0x0f, 0xc4): /* vpinsrw */
+ case X86EMUL_OPC_EVEX_66(0x0f3a, 0x20): /* vpinsrb */
+ case X86EMUL_OPC_EVEX_66(0x0f3a, 0x22): /* vpinsr{d,q} */
+ rm_is_gpr = true;
+ break;
+ }
+
generate_exception_if(d & vSIB, X86_EXC_UD);
s->modrm_rm |= ((s->rex_prefix & 1) << 3) |
- ((evex_encoded() ? !s->evex.x
- : !!(s->rex2_prefix & REX_B)) << 4);
+ ((evex_encoded() && !rm_is_gpr
+ ? !s->evex.x
+ : !!(s->rex2_prefix & REX_B)) << 4);
s->ea.type = OP_REG;
}
else if ( ad_bytes == 2 )
--- a/xen/arch/x86/x86_emulate/private.h
+++ b/xen/arch/x86/x86_emulate/private.h
@@ -202,7 +202,8 @@ enum vex_opcx {
vex_0f,
vex_0f38,
vex_0f3a,
- evex_map5 = 5,
+ evex_map4,
+ evex_map5,
evex_map6,
vex_map7,
};
@@ -235,9 +236,9 @@ union evex {
uint8_t raw[3];
struct { /* SDM names */
uint8_t opcx:3; /* mmm */
- uint8_t mbz:1;
+ uint8_t b4:1; /* B4 */
uint8_t r4:1; /* R4 (aka R') */
- uint8_t b:1; /* B */
+ uint8_t b3:1; /* B3 (aka B) */
uint8_t x:1; /* X */
uint8_t r3:1; /* R3 (aka R) */
uint8_t pfx:2; /* pp */
@@ -260,6 +261,7 @@ struct x86_emulate_state {
ext_0f = vex_0f,
ext_0f38 = vex_0f38,
ext_0f3a = vex_0f3a,
+ ext_map4 = evex_map4,
ext_map5 = evex_map5,
ext_map6 = evex_map6,
ext_map7 = vex_map7,
@@ -322,6 +324,7 @@ struct x86_emulate_state {
bool not_64bit; /* Instruction not available in 64bit. */
bool fpu_ctrl; /* Instruction is an FPU control one. */
bool fp16; /* Instruction has half-precision FP or BF16 source. */
+ bool promoted_evex; /* From legacy/VEX, i.e. ND / NF / SCC capable. */
opcode_desc_t desc;
union vex vex;
union evex evex;
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -3511,7 +3511,8 @@ x86_emulate(
if ( ea.type == OP_MEM )
{
/* convert memory operand to (%rAX) */
- evex.b = 1;
+ evex.b3 = 1;
+ evex.b4 = 0;
opc[1] &= 0x38;
}
insn_bytes = EVEX_PFX_BYTES + 2;
@@ -3724,7 +3725,8 @@ x86_emulate(
opc = init_evex(stub);
opc[0] = b;
/* Convert memory/GPR source to %rAX. */
- evex.b = 1;
+ evex.b3 = 1;
+ evex.b4 = 0;
if ( !mode_64bit() )
evex.w = 0;
/*
@@ -3780,7 +3782,8 @@ x86_emulate(
{
rex_prefix &= ~REX_B;
vex.b = 1;
- evex.b = 1;
+ evex.b3 = 1;
+ evex.b4 = 0;
opc[1] = 0x01;
rc = ops->read(ea.mem.seg, ea.mem.off, mmvalp,
@@ -3869,7 +3872,8 @@ x86_emulate(
/* Convert memory operand to (%rAX). */
rex_prefix &= ~REX_B;
vex.b = 1;
- evex.b = 1;
+ evex.b3 = 1;
+ evex.b4 = 0;
opc[1] &= 0x38;
}
if ( evex_encoded() )
@@ -4560,7 +4564,8 @@ x86_emulate(
opc = init_evex(stub);
opc[0] = b;
/* Convert memory/GPR operand to (%rAX). */
- evex.b = 1;
+ evex.b3 = 1;
+ evex.b4 = 0;
if ( !mode_64bit() )
evex.w = 0;
opc[1] = modrm & 0x38;
@@ -5464,7 +5469,8 @@ x86_emulate(
if ( ea.type == OP_MEM )
{
/* convert memory operand to (%rAX) */
- evex.b = 1;
+ evex.b3 = 1;
+ evex.b4 = 0;
opc[1] &= 0x38;
}
opc[2] = imm1;
@@ -6410,7 +6416,8 @@ x86_emulate(
opc = init_evex(stub);
opc[0] = b;
/* Convert GPR source to %rAX. */
- evex.b = 1;
+ evex.b3 = 1;
+ evex.b4 = 0;
if ( !mode_64bit() )
evex.w = 0;
opc[1] = modrm & 0xf8;
@@ -6647,7 +6654,8 @@ x86_emulate(
*/
pevex->opmsk = 0;
/* Use (%rax) as destination and modrm_reg as source. */
- pevex->b = 1;
+ pevex->b3 = 1;
+ pevex->b4 = 0;
opc[1] = (modrm_reg & 7) << 3;
pevex->RX = 1;
place_ret(&opc[2]);
@@ -6712,7 +6720,8 @@ x86_emulate(
opc[0] = 0x6f; /* vmovdqa{32,64} */
pevex->opmsk = 0;
/* Use modrm_reg as destination and (%rax) as source. */
- pevex->b = 1;
+ pevex->b3 = 1;
+ pevex->b4 = 0;
opc[1] = (modrm_reg & 7) << 3;
pevex->RX = 1;
place_ret(&opc[2]);
@@ -6802,7 +6811,8 @@ x86_emulate(
pevex->opcx = vex_0f;
opc[0] = 0x7f; /* vmovdqa{32,64} */
/* Use (%rax) as destination and modrm_reg as source. */
- pevex->b = 1;
+ pevex->b3 = 1;
+ pevex->b4 = 0;
opc[1] = (modrm_reg & 7) << 3;
pevex->RX = 1;
place_ret(&opc[2]);
@@ -7468,7 +7478,9 @@ x86_emulate(
opc[0] = b;
/* Convert memory/GPR operand to (%rAX). */
rex_prefix &= ~REX_B;
- evex.b = vex.b = 1;
+ vex.b = 1;
+ evex.b3 = 1;
+ evex.b4 = 0;
if ( !mode_64bit() )
evex.w = vex.w = 0;
opc[1] = modrm & 0x38;
@@ -7516,7 +7528,7 @@ x86_emulate(
evex.opcx = ext_0f3a;
b = 0x15;
modrm <<= 3;
- evex.r3 = evex.b;
+ evex.r3 = evex.b3;
evex.r4 = evex.x;
/* fall through */
case X86EMUL_OPC_EVEX_66(0x0f3a, 0x14): /* vpextrb $imm8,xmm,r/m */
@@ -7600,7 +7612,8 @@ x86_emulate(
{
/* Convert memory operand to (%rAX). */
vex.b = 1;
- evex.b = 1;
+ evex.b3 = 1;
+ evex.b4 = 0;
opc[1] &= 0x38;
}
opc[2] = imm1;
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 13/30] x86emul: switch to EVEX.X3 nomenclature
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (11 preceding siblings ...)
2026-11-11 14:25 ` [PATCH 12/30] x86emul: switch to EVEX.B{3,4} nomenclature Jan Beulich
@ 2026-11-11 14:25 ` Jan Beulich
2026-11-11 14:26 ` [PATCH 14/30] x86emul: support basic APX EVEX-encoded ALU insns Jan Beulich
` (16 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:25 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
The APX spec renames the X field and repurposes U for the 5th index
register bit; it can be expected for the SDM to follow once APX stuff is
folded in. The new naming is also better to reflect in our structure
field identifiers.
Like we represent VEX.X and EVEX.X3 in rex_prefix, also represent
EVEX.U (EVEX.X4) in rex2_prefix, allowing to leave index register
calculation untouched.
In the test harness'es evex-disp8.c also rename the "mbs" field at this
occasion.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
--- a/tools/tests/x86_emulator/evex-disp8.c
+++ b/tools/tests/x86_emulator/evex-disp8.c
@@ -941,10 +941,10 @@ static void test_one(const struct test *
uint8_t b4:1;
uint8_t r4:1;
uint8_t b3:1;
- uint8_t x:1;
+ uint8_t x3:1;
uint8_t r3:1;
uint8_t pfx:2;
- uint8_t mbs:1;
+ uint8_t u:1;
uint8_t reg:4;
uint8_t w:1;
uint8_t opmsk:3;
@@ -955,7 +955,7 @@ static void test_one(const struct test *
};
} evex = {
.opcx = test->spc, .pfx = test->pfx, .lr = vl,
- .r4 = 1, .b3 = 1, .x = 1, .r3 = 1, .mbs = 1,
+ .r4 = 1, .b3 = 1, .x3 = 1, .r3 = 1, .u = 1,
.reg = 0xf, .RX = 1, .opmsk = sg,
};
--- a/xen/arch/x86/x86_emulate/decode.c
+++ b/xen/arch/x86/x86_emulate/decode.c
@@ -16,7 +16,7 @@
# define ERR_PTR(val) NULL
#endif
-#define evex_encoded() (s->evex.mbs)
+#define evex_encoded() (s->evex.opcx)
struct x86_emulate_state *
x86_decode_insn(
@@ -1276,7 +1276,20 @@ int x86emul_decode(struct x86_emulate_st
s->evex.raw[1] = s->vex.raw[1];
s->evex.raw[2] = insn_fetch_type(uint8_t);
- generate_exception_if(!s->evex.mbs, X86_EXC_UD);
+ /*
+ * VEX.L's bit position matches EVEX.U. Shared code
+ * paths for vector insns use evex.lr (copying happens
+ * a little further down), but the bit being set can
+ * get in the way on shared APX code paths.
+ */
+ s->vex.l = 0;
+
+ /*
+ * .opcx is being checked here just to be on the safe
+ * side, especially as long as evex_encoded() uses
+ * this field.
+ */
+ generate_exception_if(!s->evex.opcx, X86_EXC_UD);
generate_exception_if((!(s->xcr0 & X86_XCR0_APX) &&
s->evex.b4),
X86_EXC_UD);
@@ -1293,6 +1306,8 @@ int x86emul_decode(struct x86_emulate_st
s->rex2_prefix |= REX_R;
if ( s->evex.b4 )
s->rex2_prefix |= REX_B;
+ if ( !s->evex.u )
+ s->rex2_prefix |= REX_X;
}
s->vex.opcx = s->evex.opcx;
@@ -1535,7 +1550,11 @@ int x86emul_decode(struct x86_emulate_st
if ( ext0f38_table[b].two_op )
d |= TwoOp;
if ( ext0f38_table[b].vsib )
+ {
+ generate_exception_if(evex_encoded() && !s->evex.u,
+ X86_EXC_UD);
d |= vSIB;
+ }
s->simd_size = ext0f38_table[b].simd_size;
if ( evex_encoded() )
{
@@ -1898,7 +1917,7 @@ 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() && !rm_is_gpr
- ? !s->evex.x
+ ? !s->evex.x3
: !!(s->rex2_prefix & REX_B)) << 4);
s->ea.type = OP_REG;
}
@@ -2038,6 +2057,16 @@ int x86emul_decode(struct x86_emulate_st
if ( override_seg != x86_seg_none )
s->ea.mem.seg = override_seg;
+ /*
+ * While this generic check takes care of most insns, scalar ones (with
+ * EVEX.b set) need checking individually (elsewhere).
+ */
+ generate_exception_if((evex_encoded() &&
+ !s->evex.u &&
+ (s->modrm_mod == 3 ||
+ !(s->xcr0 & X86_XCR0_APX))),
+ X86_EXC_UD);
+
/* Fetch the immediate operand, if present. */
switch ( d & SrcMask )
{
--- a/xen/arch/x86/x86_emulate/private.h
+++ b/xen/arch/x86/x86_emulate/private.h
@@ -239,10 +239,10 @@ union evex {
uint8_t b4:1; /* B4 */
uint8_t r4:1; /* R4 (aka R') */
uint8_t b3:1; /* B3 (aka B) */
- uint8_t x:1; /* X */
+ uint8_t x3:1; /* X3 (aka X) */
uint8_t r3:1; /* R3 (aka R) */
uint8_t pfx:2; /* pp */
- uint8_t mbs:1;
+ uint8_t u:1; /* U (aka X4) */
uint8_t reg:4; /* vvvv */
uint8_t w:1; /* W */
uint8_t opmsk:3; /* aaa */
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -1302,7 +1302,7 @@ int cf_check x86emul_unhandleable_rw(
#define lock_prefix (state->lock_prefix)
#define vex (state->vex)
#define evex (state->evex)
-#define evex_encoded() (evex.mbs)
+#define evex_encoded() (evex.opcx)
#define ea (state->ea)
/* Undo DEBUG wrapper. */
@@ -7529,7 +7529,7 @@ x86_emulate(
b = 0x15;
modrm <<= 3;
evex.r3 = evex.b3;
- evex.r4 = evex.x;
+ evex.r4 = evex.x3;
/* fall through */
case X86EMUL_OPC_EVEX_66(0x0f3a, 0x14): /* vpextrb $imm8,xmm,r/m */
case X86EMUL_OPC_EVEX_66(0x0f3a, 0x15): /* vpextrw $imm8,xmm,r/m */
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 14/30] x86emul: support basic APX EVEX-encoded ALU insns
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (12 preceding siblings ...)
2026-11-11 14:25 ` [PATCH 13/30] x86emul: switch to EVEX.X3 nomenclature Jan Beulich
@ 2026-11-11 14:26 ` Jan Beulich
2026-11-11 14:26 ` [PATCH 15/30] x86emul: support APX EVEX-encoded shift/rotate insns Jan Beulich
` (15 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:26 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
Quite a bit of generic (opcode independent) logic is needed to recognize /
handle (and reject when invalid to use) EVEX.nd and EVEX.nf. Opcode
independent SIMD mask register reads need suppressing for anything
promoted to EVEX from legacy or VEX encodings. Some basic SCC logic is
also better to have in decode_map4() right away, even if CCMPcc and
CTESTcc otherwise aren't covered here.
Predicates testing in the harness also needs a new "pass" added, to check
that with EVEX.nd set even insn forms with formerly a memory destination
are reported as just memory reads.
APX-NCI-NDD-NF applicability is a little odd: Anything representable by a
REX2 encoding requires the feature when EVEX-encoded, no matter whether
EVEX.nd or EVEX.nf are actually set. Anything that doesn't have a REX2
encoding works normally, i.e. requiring the feature bit only when either
of the features is actually used.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
---
RFC: To check just 64-bit mode during decode and XCR0.APX during (early)
execute is only one of the options. Like for REX2 we could also
check XCR0.APX during decode, just that predicates testing then
grows more involved (and respective entries then wouldn't be
tested anymore on APX-incapable hardware, unlike everything).
--- a/tools/tests/x86_emulator/evex-disp8.c
+++ b/tools/tests/x86_emulator/evex-disp8.c
@@ -10,7 +10,7 @@ struct test {
unsigned int pfx:2;
unsigned int vsz:3;
unsigned int esz:4;
- unsigned int scale:1;
+ unsigned int scale:2;
unsigned int ext:3;
};
@@ -19,7 +19,7 @@ enum spc {
SPC_0f,
SPC_0f38,
SPC_0f3a,
- SPC_unused4,
+ SPC_map4,
SPC_map5,
SPC_map6,
SPC_unused7,
@@ -41,6 +41,7 @@ enum vl {
enum scale { /* scale by memory operand ... */
SC_vl, /* ... vector length */
SC_el, /* ... element length */
+ SC_no, /* Don't scale. */
};
/*
@@ -861,6 +862,110 @@ static const struct test sm4_all[] = {
INSN(sm4rnds4, f2, 0f38, da, vl, d_nb, vl),
};
+static const struct test apx_f[] = {
+ INSN(adc, , map4, 11, el, dq, no),
+ INSN(adc, , map4, 13, el, dq, no),
+ INSNX(adc, , map4, 81, 2, el, dq, no),
+ INSNX(adc, , map4, 83, 2, el, dq, no),
+ INSN(adcb, , map4, 10, el, b, no),
+ INSN(adcb, , map4, 12, el, b, no),
+ INSNX(adcb, , map4, 80, 2, el, b, no),
+ INSN(adcw, 66, map4, 11, el, w, no),
+ INSN(adcw, 66, map4, 13, el, w, no),
+ INSNX(adcw, 66, map4, 81, 2, el, w, no),
+ INSNX(adcw, 66, map4, 83, 2, el, w, no),
+ INSN(add, , map4, 01, el, dq, no),
+ INSN(add, , map4, 03, el, dq, no),
+ INSNX(add, , map4, 81, 0, el, dq, no),
+ INSNX(add, , map4, 83, 0, el, dq, no),
+ INSN(addb, , map4, 00, el, b, no),
+ INSN(addb, , map4, 02, el, b, no),
+ INSNX(addb, , map4, 80, 0, el, b, no),
+ INSN(addw, 66, map4, 01, el, w, no),
+ INSN(addw, 66, map4, 03, el, w, no),
+ INSNX(addw, 66, map4, 81, 0, el, w, no),
+ INSNX(addw, 66, map4, 83, 0, el, w, no),
+ INSN(and, , map4, 21, el, dq, no),
+ INSN(and, , map4, 23, el, dq, no),
+ INSNX(and, , map4, 81, 4, el, dq, no),
+ INSNX(and, , map4, 83, 4, el, dq, no),
+ INSN(andb, , map4, 20, el, b, no),
+ INSN(andb, , map4, 22, el, b, no),
+ INSNX(andb, , map4, 80, 4, el, b, no),
+ INSN(andw, 66, map4, 21, el, w, no),
+ INSN(andw, 66, map4, 23, el, w, no),
+ INSNX(andw, 66, map4, 81, 4, el, w, no),
+ INSNX(andw, 66, map4, 83, 4, el, w, no),
+ INSNX(dec, , map4, ff, 1, el, dq, no),
+ INSNX(decb, , map4, fe, 1, el, b, no),
+ INSNX(decw, 66, map4, ff, 1, el, w, no),
+ INSNX(div, , map4, f7, 6, el, dq, no),
+ INSNX(divb, , map4, f6, 6, el, b, no),
+ INSNX(divw, 66, map4, f7, 6, el, w, no),
+ INSNX(idiv, , map4, f7, 7, el, dq, no),
+ INSNX(idivb, , map4, f6, 7, el, b, no),
+ INSNX(idivw, 66, map4, f7, 7, el, w, no),
+ INSNX(imul, , map4, f7, 5, el, dq, no),
+ INSNX(imulb, , map4, f6, 5, el, b, no),
+ INSNX(imulw, 66, map4, f7, 5, el, w, no),
+ INSNX(inc, , map4, ff, 0, el, dq, no),
+ INSNX(incb, , map4, fe, 0, el, b, no),
+ INSNX(incw, 66, map4, ff, 0, el, w, no),
+ INSNX(mul, , map4, f7, 4, el, dq, no),
+ INSNX(mulb, , map4, f6, 4, el, b, no),
+ INSNX(mulw, 66, map4, f7, 4, el, w, no),
+ INSNX(neg, , map4, f7, 3, el, dq, no),
+ INSNX(negb, , map4, f6, 3, el, b, no),
+ INSNX(negw, 66, map4, f7, 3, el, w, no),
+ INSNX(not, , map4, f7, 2, el, dq, no),
+ INSNX(notb, , map4, f6, 2, el, b, no),
+ INSNX(notw, 66, map4, f7, 2, el, w, no),
+ INSN(or, , map4, 09, el, dq, no),
+ INSN(or, , map4, 0b, el, dq, no),
+ INSNX(or, , map4, 81, 1, el, dq, no),
+ INSNX(or, , map4, 83, 1, el, dq, no),
+ INSN(orb, , map4, 08, el, b, no),
+ INSN(orb, , map4, 0a, el, b, no),
+ INSNX(orb, , map4, 80, 1, el, b, no),
+ INSN(orw, 66, map4, 09, el, w, no),
+ INSN(orw, 66, map4, 0b, el, w, no),
+ INSNX(orw, 66, map4, 81, 1, el, w, no),
+ INSNX(orw, 66, map4, 83, 1, el, w, no),
+ INSN(sbb, , map4, 19, el, dq, no),
+ INSN(sbb, , map4, 1b, el, dq, no),
+ INSNX(sbb, , map4, 81, 3, el, dq, no),
+ INSNX(sbb, , map4, 83, 3, el, dq, no),
+ INSN(sbbb, , map4, 18, el, b, no),
+ INSN(sbbb, , map4, 1a, el, b, no),
+ INSNX(sbbb, , map4, 80, 3, el, b, no),
+ INSN(sbbw, 66, map4, 19, el, w, no),
+ INSN(sbbw, 66, map4, 1b, el, w, no),
+ INSNX(sbbw, 66, map4, 81, 3, el, w, no),
+ INSNX(sbbw, 66, map4, 83, 3, el, w, no),
+ INSN(sub, , map4, 29, el, dq, no),
+ INSN(sub, , map4, 2b, el, dq, no),
+ INSNX(sub, , map4, 81, 5, el, dq, no),
+ INSNX(sub, , map4, 83, 5, el, dq, no),
+ INSN(subb, , map4, 28, el, b, no),
+ INSN(subb, , map4, 2a, el, b, no),
+ INSNX(subb, , map4, 80, 5, el, b, no),
+ INSN(subw, 66, map4, 29, el, w, no),
+ INSN(subw, 66, map4, 2b, el, w, no),
+ INSNX(subw, 66, map4, 81, 5, el, w, no),
+ INSNX(subw, 66, map4, 83, 5, el, w, no),
+ INSN(xor, , map4, 31, el, dq, no),
+ INSN(xor, , map4, 33, el, dq, no),
+ INSNX(xor, , map4, 81, 6, el, dq, no),
+ INSNX(xor, , map4, 83, 6, el, dq, no),
+ INSN(xorb, , map4, 30, el, b, no),
+ INSN(xorb, , map4, 32, el, b, no),
+ INSNX(xorb, , map4, 80, 6, el, b, no),
+ INSN(xorw, 66, map4, 31, el, w, no),
+ INSN(xorw, 66, map4, 33, el, w, no),
+ INSNX(xorw, 66, map4, 81, 6, el, w, no),
+ INSNX(xorw, 66, map4, 83, 6, 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 };
@@ -882,6 +987,17 @@ static const uint16_t imm0f[16] = {
(1 << 0x6) /* vshufp{d,s} */,
};
+/*
+ * This table, indicating the size of a possible immediate for a map 4 major
+ * opcode, is indexed by major opcode byte. Entries of value 4 will mean 2
+ * when operand size is 16-bit.
+ */
+static const uint8_t immM4[256] = {
+ [0x80] = 1,
+ [0x81] = 4,
+ [0x83] = 1,
+};
+
static struct x86_emulate_ops emulops;
/*
@@ -912,7 +1028,17 @@ static int read(enum x86_segment seg, un
{
if ( !record_access(seg, offset, bytes + !bytes) )
return X86EMUL_UNHANDLEABLE;
- memset(p_data, 0, bytes);
+
+ /*
+ * While 0 is the "better" dummy value, APX'es DIV/IDIV won't tolerate
+ * that.
+ */
+ memset(p_data,
+ ((ctxt->opcode | X86EMUL_OPC_66(0, 1))
+ != X86EMUL_OPC_EVEX_66(4, 0xf7) ? 0
+ : 0x7f),
+ bytes);
+
return X86EMUL_OKAY;
}
@@ -924,6 +1050,15 @@ static int write(enum x86_segment seg, u
return X86EMUL_OKAY;
}
+static int rmw(enum x86_segment seg, unsigned long offset, unsigned int bytes,
+ uint32_t *eflags, struct x86_emulate_state *state,
+ struct x86_emulate_ctxt *ctxt)
+{
+ if ( !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)
{
@@ -931,9 +1066,9 @@ static void test_one(const struct test *
int rc;
bool sg = strstr(test->mnemonic, "gather") ||
strstr(test->mnemonic, "scatter");
- bool imm = test->spc == SPC_0f3a ||
- (test->spc == SPC_0f &&
- (imm0f[test->opc >> 4] & (1 << (test->opc & 0xf))));
+ unsigned int imm = test->spc == SPC_0f3a ||
+ (test->spc == SPC_0f &&
+ (imm0f[test->opc >> 4] & (1 << (test->opc & 0xf))));
union evex {
uint8_t raw[3];
struct {
@@ -966,7 +1101,7 @@ static void test_one(const struct test *
break;
case ESZ_w:
- evex.w = 1;
+ evex.w = test->scale != SC_no;
/*
* VBMAC{,X}OR16x16x16 don't follow the general pattern: EVEX.W controls
* reduction kind there, not element size.
@@ -1034,6 +1169,13 @@ static void test_one(const struct test *
ASSERT_UNREACHABLE();
}
+ if ( test->spc == SPC_map4 )
+ {
+ imm = immM4[test->opc];
+ if ( imm == 4 && test->pfx == PFX_66 )
+ imm = 2;
+ }
+
/*
* Note: SIB addressing is used here, such that S/G insns can be handled
* without extra conditionals.
@@ -1047,9 +1189,28 @@ static void test_one(const struct test *
instr[6] = 0x22; /* SIB: base rDX, index none / xMM4 */
instr[7] = 1; /* Disp8 */
instr[8] = 0; /* immediate, if any */
+ instr[9] = 0; /* immediate, if wider than 8 bits */
+ instr[10] = 0; /* immediate, if wider than 16 bits */
+ instr[11] = 0; /* immediate, if wider than 24 bits */
- asm volatile ( "kxnorw %k1, %k1, %k1" );
- asm volatile ( "vxorps %xmm4, %xmm4, %xmm4" );
+ if ( test->spc != SPC_map4 )
+ {
+ asm volatile ( "kxnorw %k1, %k1, %k1" );
+ asm volatile ( "vxorps %xmm4, %xmm4, %xmm4" );
+ }
+ else
+ {
+ switch ( test->opc )
+ {
+ case 0xf6:
+ /*
+ * For APX 8-bit DIV/IDIV %ah needs to be less than what read()
+ * provides as divisor. Just set the entire %rax to zero.
+ */
+ ctxt->regs->eax = 0;
+ break;
+ }
+ }
ctxt->regs->eip = (unsigned long)&instr[0];
ctxt->regs->edx = 0;
@@ -1060,11 +1221,11 @@ static void test_one(const struct test *
(ctxt->regs->eip != (unsigned long)&instr[8 + imm]) )
goto fail;
- for ( i = 0; i < (test->scale == SC_vl ? vsz : esz); ++i )
+ n = test->scale == SC_vl ? vsz : test->scale != SC_no ? esz : 1;
+ for ( i = 0; i < n; ++i )
if ( accessed[i] )
goto fail;
- n = test->scale == SC_vl ? vsz : esz;
if ( !sg )
n += vsz;
else
@@ -1120,8 +1281,11 @@ static void test_one(const struct test *
return;
fail:
- printf("failed (v%s%s %u-bit)\n", test->mnemonic,
- evex.bcst ? "/bcst" : "", 128 << vl);
+ if ( test->scale != SC_no )
+ printf("failed (v%s%s %u-bit)\n", test->mnemonic,
+ evex.bcst ? "/bcst" : "", 128 << vl);
+ else
+ printf("failed (%s [0x%02x])\n", test->mnemonic, test->opc);
exit(1);
}
@@ -1184,7 +1348,8 @@ static void test_group(const struct test
case ESZ_dq:
test_pair(&tests[i], vl[j], ESZ_d,
- strncmp(tests[i].mnemonic, "cvt", 3) ? "d" : "l",
+ (strncmp(tests[i].mnemonic, "cvt", 3) &&
+ tests[i].spc != SPC_map4 ? "d" : "l"),
ESZ_q, "q", instr, ctxt);
break;
@@ -1288,4 +1453,28 @@ void evex_disp8_test(void *instr, struct
run(cpu_has_avx10_2, avx10_2, 128);
run(cpu_has_avx10_2_aux, avx10_2_aux, all);
+
+ if ( cpu_has_apx_f )
+ {
+ bool wb = ctxt->force_writeback;
+ bool nci_ndd_nf = cpu_policy.apx.apx_nci_ndd_nf;
+
+ emulops.rmw = rmw;
+
+ ctxt->force_writeback = true;
+
+ /*
+ * The emulator doesn't itself use EVEX-encoded APX insns; allow them
+ * them to be decoded successfully.
+ */
+ cpu_policy.apx.apx_nci_ndd_nf = true;
+
+ printf("%-40s", "Testing APX-F disp8 handling...");
+ test_group(apx_f, ARRAY_SIZE(apx_f),
+ vl_128, ARRAY_SIZE(vl_128), instr, ctxt);
+ printf("okay\n");
+
+ cpu_policy.apx.apx_nci_ndd_nf = nci_ndd_nf;
+ ctxt->force_writeback = wb;
+ }
}
--- a/tools/tests/x86_emulator/predicates.c
+++ b/tools/tests/x86_emulator/predicates.c
@@ -1642,6 +1642,7 @@ static const struct evex {
uint8_t w:2;
uint8_t l:3;
bool mask:1;
+ bool nd:1;
#define L2 4
#define Ln (L0 | L1 | L2)
} evex_0f[] = {
@@ -2127,6 +2128,119 @@ static const struct evex {
{ { 0xc2 }, 3, T, R, pfx_f2, W0, Ln }, /* vcmpbf16 */
{ { 0xce }, 3, T, R, pfx_66, W1, Ln }, /* vgf2p8affineqb */
{ { 0xcf }, 3, T, R, pfx_66, W1, Ln }, /* vgf2p8affineinvqb */
+}, evex_map4[] = {
+#ifdef __x86_64__
+#define ND .nd = 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 */
+ { { 0x02 }, 2, T, R, pfx_no, WIG, L0, ND }, /* add */
+ { { 0x03 }, 2, T, R, pfx_no, Wn, L0, ND }, /* add */
+ { { 0x03 }, 2, T, R, pfx_66, Wn, L0, ND }, /* add */
+ { { 0x08 }, 2, T, W, pfx_no, WIG, L0, ND }, /* or */
+ { { 0x09 }, 2, T, W, pfx_no, Wn, L0, ND }, /* or */
+ { { 0x09 }, 2, T, W, pfx_66, Wn, L0, ND }, /* or */
+ { { 0x0a }, 2, T, R, pfx_no, WIG, L0, ND }, /* or */
+ { { 0x0b }, 2, T, R, pfx_no, Wn, L0, ND }, /* or */
+ { { 0x0b }, 2, T, R, pfx_66, Wn, L0, ND }, /* or */
+ { { 0x10 }, 2, T, W, pfx_no, WIG, L0, ND }, /* adc */
+ { { 0x11 }, 2, T, W, pfx_no, Wn, L0, ND }, /* adc */
+ { { 0x11 }, 2, T, W, pfx_66, Wn, L0, ND }, /* adc */
+ { { 0x12 }, 2, T, R, pfx_no, WIG, L0, ND }, /* adc */
+ { { 0x13 }, 2, T, R, pfx_no, Wn, L0, ND }, /* adc */
+ { { 0x13 }, 2, T, R, pfx_66, Wn, L0, ND }, /* adc */
+ { { 0x18 }, 2, T, W, pfx_no, WIG, L0, ND }, /* sbb */
+ { { 0x19 }, 2, T, W, pfx_no, Wn, L0, ND }, /* sbb */
+ { { 0x19 }, 2, T, W, pfx_66, Wn, L0, ND }, /* sbb */
+ { { 0x1a }, 2, T, R, pfx_no, WIG, L0, ND }, /* sbb */
+ { { 0x1b }, 2, T, R, pfx_no, Wn, L0, ND }, /* sbb */
+ { { 0x1b }, 2, T, R, pfx_66, Wn, L0, ND }, /* sbb */
+ { { 0x20 }, 2, T, W, pfx_no, WIG, L0, ND }, /* and */
+ { { 0x21 }, 2, T, W, pfx_no, Wn, L0, ND }, /* and */
+ { { 0x21 }, 2, T, W, pfx_66, Wn, L0, ND }, /* and */
+ { { 0x22 }, 2, T, R, pfx_no, WIG, L0, ND }, /* and */
+ { { 0x23 }, 2, T, R, pfx_no, Wn, L0, ND }, /* and */
+ { { 0x23 }, 2, T, R, pfx_66, Wn, L0, ND }, /* and */
+ { { 0x28 }, 2, T, W, pfx_no, WIG, L0, ND }, /* sub */
+ { { 0x29 }, 2, T, W, pfx_no, Wn, L0, ND }, /* sub */
+ { { 0x29 }, 2, T, W, pfx_66, Wn, L0, ND }, /* sub */
+ { { 0x2a }, 2, T, R, pfx_no, WIG, L0, ND }, /* sub */
+ { { 0x2b }, 2, T, R, pfx_no, Wn, L0, ND }, /* sub */
+ { { 0x2b }, 2, T, R, pfx_66, Wn, L0, ND }, /* sub */
+ { { 0x30 }, 2, T, W, pfx_no, WIG, L0, ND }, /* xor */
+ { { 0x31 }, 2, T, W, pfx_no, Wn, L0, ND }, /* xor */
+ { { 0x31 }, 2, T, W, pfx_66, Wn, L0, ND }, /* xor */
+ { { 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 */
+ { { 0x80, 0x00 }, 3, T, W, pfx_no, WIG, L0, ND }, /* add */
+ { { 0x80, 0x08 }, 3, T, W, pfx_no, WIG, L0, ND }, /* or */
+ { { 0x80, 0x10 }, 3, T, W, pfx_no, WIG, L0, ND }, /* adc */
+ { { 0x80, 0x18 }, 3, T, W, pfx_no, WIG, L0, ND }, /* sbb */
+ { { 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 */
+ { { 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 */
+ { { 0x81, 0x08 }, 6, T, W, pfx_no, Wn, L0, ND }, /* or */
+ { { 0x81, 0x08 }, 4, T, W, pfx_66, W0, L0, ND }, /* or */
+ { { 0x81, 0x08 }, 6, T, W, pfx_66, W1, L0, ND }, /* or */
+ { { 0x81, 0x10 }, 6, T, W, pfx_no, Wn, L0, ND }, /* adc */
+ { { 0x81, 0x10 }, 4, T, W, pfx_66, W0, L0, ND }, /* adc */
+ { { 0x81, 0x10 }, 6, T, W, pfx_66, W1, L0, ND }, /* adc */
+ { { 0x81, 0x18 }, 6, T, W, pfx_no, Wn, L0, ND }, /* sbb */
+ { { 0x81, 0x18 }, 4, T, W, pfx_66, W0, L0, ND }, /* sbb */
+ { { 0x81, 0x18 }, 6, T, W, pfx_66, W1, L0, ND }, /* sbb */
+ { { 0x81, 0x20 }, 6, T, W, pfx_no, Wn, L0, ND }, /* and */
+ { { 0x81, 0x20 }, 4, T, W, pfx_66, W0, L0, ND }, /* and */
+ { { 0x81, 0x20 }, 6, T, W, pfx_66, W1, L0, ND }, /* and */
+ { { 0x81, 0x28 }, 6, T, W, pfx_no, Wn, L0, ND }, /* sub */
+ { { 0x81, 0x28 }, 4, T, W, pfx_66, W0, L0, ND }, /* sub */
+ { { 0x81, 0x28 }, 6, T, W, pfx_66, W1, L0, ND }, /* sub */
+ { { 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 */
+ { { 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 */
+ { { 0x83, 0x08 }, 3, T, W, pfx_66, Wn, L0, ND }, /* or */
+ { { 0x83, 0x10 }, 3, T, W, pfx_no, Wn, L0, ND }, /* adc */
+ { { 0x83, 0x10 }, 3, T, W, pfx_66, Wn, L0, ND }, /* adc */
+ { { 0x83, 0x18 }, 3, T, W, pfx_no, Wn, L0, ND }, /* sbb */
+ { { 0x83, 0x18 }, 3, T, W, pfx_66, Wn, L0, ND }, /* sbb */
+ { { 0x83, 0x20 }, 3, T, W, pfx_no, Wn, L0, ND }, /* and */
+ { { 0x83, 0x20 }, 3, T, W, pfx_66, Wn, L0, ND }, /* and */
+ { { 0x83, 0x28 }, 3, T, W, pfx_no, Wn, L0, ND }, /* sub */
+ { { 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 */
+ { { 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, 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 */
+ { { 0xf7, 0x18 }, 2, T, W, pfx_66, Wn, L0, ND }, /* neg */
+ { { 0xf7, 0x20 }, 2, T, R, pfx_no, Wn, L0 }, /* mul */
+ { { 0xf7, 0x20 }, 2, T, R, pfx_66, Wn, L0 }, /* mul */
+ { { 0xf7, 0x28 }, 2, T, R, pfx_no, Wn, L0 }, /* imul */
+ { { 0xf7, 0x28 }, 2, T, R, pfx_66, Wn, L0 }, /* imul */
+ { { 0xf7, 0x30 }, 2, T, R, pfx_no, Wn, L0 }, /* div */
+ { { 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 */
+ { { 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 */
+ { { 0xff, 0x00 }, 2, T, W, pfx_66, Wn, L0, ND }, /* inc */
+ { { 0xff, 0x08 }, 2, T, W, pfx_no, Wn, L0, ND }, /* dec */
+ { { 0xff, 0x08 }, 2, T, W, pfx_66, Wn, L0, ND }, /* dec */
+#undef ND
+#endif /* __x86_64__ */
}, evex_map5[] = {
{ { 0x10 }, 2, T, R, pfx_f3, W0, LIG }, /* vmovsh */
{ { 0x11 }, 2, T, W, pfx_f3, W0, LIG }, /* vmovsh */
@@ -2303,7 +2417,7 @@ static const struct {
{ evex_0f, ARRAY_SIZE(evex_0f) },
{ evex_0f38, ARRAY_SIZE(evex_0f38) },
{ evex_0f3a, ARRAY_SIZE(evex_0f3a) },
- { NULL, 0 },
+ { evex_map4, ARRAY_SIZE(evex_map4) },
{ evex_map5, ARRAY_SIZE(evex_map5) },
{ evex_map6, ARRAY_SIZE(evex_map6) },
};
@@ -2641,6 +2755,9 @@ void predicates_test(void *instr, struct
for ( x = 0; x < ARRAY_SIZE(evex); ++x )
{
+ if ( evex[x].tbl == evex_map4 && !m )
+ continue;
+
for ( t = 0; t < evex[x].num; ++t )
{
uint8_t *ptr = instr;
@@ -2679,6 +2796,21 @@ void predicates_test(void *instr, struct
evex[x].tbl[t].modrm ? (void *)ptr - instr + 1 : 0,
evex[x].tbl[t].mem, ctxt, fetch);
}
+
+ if ( evex[x].tbl[t].nd )
+ {
+ ptr[-1] |= 0x10;
+ /* Keep EVEX.W predictably clear for WIG / Wn cases. */
+ if ( evex[x].tbl[t].w != W1 )
+ ptr[-2] &= ~0x80;
+ else
+ ptr[-2] |= 0x80;
+ memcpy(ptr, evex[x].tbl[t].opc, evex[x].tbl[t].len);
+
+ do_test(instr, evex[x].tbl[t].len + ((void *)ptr - instr),
+ evex[x].tbl[t].modrm ? (void *)ptr - instr + 1 : 0,
+ mem_read, ctxt, fetch);
+ }
}
}
}
--- a/tools/tests/x86_emulator/test_x86_emulator.c
+++ b/tools/tests/x86_emulator/test_x86_emulator.c
@@ -1098,7 +1098,9 @@ int main(int argc, char **argv)
int rc;
#ifdef __x86_64__
unsigned int vendor_native;
+ bool nci_ndd_nf;
#else
+# define nci_ndd_nf false
unsigned int bcdres_native, bcdres_emul;
#endif
@@ -1126,6 +1128,10 @@ int main(int argc, char **argv)
if ( !stack_exec )
printf("Warning: Stack could not be made executable (%d).\n", errno);
+#ifndef nci_ndd_nf
+ nci_ndd_nf = cpu_policy.apx.apx_nci_ndd_nf;
+#endif
+
rmw_restart:
printf("%-40s", "Testing addl %ecx,(%eax)...");
instr[0] = 0x01; instr[1] = 0x08;
@@ -1360,6 +1366,148 @@ int main(int argc, char **argv)
(regs.rip != (unsigned long)&instr[4]) )
goto fail;
printf("okay\n");
+
+ /*
+ * The emulator doesn't itself use EVEX-encoded APX insns, so we can
+ * actually run these tests on all hardware.
+ */
+ if ( true /* cpu_has_apx_nci_ndd_nf */ )
+ {
+ cpu_policy.apx.apx_nci_ndd_nf = true;
+
+ /* EVEX-encoded forms of the above/below, where applicable. */
+ instr[0] = 0x62;
+
+ printf("%-40s", "Testing {nf}add %ecx,(%rax)...");
+ instr[1] = 0xf4; instr[2] = 0x7c; instr[3] = 0x0c;
+ instr[4] = 0x01; instr[5] = 0x08;
+ regs.eflags = EFLAGS_ALWAYS_SET;
+ regs.rip = (unsigned long)&instr[0];
+ regs.ecx = 0x12345678;
+ regs.rax = (unsigned long)res;
+ *res = 0x7fffffff;
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (*res != 0x92345677) ||
+ (regs.eflags != EFLAGS_ALWAYS_SET) ||
+ (regs.rip != (unsigned long)&instr[6]) )
+ goto fail;
+ printf("okay\n");
+
+ printf("%-40s", "Testing add %ecx,(%rax),%edx...");
+ instr[2] = 0x6c; instr[3] = 0x18;
+ regs.rip = (unsigned long)&instr[0];
+ regs.edx = 0x87654321;
+ *res = 0x7fffffff;
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rdx != 0x92345677) ||
+ (*res != 0x7fffffff) ||
+ (regs.eflags != (EFLAGS_ALWAYS_SET |
+ X86_EFLAGS(OF, SF, AF, PF))) ||
+ (regs.rip != (unsigned long)&instr[6]) )
+ goto fail;
+ printf("okay\n");
+
+ printf("%-40s", "Testing add %cx,(%rax),%dx...");
+ instr[2] = 0x6d;
+ regs.eflags = EFLAGS_ALWAYS_SET;
+ regs.rip = (unsigned long)&instr[0];
+ regs.ecx = 0x56781234;
+ regs.edx = 0x87654321;
+ *res = 0xffff7fff;
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rdx != 0x00009233) ||
+ (*res != 0xffff7fff) ||
+ (regs.eflags != (EFLAGS_ALWAYS_SET |
+ X86_EFLAGS(OF, SF, AF, PF))) ||
+ (regs.rip != (unsigned long)&instr[6]) )
+ goto fail;
+ printf("okay\n");
+
+ printf("%-40s", "Testing add %cl,(%rax),%dl...");
+ instr[2] = 0x6c; instr[4] = 0x00;
+ regs.eflags = EFLAGS_ALWAYS_SET;
+ regs.rip = (unsigned long)&instr[0];
+ regs.ecx = 0x34567812;
+ regs.edx = 0x87654321;
+ *res = 0xffffff7f;
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rdx != 0x00000091) ||
+ (*res != 0xffffff7f) ||
+ (regs.eflags != (EFLAGS_ALWAYS_SET | X86_EFLAGS(OF, SF, AF))) ||
+ (regs.rip != (unsigned long)&instr[6]) )
+ goto fail;
+ printf("okay\n");
+
+ printf("%-40s", "Testing {nf}xor (%rax),%ecx,%edx...");
+ instr[3] = 0x1c; instr[4] = 0x33;
+ regs.eflags = EFLAGS_ALWAYS_SET;
+ regs.rip = (unsigned long)&instr[0];
+ regs.ecx = 0xffffffff12345678UL;
+ regs.edx = 0xccccccccccccccccUL;
+ *res = 0x92345677;
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (*res != 0x92345677) ||
+ (regs.rcx != 0xffffffff12345678UL) ||
+ (regs.rdx != 0x8000000fUL) ||
+ (regs.eflags != EFLAGS_ALWAYS_SET) ||
+ (regs.rip != (unsigned long)&instr[6]) )
+ goto fail;
+ printf("okay\n");
+
+ printf("%-40s", "Testing not (%rdi),%cl...");
+ instr[2] = 0x74; instr[3] = 0x18; instr[4] = 0xf6; instr[5] = 0x17;
+ *res = 0x22334455;
+ regs.eflags = EFLAGS_MASK;
+ regs.rip = (unsigned long)&instr[0];
+ regs.rdi = (unsigned long)res;
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (*res != 0x22334455) ||
+ (regs.rcx != 0x000000aa) ||
+ ((regs.eflags & EFLAGS_MASK) != EFLAGS_MASK) ||
+ (regs.rip != (unsigned long)&instr[6]) )
+ goto fail;
+ printf("okay\n");
+
+ printf("%-40s", "Testing dec %cx,%ax...");
+ instr[2] = 0x7d; instr[4] = 0xff; instr[5] = 0xc9;
+ regs.eflags = EFLAGS_ALWAYS_SET;
+ regs.rip = (unsigned long)&instr[0];
+ regs.ecx = 0;
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rax != 0x0000ffff) ||
+ (regs.rcx != 0) ||
+ (regs.eflags != (EFLAGS_ALWAYS_SET | X86_EFLAGS(SF, AF, PF))) ||
+ (regs.rip != (unsigned long)&instr[6]) )
+ goto fail;
+ printf("okay\n");
+
+ printf("%-40s", "Testing {nf}imull -4(%rcx)...");
+ instr[2] = 0x7c; instr[3] = 0x0c;
+ instr[4] = 0xf7; instr[5] = 0x69; instr[6] = 0xfc;
+ regs.eflags = EFLAGS_ALWAYS_SET;
+ regs.rip = (unsigned long)&instr[0];
+ regs.eax = 0x89abcdef;
+ *res = 0x12345678;
+ regs.rcx = (unsigned long)(res + 1);
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rax != 0x89abcdefU * 0x12345678) ||
+ (regs.rdx != (uint64_t)((int64_t)(int32_t)0x89abcdef *
+ 0x12345678) >> 32) ||
+ (regs.eflags != EFLAGS_ALWAYS_SET) ||
+ (regs.rip != (unsigned long)&instr[7]) )
+ goto fail;
+ printf("okay\n");
+
+ cpu_policy.apx.apx_nci_ndd_nf = nci_ndd_nf;
+ }
#endif
printf("%-40s", "Testing xadd %ax,(%ecx)...");
@@ -5782,8 +5930,16 @@ int main(int argc, char **argv)
if ( stack_exec )
evex_disp8_test(instr, &ctxt, &emulops);
+ /*
+ * Some EVEX.nd / EVEX.nf checks occur during decode. Allow decode to
+ * succeed in such cases.
+ */
+ cpu_policy.apx.apx_nci_ndd_nf = true;
+
predicates_test(instr, &ctxt, fetch);
+ cpu_policy.apx.apx_nci_ndd_nf = nci_ndd_nf;
+
for ( j = 0; j < ARRAY_SIZE(blobs); j++ )
{
unsigned int nr;
--- a/xen/arch/x86/x86_emulate/decode.c
+++ b/xen/arch/x86/x86_emulate/decode.c
@@ -530,6 +530,170 @@ static const struct ext0f3a_table {
[0xf0] = { .two_op = 1 /* Mov */ },
};
+static const struct map4_table {
+ bool to_mem:1; /* Unless EVEX.nd is set. */
+ bool byte:1;
+ bool immW:1;
+ bool imm8:1;
+ bool nd:1;
+ bool nf:1;
+ bool nci_ndd_nf:1; /* APX-NCI-NDD-NF required even if EVEX.{nd,nf}=0. */
+} map4_table[256][4] = {
+ [0x00] = { /* ADD (byte) */
+ [vex_np] = { .to_mem = true, .byte = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ },
+ [0x01] = { /* ADD ({,d,q}word) */
+ [vex_np] = { .to_mem = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ [vex_66] = { .to_mem = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ },
+ [0x02] = { /* ADD (byte) */
+ [vex_np] = { .byte = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ },
+ [0x03] = { /* ADD ({,d,q}word) */
+ [vex_np] = { .nd = true, .nf = true, .nci_ndd_nf = true },
+ [vex_66] = { .nd = true, .nf = true, .nci_ndd_nf = true },
+ },
+ [0x08] = { /* OR (byte) */
+ [vex_np] = { .to_mem = true, .byte = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ },
+ [0x09] = { /* OR ({,d,q}word) */
+ [vex_np] = { .to_mem = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ [vex_66] = { .to_mem = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ },
+ [0x0a] = { /* OR (byte) */
+ [vex_np] = { .byte = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ },
+ [0x0b] = { /* OR ({,d,q}word) */
+ [vex_np] = { .nd = true, .nf = true, .nci_ndd_nf = true },
+ [vex_66] = { .nd = true, .nf = true, .nci_ndd_nf = true },
+ },
+ [0x10] = { /* ADC (byte) */
+ [vex_np] = { .to_mem = true, .byte = true, .nd = true,
+ .nci_ndd_nf = true },
+ },
+ [0x11] = { /* ADC ({,d,q}word) */
+ [vex_np] = { .to_mem = true, .nd = true, .nci_ndd_nf = true },
+ [vex_66] = { .to_mem = true, .nd = true, .nci_ndd_nf = true },
+ },
+ [0x12] = { /* ADC (byte) */
+ [vex_np] = { .byte = true, .nd = true, .nci_ndd_nf = true },
+ },
+ [0x13] = { /* ADC ({,d,q}word) */
+ [vex_np] = { .nd = true, .nci_ndd_nf = true },
+ [vex_66] = { .nd = true, .nci_ndd_nf = true },
+ },
+ [0x18] = { /* SBB (byte) */
+ [vex_np] = { .to_mem = true, .byte = true, .nd = true,
+ .nci_ndd_nf = true },
+ },
+ [0x19] = { /* SBB ({,d,q}word) */
+ [vex_np] = { .to_mem = true, .nd = true, .nci_ndd_nf = true },
+ [vex_66] = { .to_mem = true, .nd = true, .nci_ndd_nf = true },
+ },
+ [0x1a] = { /* SBB (byte) */
+ [vex_np] = { .byte = true, .nd = true, .nci_ndd_nf = true },
+ },
+ [0x1b] = { /* SBB ({,d,q}word) */
+ [vex_np] = { .nd = true, .nci_ndd_nf = true },
+ [vex_66] = { .nd = true, .nci_ndd_nf = true },
+ },
+ [0x20] = { /* AND (byte) */
+ [vex_np] = { .to_mem = true, .byte = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ },
+ [0x21] = { /* AND ({,d,q}word) */
+ [vex_np] = { .to_mem = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ [vex_66] = { .to_mem = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ },
+ [0x22] = { /* AND (byte) */
+ [vex_np] = { .byte = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ },
+ [0x23] = { /* AND ({,d,q}word) */
+ [vex_np] = { .nd = true, .nf = true, .nci_ndd_nf = true },
+ [vex_66] = { .nd = true, .nf = true, .nci_ndd_nf = true },
+ },
+ [0x28] = { /* SUB (byte) */
+ [vex_np] = { .to_mem = true, .byte = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ },
+ [0x29] = { /* SUB ({,d,q}word) */
+ [vex_np] = { .to_mem = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ [vex_66] = { .to_mem = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ },
+ [0x2a] = { /* SUB (byte) */
+ [vex_np] = { .byte = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ },
+ [0x2b] = { /* SUB ({,d,q}word) */
+ [vex_np] = { .nd = true, .nf = true, .nci_ndd_nf = true },
+ [vex_66] = { .nd = true, .nf = true, .nci_ndd_nf = true },
+ },
+ [0x30] = { /* XOR (byte) */
+ [vex_np] = { .to_mem = true, .byte = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ },
+ [0x31] = { /* XOR ({,d,q}word) */
+ [vex_np] = { .to_mem = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ [vex_66] = { .to_mem = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ },
+ [0x32] = { /* XOR (byte) */
+ [vex_np] = { .byte = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ },
+ [0x33] = { /* XOR ({,d,q}word) */
+ [vex_np] = { .nd = true, .nf = true, .nci_ndd_nf = true },
+ [vex_66] = { .nd = true, .nf = true, .nci_ndd_nf = true },
+ },
+ [0x80] = { /* Grp1 (byte); .nf depends on modrm_reg */
+ [vex_np] = { .to_mem = true, .byte = true, .imm8 = true, .nd = true,
+ .nci_ndd_nf = true },
+ },
+ [0x81] = { /* Grp1 ({,d,q}word); .nf depends on modrm_reg */
+ [vex_np] = { .to_mem = true, .immW = true, .nd = true,
+ .nci_ndd_nf = true },
+ [vex_66] = { .to_mem = true, .immW = true, .nd = true,
+ .nci_ndd_nf = true },
+ },
+ [0x83] = { /* Grp1 ({,d,q}word); .nf depends on modrm_reg */
+ [vex_np] = { .to_mem = true, .imm8 = true, .nd = true,
+ .nci_ndd_nf = true },
+ [vex_66] = { .to_mem = true, .imm8 = true, .nd = true,
+ .nci_ndd_nf = true },
+ },
+ [0xf6] = { /* Grp3 (byte); most attributes depend on modrm_reg */
+ [vex_np] = { .byte = true, .nci_ndd_nf = true },
+ },
+ [0xf7] = { /* Grp3 ({,d,q}word); most attributes depend on modrm_reg */
+ [vex_np] = { .nci_ndd_nf = true },
+ [vex_66] = { .nci_ndd_nf = true },
+ },
+ [0xfe] = { /* Grp4 */
+ [vex_np] = { .to_mem = true, .byte = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ },
+ [0xff] = { /* Grp5 */
+ [vex_np] = { .to_mem = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ [vex_66] = { .to_mem = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ },
+};
+
static const opcode_desc_t xop_table[] = {
DstReg|SrcImmByte|ModRM,
DstReg|SrcMem|ModRM,
@@ -1030,6 +1194,78 @@ decode_0f3a(struct x86_emulate_state *s,
return X86EMUL_OKAY;
}
+static int
+decode_map4(struct x86_emulate_state *s,
+ struct x86_emulate_ctxt *ctxt,
+ const struct x86_emulate_ops *ops)
+{
+ int rc = X86EMUL_OKAY;
+ bool nd = map4_table[ctxt->opcode & 0xff][s->evex.pfx].nd;
+ bool nf = map4_table[ctxt->opcode & 0xff][s->evex.pfx].nf;
+ bool nci_ndd_nf = map4_table[ctxt->opcode & 0xff][s->evex.pfx].nci_ndd_nf;
+ bool scc = false;
+
+ generate_exception_if(s->evex.lr || s->evex.z, X86_EXC_UD);
+
+ switch ( ctxt->opcode & 0xff )
+ {
+ case 0x80: case 0x81: case 0x83: /* Grp1 */
+ if ( (s->modrm_reg & 7) == 7 ) /* ccmp */
+ {
+ s->desc = (s->desc & ByteOp) | DstNone | SrcMem;
+ nd = false;
+ scc = true;
+ break;
+ }
+
+ /* No need to include opcode bit 0 in the index calculation. */
+ nf = map4_table[(s->modrm_reg & 7) << 3][s->evex.pfx].nf;
+ break;
+
+ case 0xf6: case 0xf7: /* Grp3 */
+ switch ( s->modrm_reg & 7 )
+ {
+ case 0: case 1: /* ctest */
+ s->desc = (s->desc & ByteOp) | DstNone | SrcMem;
+ scc = true;
+ break;
+
+ case 2: /* not */
+ nd = true;
+ break;
+
+ case 3: /* neg */
+ nd = true;
+ nf = true;
+ break;
+
+ default: /* {,i}mul, {,i}div */
+ nf = true;
+ break;
+ }
+ break;
+ }
+
+ generate_exception_if((scc || nci_ndd_nf) && !vcpu_has_apx_nci_ndd_nf(),
+ X86_EXC_UD);
+ generate_exception_if((EVEX_ND(&s->evex) &&
+ (!nd || !vcpu_has_apx_nci_ndd_nf())),
+ X86_EXC_UD);
+ if ( !scc )
+ {
+ generate_exception_if(s->evex.opmsk & 3, X86_EXC_UD);
+ generate_exception_if((EVEX_NF(&s->evex) &&
+ (!nf || !vcpu_has_apx_nci_ndd_nf())),
+ X86_EXC_UD);
+ generate_exception_if((!EVEX_ND(&s->evex) &&
+ (s->evex.reg != 0xf || !s->evex.RX)),
+ X86_EXC_UD);
+ }
+
+ done:
+ return rc;
+}
+
#define ad_bytes (s->ad_bytes) /* for truncate_ea() */
int x86emul_decode(struct x86_emulate_state *s,
@@ -1350,7 +1586,37 @@ int x86emul_decode(struct x86_emulate_st
goto done;
}
+ opcode |= MASK_INSR(4, X86EMUL_OPC_EXT_MASK);
s->promoted_evex = true;
+
+ /*
+ * In the common case (and when NP and 66 table entries
+ * match) an embedded 66 prefix takes the role of an
+ * operand size override (to 16 bits).
+ */
+ if ( s->op_bytes == 4 && s->evex.pfx == vex_66 &&
+ !memcmp(&map4_table[b][vex_np],
+ &map4_table[b][vex_66],
+ sizeof(**map4_table)) )
+ s->op_bytes = 2;
+
+ d = ModRM;
+ if ( map4_table[b][s->evex.pfx].byte )
+ d |= ByteOp;
+
+ if ( map4_table[b][s->evex.pfx].to_mem )
+ d |= DstMem;
+ else
+ d |= DstReg;
+
+ if ( map4_table[b][s->evex.pfx].immW )
+ d |= SrcImm;
+ else if ( map4_table[b][s->evex.pfx].imm8 )
+ d |= SrcImmByte;
+ else if ( !map4_table[b][s->evex.pfx].to_mem )
+ d |= SrcMem;
+ else
+ d |= SrcReg;
break;
case evex_map5:
@@ -1444,12 +1710,14 @@ int x86emul_decode(struct x86_emulate_st
switch ( s->ext )
{
case ext_none:
+ case ext_map4:
switch ( b )
{
case 0xf6 ... 0xf7: /* Grp3 */
+ d &= ByteOp;
switch ( s->modrm_reg & 7 )
{
- case 0 ... 1: /* test */
+ case 0 ... 1: /* {,c}test */
d |= DstMem | SrcImm;
break;
case 2: /* not */
@@ -2136,6 +2404,10 @@ int x86emul_decode(struct x86_emulate_st
s->simd_size = ext8f08_table[b].simd_size;
break;
+ case ext_map4:
+ rc = decode_map4(s, ctxt, ops);
+ break;
+
case ext_map5:
case ext_map6:
case ext_map7:
@@ -2164,10 +2436,10 @@ int x86emul_decode(struct x86_emulate_st
{
case simd_none:
/*
- * When prefix 66 has a meaning different from operand-size override,
- * operand size defaults to 4 and can't be overridden to 2.
+ * When (legacy) prefix 66 has a meaning different from operand-size
+ * override, operand size defaults to 4 and can't be overridden to 2.
*/
- if ( s->op_bytes == 2 &&
+ if ( s->op_bytes == 2 && !s->vex.opcx &&
(ctxt->opcode & X86EMUL_OPC_PFX_MASK) == X86EMUL_OPC_66(0, 0) )
s->op_bytes = 4;
break;
--- a/xen/arch/x86/x86_emulate/private.h
+++ b/xen/arch/x86/x86_emulate/private.h
@@ -253,6 +253,9 @@ union evex {
};
};
+#define EVEX_ND(e) ((e)->brs)
+#define EVEX_NF(e) (!!((e)->opmsk & 4))
+
struct x86_emulate_state {
unsigned int op_bytes, ad_bytes;
@@ -620,6 +623,8 @@ amd_like(const struct x86_emulate_ctxt *
#define vcpu_has_avx10(minor) (ctxt->cpuid->avx10.version >= (minor))
#define vcpu_has_avx10_2_aux() (ctxt->cpuid->avx10.avx10_v2_aux)
+#define vcpu_has_apx_nci_ndd_nf() (ctxt->cpuid->apx.apx_nci_ndd_nf)
+
#define vcpu_must_have(feat, ...) \
generate_exception_if(!vcpu_has_##feat(__VA_ARGS__), X86_EXC_UD)
--- a/xen/arch/x86/x86_emulate/util.c
+++ b/xen/arch/x86/x86_emulate/util.c
@@ -194,8 +194,10 @@ bool cf_check x86_insn_is_mem_write(cons
switch ( s->desc & DstMask )
{
case DstMem:
- /* The SrcMem check is to cover {,V}MASKMOV{Q,DQU}. */
- return s->modrm_mod != 3 || (s->desc & SrcMask) == SrcMem;
+ return (s->modrm_mod != 3 &&
+ (!s->promoted_evex || !EVEX_ND(&s->evex))) ||
+ /* This is to cover {,V}MASKMOV{Q,DQU}. */
+ (s->desc & SrcMask) == SrcMem;
case DstBitBase:
case DstImplicit:
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -1204,6 +1204,14 @@ static unsigned long *decode_vex_gpr(
return decode_gpr(regs, NULL, ~vex_reg & (mode_64bit() ? 0xf : 7));
}
+static unsigned long *decode_evex_gpr(
+ unsigned int evex_reg, struct cpu_user_regs *regs,
+ struct egpr *egpr, const struct x86_emulate_ctxt *ctxt)
+{
+ ASSERT(mode_64bit());
+ return decode_gpr(regs, egpr, ~evex_reg & 0x1f);
+}
+
#define visa_check(subfeat) \
generate_exception_if(!cp->feat.avx512 ## subfeat && !cp->feat.avx10, \
X86_EXC_UD)
@@ -1369,6 +1377,20 @@ x86_emulate(
elem_bytes = 2 << (!state->fp16 + evex.w);
generate_exception_if(state->not_64bit && mode_64bit(), X86_EXC_UD);
+#ifdef __XEN__
+ generate_exception_if(state->promoted_evex && !(state->xcr0 & X86_XCR0_APX),
+ X86_EXC_UD);
+#else
+ /*
+ * To emulate promoted insns we don't use their EVEX forms. For testing
+ * purposes we can therefore permit such encodings without APX active, so
+ * long as they don't use any of the eGPR-s.
+ */
+ generate_exception_if((state->promoted_evex &&
+ (!evex.r4 || evex.b4 || !evex.u || !evex.RX) &&
+ !(state->xcr0 & X86_XCR0_APX)),
+ X86_EXC_UD);
+#endif
if ( ea.type == OP_REG )
ea.reg = _decode_gpr(&_regs, &state->egpr, modrm_rm,
@@ -1445,7 +1467,7 @@ x86_emulate(
#ifndef X86EMUL_NO_SIMD
/* With a memory operand, fetch the mask register in use (if any). */
- if ( ea.type == OP_MEM && evex.opmsk &&
+ if ( ea.type == OP_MEM && !state->promoted_evex && evex.opmsk &&
x86emul_get_fpu(fpu_type = X86EMUL_FPU_opmask,
ctxt, ops) == X86EMUL_OKAY )
{
@@ -1555,6 +1577,7 @@ x86_emulate(
generate_exception_if(lock_prefix, X86_EXC_UD);
break;
}
+ generate_exception_if(evex_encoded() && lock_prefix, X86_EXC_UD);
ea.bytes = (d & ByteOp) ? 1 : op_bytes;
dst = ea;
if ( dst.type == OP_REG )
@@ -1574,7 +1597,7 @@ x86_emulate(
generate_exception_if(lock_prefix, X86_EXC_UD);
fail_if(!ops->write);
}
- else if ( !ops->rmw )
+ else if ( !ops->rmw || (evex_encoded() && EVEX_ND(&evex)) )
{
fail_if(lock_prefix ? !ops->cmpxchg : !ops->write);
if ( (rc = read_ulong(dst.mem.seg, dst.mem.off,
@@ -1594,72 +1617,128 @@ x86_emulate(
unsigned int i, n;
unsigned long dummy;
- case 0x00: case 0x01: add: /* add reg,mem */
- if ( ops->rmw && dst.type == OP_MEM )
+ case 0x00: case 0x01: /* add reg,mem */
+ case X86EMUL_OPC_EVEX (4, 0x00): /* add r8,r/m{,r8} */
+ case X86EMUL_OPC_EVEX (4, 0x01): /* add reg,r/m{,reg} */
+ case X86EMUL_OPC_EVEX_66(4, 0x01): /* add r16,r/m{,r16} */
+ add:
+ if ( ops->rmw && dst.type == OP_MEM &&
+ (!evex_encoded() || !EVEX_ND(&evex)) )
state->rmw = rmw_add;
else
{
case 0x02 ... 0x05: /* add */
+ case X86EMUL_OPC_EVEX (4, 0x02): /* add r/m,r8{,r8} */
+ case X86EMUL_OPC_EVEX (4, 0x03): /* add r/m,reg{,reg} */
+ case X86EMUL_OPC_EVEX_66(4, 0x03): /* add r/m,r16{,r16} */
emulate_2op_SrcV("add", src, dst, _regs.eflags);
}
break;
- case 0x08: case 0x09: or: /* or reg,mem */
- if ( ops->rmw && dst.type == OP_MEM )
+ case 0x08: case 0x09: /* or reg,mem */
+ case X86EMUL_OPC_EVEX (4, 0x08): /* or r8,r/m{,r8} */
+ case X86EMUL_OPC_EVEX (4, 0x09): /* or reg,r/m{,reg} */
+ case X86EMUL_OPC_EVEX_66(4, 0x09): /* or r16,r/m{,r16} */
+ or:
+ if ( ops->rmw && dst.type == OP_MEM &&
+ (!evex_encoded() || !EVEX_ND(&evex)) )
state->rmw = rmw_or;
else
{
case 0x0a ... 0x0d: /* or */
+ case X86EMUL_OPC_EVEX (4, 0x0a): /* or r/m,r8{,r8} */
+ case X86EMUL_OPC_EVEX (4, 0x0b): /* or r/m,reg{,reg} */
+ case X86EMUL_OPC_EVEX_66(4, 0x0b): /* or r/m,r16{,r16} */
emulate_2op_SrcV("or", src, dst, _regs.eflags);
}
break;
- case 0x10: case 0x11: adc: /* adc reg,mem */
- if ( ops->rmw && dst.type == OP_MEM )
+ case 0x10: case 0x11: /* adc reg,mem */
+ case X86EMUL_OPC_EVEX (4, 0x10): /* adc r8,r/m{,r8} */
+ case X86EMUL_OPC_EVEX (4, 0x11): /* adc reg,r/m{,reg} */
+ case X86EMUL_OPC_EVEX_66(4, 0x11): /* adc r16,r/m{,r16} */
+ adc:
+ if ( ops->rmw && dst.type == OP_MEM &&
+ (!evex_encoded() || !EVEX_ND(&evex)) )
state->rmw = rmw_adc;
else
{
case 0x12 ... 0x15: /* adc */
+ case X86EMUL_OPC_EVEX (4, 0x12): /* adc r/m,r8{,r8} */
+ case X86EMUL_OPC_EVEX (4, 0x13): /* adc r/m,reg{,reg} */
+ case X86EMUL_OPC_EVEX_66(4, 0x13): /* adc r/m,r16{,r16} */
emulate_2op_SrcV("adc", src, dst, _regs.eflags);
}
break;
- case 0x18: case 0x19: sbb: /* sbb reg,mem */
- if ( ops->rmw && dst.type == OP_MEM )
+ case 0x18: case 0x19: /* sbb reg,mem */
+ case X86EMUL_OPC_EVEX (4, 0x18): /* sbb r8,r/m{,r8} */
+ case X86EMUL_OPC_EVEX (4, 0x19): /* sbb reg,r/m{,reg} */
+ case X86EMUL_OPC_EVEX_66(4, 0x19): /* sbb r16,r/m{,r16} */
+ sbb:
+ if ( ops->rmw && dst.type == OP_MEM &&
+ (!evex_encoded() || !EVEX_ND(&evex)) )
state->rmw = rmw_sbb;
else
{
case 0x1a ... 0x1d: /* sbb */
+ case X86EMUL_OPC_EVEX (4, 0x1a): /* sbb r/m,r8{,r8} */
+ case X86EMUL_OPC_EVEX (4, 0x1b): /* sbb r/m,reg{,reg} */
+ case X86EMUL_OPC_EVEX_66(4, 0x1b): /* sbb r/m,r16{,r16} */
emulate_2op_SrcV("sbb", src, dst, _regs.eflags);
}
break;
- case 0x20: case 0x21: and: /* and reg,mem */
- if ( ops->rmw && dst.type == OP_MEM )
+ case 0x20: case 0x21: /* and reg,mem */
+ case X86EMUL_OPC_EVEX (4, 0x20): /* and r8,r/m{,r8} */
+ case X86EMUL_OPC_EVEX (4, 0x21): /* and reg,r/m{,reg} */
+ case X86EMUL_OPC_EVEX_66(4, 0x21): /* and r16,r/m{,r16} */
+ and:
+ if ( ops->rmw && dst.type == OP_MEM &&
+ (!evex_encoded() || !EVEX_ND(&evex)) )
state->rmw = rmw_and;
else
{
case 0x22 ... 0x25: /* and */
+ case X86EMUL_OPC_EVEX (4, 0x22): /* and r/m,r8{,r8} */
+ case X86EMUL_OPC_EVEX (4, 0x23): /* and r/m,reg{,reg} */
+ case X86EMUL_OPC_EVEX_66(4, 0x23): /* and r/m,r16{,r16} */
emulate_2op_SrcV("and", src, dst, _regs.eflags);
}
break;
- case 0x28: case 0x29: sub: /* sub reg,mem */
- if ( ops->rmw && dst.type == OP_MEM )
+ case 0x28: case 0x29: /* sub reg,mem */
+ case X86EMUL_OPC_EVEX (4, 0x28): /* sub r8,r/m{,r8} */
+ case X86EMUL_OPC_EVEX (4, 0x29): /* sub reg,r/m{,reg} */
+ case X86EMUL_OPC_EVEX_66(4, 0x29): /* sub r16,r/m{,r16} */
+ sub:
+ if ( ops->rmw && dst.type == OP_MEM &&
+ (!evex_encoded() || !EVEX_ND(&evex)) )
state->rmw = rmw_sub;
else
{
case 0x2a ... 0x2d: /* sub */
+ case X86EMUL_OPC_EVEX (4, 0x2a): /* sub r/m,r8{,r8} */
+ case X86EMUL_OPC_EVEX (4, 0x2b): /* sub r/m,reg{,reg} */
+ case X86EMUL_OPC_EVEX_66(4, 0x2b): /* sub r/m,r16{,r16} */
emulate_2op_SrcV("sub", src, dst, _regs.eflags);
}
break;
- case 0x30: case 0x31: xor: /* xor reg,mem */
- if ( ops->rmw && dst.type == OP_MEM )
+ case 0x30: case 0x31: /* xor reg,mem */
+ case X86EMUL_OPC_EVEX (4, 0x30): /* xor r8,r/m{,r8} */
+ case X86EMUL_OPC_EVEX (4, 0x31): /* xor reg,r/m{,reg} */
+ case X86EMUL_OPC_EVEX_66(4, 0x31): /* xor r16,r/m{,r16} */
+ xor:
+ if ( ops->rmw && dst.type == OP_MEM &&
+ (!evex_encoded() || !EVEX_ND(&evex)) )
state->rmw = rmw_xor;
else
{
case 0x32 ... 0x35: /* xor */
+ case X86EMUL_OPC_EVEX (4, 0x32): /* xor r/m,r8{,r8} */
+ case X86EMUL_OPC_EVEX (4, 0x33): /* xor r/m,reg{,reg} */
+ case X86EMUL_OPC_EVEX_66(4, 0x33): /* xor r/m,r16{,r16} */
emulate_2op_SrcV("xor", src, dst, _regs.eflags);
}
break;
@@ -1970,6 +2049,11 @@ x86_emulate(
break;
case 0x80: case 0x81: case 0x82: case 0x83: /* Grp1 */
+ case X86EMUL_OPC_EVEX (4, 0x80): /* <op> imm8,r/m{,r8} */
+ case X86EMUL_OPC_EVEX (4, 0x81): /* <op> imm,r/m{,reg} */
+ case X86EMUL_OPC_EVEX_66(4, 0x81): /* <op> imm16,r/m{,r16} */
+ case X86EMUL_OPC_EVEX (4, 0x83): /* <op> imm8s,r/m{,reg} */
+ case X86EMUL_OPC_EVEX_66(4, 0x83): /* <op> imm8s,r/m{,r16} */
switch ( modrm_reg & 7 )
{
case 0: goto add;
@@ -1981,6 +2065,8 @@ x86_emulate(
case 6: goto xor;
case 7:
dst.val = imm1;
+ if ( evex_encoded() )
+ goto unimplemented_insn;
goto cmp;
}
break;
@@ -2650,6 +2736,9 @@ x86_emulate(
break;
case 0xf6 ... 0xf7: /* Grp3 */
+ case X86EMUL_OPC_EVEX (4, 0xf6):
+ case X86EMUL_OPC_EVEX (4, 0xf7):
+ case X86EMUL_OPC_EVEX_66(4, 0xf7):
if ( (d & DstMask) == DstEax )
dst.reg = (unsigned long *)&_regs.r(ax);
switch ( modrm_reg & 7 )
@@ -2659,15 +2748,19 @@ x86_emulate(
case 0 ... 1: /* test */
dst.val = imm1;
dst.bytes = src.bytes;
+ if ( evex_encoded() )
+ goto unimplemented_insn;
goto test;
case 2: /* not */
- if ( ops->rmw && dst.type == OP_MEM )
+ if ( ops->rmw && dst.type == OP_MEM &&
+ (!evex_encoded() || !EVEX_ND(&evex)) )
state->rmw = rmw_not;
else
dst.val = ~dst.val;
break;
case 3: /* neg */
- if ( ops->rmw && dst.type == OP_MEM )
+ if ( ops->rmw && dst.type == OP_MEM &&
+ (!evex_encoded() || !EVEX_ND(&evex)) )
state->rmw = rmw_neg;
else
emulate_1op("neg", dst, _regs.eflags);
@@ -2889,19 +2982,24 @@ x86_emulate(
break;
case 0xfe: /* Grp4 */
+ case X86EMUL_OPC_EVEX (4, 0xfe): /* inc/dec r/m{,r8} */
+ case X86EMUL_OPC_EVEX (4, 0xff): /* inc/dec r/m{,reg} */
+ case X86EMUL_OPC_EVEX_66(4, 0xff): /* inc/dec r/m{,r16} */
generate_exception_if((modrm_reg & 7) >= 2, X86_EXC_UD);
/* Fallthrough. */
case 0xff: /* Grp5 */
switch ( modrm_reg & 7 )
{
case 0: /* inc */
- if ( ops->rmw && dst.type == OP_MEM )
+ if ( ops->rmw && dst.type == OP_MEM &&
+ (!evex_encoded() || !EVEX_ND(&evex)) )
state->rmw = rmw_inc;
else
emulate_1op("inc", dst, _regs.eflags);
break;
case 1: /* dec */
- if ( ops->rmw && dst.type == OP_MEM )
+ if ( ops->rmw && dst.type == OP_MEM &&
+ (!evex_encoded() || !EVEX_ND(&evex)) )
state->rmw = rmw_dec;
else
emulate_1op("dec", dst, _regs.eflags);
@@ -8773,6 +8871,39 @@ x86_emulate(
put_stub(stub);
}
+ if ( state->promoted_evex &&
+ (dst.type != OP_NONE || state->rmw != rmw_NONE) )
+ {
+ if ( EVEX_ND(&evex) )
+ {
+ dst.reg = decode_evex_gpr(evex.reg | (evex.RX << 4), &_regs,
+ &state->egpr, ctxt);
+ dst.type = OP_REG;
+
+ /*
+ * EVEX.nd additionally results in zero-extension even for byte/word
+ * operand size.
+ */
+ switch ( dst.bytes )
+ {
+ case 1:
+ dst.val = (uint8_t)dst.val;
+ fallthrough;
+ case 2:
+ dst.val = (uint16_t)dst.val;
+ dst.bytes = 4;
+ break;
+ }
+ }
+
+ if ( EVEX_NF(&evex) )
+ {
+ /* Squash updates to status flags. */
+ _regs.eflags &= ~EFLAGS_MASK;
+ _regs.eflags |= ctxt->regs->eflags & EFLAGS_MASK;
+ }
+ }
+
switch ( dst.type )
{
case OP_REG:
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 15/30] x86emul: support APX EVEX-encoded shift/rotate insns
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (13 preceding siblings ...)
2026-11-11 14:26 ` [PATCH 14/30] x86emul: support basic APX EVEX-encoded ALU insns Jan Beulich
@ 2026-11-11 14:26 ` Jan Beulich
2026-11-11 14:27 ` [PATCH 16/30] x86emul: support remaining basic APX EVEX-Map4 insns Jan Beulich
` (14 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:26 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
With generic logic having been put in place this is largely mechanical
addition of new table entries / case labels. Pre-existing testcases are
once again cloned into EVEX counterpart forms.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
--- a/tools/tests/x86_emulator/evex-disp8.c
+++ b/tools/tests/x86_emulator/evex-disp8.c
@@ -931,6 +931,86 @@ static const struct test apx_f[] = {
INSN(orw, 66, map4, 0b, el, w, no),
INSNX(orw, 66, map4, 81, 1, el, w, no),
INSNX(orw, 66, map4, 83, 1, el, w, no),
+ INSNX(rcl, , map4, c1, 2, el, dq, no),
+ INSNX(rcl, , map4, d1, 2, el, dq, no),
+ INSNX(rcl, , map4, d3, 2, el, dq, no),
+ INSNX(rclb, , map4, c0, 2, el, b, no),
+ INSNX(rclb, , map4, d0, 2, el, b, no),
+ INSNX(rclb, , map4, d2, 2, el, b, no),
+ INSNX(rclw, 66, map4, c1, 2, el, w, no),
+ INSNX(rclw, 66, map4, d1, 2, el, w, no),
+ INSNX(rclw, 66, map4, d3, 2, el, w, no),
+ INSNX(rcr, , map4, c1, 3, el, dq, no),
+ INSNX(rcr, , map4, d1, 3, el, dq, no),
+ INSNX(rcr, , map4, d3, 3, el, dq, no),
+ INSNX(rcrb, , map4, c0, 3, el, b, no),
+ INSNX(rcrb, , map4, d0, 3, el, b, no),
+ INSNX(rcrb, , map4, d2, 3, el, b, no),
+ INSNX(rcrw, 66, map4, c1, 3, el, w, no),
+ INSNX(rcrw, 66, map4, d1, 3, el, w, no),
+ INSNX(rcrw, 66, map4, d3, 3, el, w, no),
+ INSNX(rol, , map4, c1, 0, el, dq, no),
+ INSNX(rol, , map4, d1, 0, el, dq, no),
+ INSNX(rol, , map4, d3, 0, el, dq, no),
+ INSNX(rolb, , map4, c0, 0, el, b, no),
+ INSNX(rolb, , map4, d0, 0, el, b, no),
+ INSNX(rolb, , map4, d2, 0, el, b, no),
+ INSNX(rolw, 66, map4, c1, 0, el, w, no),
+ INSNX(rolw, 66, map4, d1, 0, el, w, no),
+ INSNX(rolw, 66, map4, d3, 0, el, w, no),
+ INSNX(ror, , map4, c1, 1, el, dq, no),
+ INSNX(ror, , map4, d1, 1, el, dq, no),
+ INSNX(ror, , map4, d3, 1, el, dq, no),
+ INSNX(rorb, , map4, c0, 1, el, b, no),
+ INSNX(rorb, , map4, d0, 1, el, b, no),
+ INSNX(rorb, , map4, d2, 1, el, b, no),
+ INSNX(rorw, 66, map4, c1, 1, el, w, no),
+ INSNX(rorw, 66, map4, d1, 1, el, w, no),
+ INSNX(rorw, 66, map4, d3, 1, el, w, no),
+ INSNX(shl, , map4, c1, 4, el, dq, no),
+ INSNX(shl, , map4, d1, 4, el, dq, no),
+ INSNX(shl, , map4, d3, 4, el, dq, no),
+ INSNX(shlb, , map4, c0, 4, el, b, no),
+ INSNX(shlb, , map4, d0, 4, el, b, no),
+ INSNX(shlb, , map4, d2, 4, el, b, no),
+ INSN(shld, , map4, 24, el, dq, no),
+ INSN(shld, , map4, a5, el, dq, no),
+ INSN(shldw, 66, map4, 24, el, w, no),
+ INSN(shldw, 66, map4, a5, el, w, no),
+ INSNX(shlw, 66, map4, c1, 4, el, w, no),
+ INSNX(shlw, 66, map4, d1, 4, el, w, no),
+ INSNX(shlw, 66, map4, d3, 4, el, w, no),
+ INSNX(shr, , map4, c1, 5, el, dq, no),
+ INSNX(shr, , map4, d1, 5, el, dq, no),
+ INSNX(shr, , map4, d3, 5, el, dq, no),
+ INSNX(shrb, , map4, c0, 5, el, b, no),
+ INSNX(shrb, , map4, d0, 5, el, b, no),
+ INSNX(shrb, , map4, d2, 5, el, b, no),
+ INSN(shrd, , map4, 2c, el, dq, no),
+ INSN(shrd, , map4, ad, el, dq, no),
+ INSN(shrdw, 66, map4, 2c, el, w, no),
+ INSN(shrdw, 66, map4, ad, el, w, no),
+ INSNX(shrw, 66, map4, c1, 5, el, w, no),
+ INSNX(shrw, 66, map4, d1, 5, el, w, no),
+ INSNX(shrw, 66, map4, d3, 5, el, w, no),
+ INSNX(sal, , map4, c1, 6, el, dq, no),
+ INSNX(sal, , map4, d1, 6, el, dq, no),
+ INSNX(sal, , map4, d3, 6, el, dq, no),
+ INSNX(salb, , map4, c0, 6, el, b, no),
+ INSNX(salb, , map4, d0, 6, el, b, no),
+ INSNX(salb, , map4, d2, 6, el, b, no),
+ INSNX(salw, 66, map4, c1, 6, el, w, no),
+ INSNX(salw, 66, map4, d1, 6, el, w, no),
+ INSNX(salw, 66, map4, d3, 6, el, w, no),
+ INSNX(sar, , map4, c1, 7, el, dq, no),
+ INSNX(sar, , map4, d1, 7, el, dq, no),
+ INSNX(sar, , map4, d3, 7, el, dq, no),
+ INSNX(sarb, , map4, c0, 7, el, b, no),
+ INSNX(sarb, , map4, d0, 7, el, b, no),
+ INSNX(sarb, , map4, d2, 7, el, b, no),
+ INSNX(sarw, 66, map4, c1, 7, el, w, no),
+ INSNX(sarw, 66, map4, d1, 7, el, w, no),
+ INSNX(sarw, 66, map4, d3, 7, el, w, no),
INSN(sbb, , map4, 19, el, dq, no),
INSN(sbb, , map4, 1b, el, dq, no),
INSNX(sbb, , map4, 81, 3, el, dq, no),
@@ -993,6 +1073,10 @@ static const uint16_t imm0f[16] = {
* when operand size is 16-bit.
*/
static const uint8_t immM4[256] = {
+ [0x24] = 1,
+ [0x2c] = 1,
+ [0xc0] = 1,
+ [0xc1] = 1,
[0x80] = 1,
[0x81] = 4,
[0x83] = 1,
--- a/tools/tests/x86_emulator/predicates.c
+++ b/tools/tests/x86_emulator/predicates.c
@@ -2161,12 +2161,16 @@ static const struct evex {
{ { 0x22 }, 2, T, R, pfx_no, WIG, L0, ND }, /* and */
{ { 0x23 }, 2, T, R, pfx_no, Wn, L0, ND }, /* and */
{ { 0x23 }, 2, T, R, pfx_66, Wn, L0, ND }, /* and */
+ { { 0x24 }, 3, T, W, pfx_no, Wn, L0, ND }, /* shld */
+ { { 0x24 }, 3, T, W, pfx_66, Wn, L0, ND }, /* shld */
{ { 0x28 }, 2, T, W, pfx_no, WIG, L0, ND }, /* sub */
{ { 0x29 }, 2, T, W, pfx_no, Wn, L0, ND }, /* sub */
{ { 0x29 }, 2, T, W, pfx_66, Wn, L0, ND }, /* sub */
{ { 0x2a }, 2, T, R, pfx_no, WIG, L0, ND }, /* sub */
{ { 0x2b }, 2, T, R, pfx_no, Wn, L0, ND }, /* sub */
{ { 0x2b }, 2, T, R, pfx_66, Wn, L0, ND }, /* sub */
+ { { 0x2c }, 3, T, W, pfx_no, Wn, L0, ND }, /* shrd */
+ { { 0x2c }, 3, T, W, pfx_66, Wn, L0, ND }, /* shrd */
{ { 0x30 }, 2, T, W, pfx_no, WIG, L0, ND }, /* xor */
{ { 0x31 }, 2, T, W, pfx_no, Wn, L0, ND }, /* xor */
{ { 0x31 }, 2, T, W, pfx_66, Wn, L0, ND }, /* xor */
@@ -2215,6 +2219,82 @@ 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 */
+ { { 0xa5 }, 2, T, W, pfx_no, Wn, L0, ND }, /* shld */
+ { { 0xa5 }, 2, T, W, pfx_66, Wn, L0, ND }, /* shld */
+ { { 0xad }, 2, T, W, pfx_no, Wn, L0, ND }, /* shrd */
+ { { 0xad }, 2, T, W, pfx_66, Wn, L0, ND }, /* shrd */
+ { { 0xc0, 0x00 }, 3, T, W, pfx_no, WIG, L0, ND }, /* rol */
+ { { 0xc0, 0x08 }, 3, T, W, pfx_no, WIG, L0, ND }, /* ror */
+ { { 0xc0, 0x10 }, 3, T, W, pfx_no, WIG, L0, ND }, /* rcl */
+ { { 0xc0, 0x18 }, 3, T, W, pfx_no, WIG, L0, ND }, /* rcr */
+ { { 0xc0, 0x20 }, 3, T, W, pfx_no, WIG, L0, ND }, /* shl */
+ { { 0xc0, 0x28 }, 3, T, W, pfx_no, WIG, L0, ND }, /* shr */
+ { { 0xc0, 0x30 }, 3, T, W, pfx_no, WIG, L0, ND }, /* sal */
+ { { 0xc0, 0x38 }, 3, T, W, pfx_no, WIG, L0, ND }, /* sar */
+ { { 0xc1, 0x00 }, 3, T, W, pfx_no, Wn, L0, ND }, /* rol */
+ { { 0xc1, 0x00 }, 3, T, W, pfx_66, Wn, L0, ND }, /* rol */
+ { { 0xc1, 0x08 }, 3, T, W, pfx_no, Wn, L0, ND }, /* ror */
+ { { 0xc1, 0x08 }, 3, T, W, pfx_66, Wn, L0, ND }, /* ror */
+ { { 0xc1, 0x10 }, 3, T, W, pfx_no, Wn, L0, ND }, /* rcl */
+ { { 0xc1, 0x10 }, 3, T, W, pfx_66, Wn, L0, ND }, /* rcl */
+ { { 0xc1, 0x18 }, 3, T, W, pfx_no, Wn, L0, ND }, /* rcr */
+ { { 0xc1, 0x18 }, 3, T, W, pfx_66, Wn, L0, ND }, /* rcr */
+ { { 0xc1, 0x20 }, 3, T, W, pfx_no, Wn, L0, ND }, /* shl */
+ { { 0xc1, 0x20 }, 3, T, W, pfx_66, Wn, L0, ND }, /* shl */
+ { { 0xc1, 0x28 }, 3, T, W, pfx_no, Wn, L0, ND }, /* shr */
+ { { 0xc1, 0x28 }, 3, T, W, pfx_66, Wn, L0, ND }, /* shr */
+ { { 0xc1, 0x30 }, 3, T, W, pfx_no, Wn, L0, ND }, /* sal */
+ { { 0xc1, 0x30 }, 3, T, W, pfx_66, Wn, L0, ND }, /* sal */
+ { { 0xc1, 0x38 }, 3, T, W, pfx_no, Wn, L0, ND }, /* sar */
+ { { 0xc1, 0x38 }, 3, T, W, pfx_66, Wn, L0, ND }, /* sar */
+ { { 0xd0, 0x00 }, 2, T, W, pfx_no, WIG, L0, ND }, /* rol */
+ { { 0xd0, 0x08 }, 2, T, W, pfx_no, WIG, L0, ND }, /* ror */
+ { { 0xd0, 0x10 }, 2, T, W, pfx_no, WIG, L0, ND }, /* rcl */
+ { { 0xd0, 0x18 }, 2, T, W, pfx_no, WIG, L0, ND }, /* rcr */
+ { { 0xd0, 0x20 }, 2, T, W, pfx_no, WIG, L0, ND }, /* shl */
+ { { 0xd0, 0x28 }, 2, T, W, pfx_no, WIG, L0, ND }, /* shr */
+ { { 0xd0, 0x30 }, 2, T, W, pfx_no, WIG, L0, ND }, /* sal */
+ { { 0xd0, 0x38 }, 2, T, W, pfx_no, WIG, L0, ND }, /* sar */
+ { { 0xd1, 0x00 }, 2, T, W, pfx_no, Wn, L0, ND }, /* rol */
+ { { 0xd1, 0x00 }, 2, T, W, pfx_66, Wn, L0, ND }, /* rol */
+ { { 0xd1, 0x08 }, 2, T, W, pfx_no, Wn, L0, ND }, /* ror */
+ { { 0xd1, 0x08 }, 2, T, W, pfx_66, Wn, L0, ND }, /* ror */
+ { { 0xd1, 0x10 }, 2, T, W, pfx_no, Wn, L0, ND }, /* rcl */
+ { { 0xd1, 0x10 }, 2, T, W, pfx_66, Wn, L0, ND }, /* rcl */
+ { { 0xd1, 0x18 }, 2, T, W, pfx_no, Wn, L0, ND }, /* rcr */
+ { { 0xd1, 0x18 }, 2, T, W, pfx_66, Wn, L0, ND }, /* rcr */
+ { { 0xd1, 0x20 }, 2, T, W, pfx_no, Wn, L0, ND }, /* shl */
+ { { 0xd1, 0x20 }, 2, T, W, pfx_66, Wn, L0, ND }, /* shl */
+ { { 0xd1, 0x28 }, 2, T, W, pfx_no, Wn, L0, ND }, /* shr */
+ { { 0xd1, 0x28 }, 2, T, W, pfx_66, Wn, L0, ND }, /* shr */
+ { { 0xd1, 0x30 }, 2, T, W, pfx_no, Wn, L0, ND }, /* sal */
+ { { 0xd1, 0x30 }, 2, T, W, pfx_66, Wn, L0, ND }, /* sal */
+ { { 0xd1, 0x38 }, 2, T, W, pfx_no, Wn, L0, ND }, /* sar */
+ { { 0xd1, 0x38 }, 2, T, W, pfx_66, Wn, L0, ND }, /* sar */
+ { { 0xd2, 0x00 }, 2, T, W, pfx_no, WIG, L0, ND }, /* rol */
+ { { 0xd2, 0x08 }, 2, T, W, pfx_no, WIG, L0, ND }, /* ror */
+ { { 0xd2, 0x10 }, 2, T, W, pfx_no, WIG, L0, ND }, /* rcl */
+ { { 0xd2, 0x18 }, 2, T, W, pfx_no, WIG, L0, ND }, /* rcr */
+ { { 0xd2, 0x20 }, 2, T, W, pfx_no, WIG, L0, ND }, /* shl */
+ { { 0xd2, 0x28 }, 2, T, W, pfx_no, WIG, L0, ND }, /* shr */
+ { { 0xd2, 0x30 }, 2, T, W, pfx_no, WIG, L0, ND }, /* sal */
+ { { 0xd2, 0x38 }, 2, T, W, pfx_no, WIG, L0, ND }, /* sar */
+ { { 0xd3, 0x00 }, 2, T, W, pfx_no, Wn, L0, ND }, /* rol */
+ { { 0xd3, 0x00 }, 2, T, W, pfx_66, Wn, L0, ND }, /* rol */
+ { { 0xd3, 0x08 }, 2, T, W, pfx_no, Wn, L0, ND }, /* ror */
+ { { 0xd3, 0x08 }, 2, T, W, pfx_66, Wn, L0, ND }, /* ror */
+ { { 0xd3, 0x10 }, 2, T, W, pfx_no, Wn, L0, ND }, /* rcl */
+ { { 0xd3, 0x10 }, 2, T, W, pfx_66, Wn, L0, ND }, /* rcl */
+ { { 0xd3, 0x18 }, 2, T, W, pfx_no, Wn, L0, ND }, /* rcr */
+ { { 0xd3, 0x18 }, 2, T, W, pfx_66, Wn, L0, ND }, /* rcr */
+ { { 0xd3, 0x20 }, 2, T, W, pfx_no, Wn, L0, ND }, /* shl */
+ { { 0xd3, 0x20 }, 2, T, W, pfx_66, Wn, L0, ND }, /* shl */
+ { { 0xd3, 0x28 }, 2, T, W, pfx_no, Wn, L0, ND }, /* shr */
+ { { 0xd3, 0x28 }, 2, T, W, pfx_66, Wn, L0, ND }, /* shr */
+ { { 0xd3, 0x30 }, 2, T, W, pfx_no, Wn, L0, ND }, /* sal */
+ { { 0xd3, 0x30 }, 2, T, W, pfx_66, Wn, L0, ND }, /* sal */
+ { { 0xd3, 0x38 }, 2, T, W, pfx_no, Wn, L0, ND }, /* sar */
+ { { 0xd3, 0x38 }, 2, T, W, pfx_66, Wn, L0, ND }, /* sar */
{ { 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 */
--- a/tools/tests/x86_emulator/test_x86_emulator.c
+++ b/tools/tests/x86_emulator/test_x86_emulator.c
@@ -1474,6 +1474,66 @@ int main(int argc, char **argv)
goto fail;
printf("okay\n");
+ printf("%-40s", "Testing rcl $2,(%rdi),%edi...");
+ instr[2] = 0x44; instr[4] = 0xc1; instr[5] = 0x17; instr[6] = 0x02;
+ *res = 0x2233445f;
+ regs.eflags = EFLAGS_ALWAYS_SET | X86_EFLAGS_CF;
+ regs.rip = (unsigned long)&instr[0];
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rdi != ((0x2233445fU << 2) | 2)) ||
+ (*res != 0x2233445f) ||
+ ((regs.eflags & (EFLAGS_MASK & ~X86_EFLAGS_OF))
+ != EFLAGS_ALWAYS_SET) ||
+ (regs.rip != (unsigned long)&instr[7]) )
+ goto fail;
+ printf("okay\n");
+
+ printf("%-40s", "Testing shrd $8,%ecx,(%rdi),%ecx...");
+ instr[2] = 0x74; instr[4] = 0x2c; instr[5] = 0x0f; instr[6] = 0x08;
+ *res = 0x22334455;
+ regs.eflags = EFLAGS_ALWAYS_SET | X86_EFLAGS_CF;
+ regs.rip = (unsigned long)&instr[0];
+ regs.ecx = 0x44332211;
+ regs.rdi = (unsigned long)res;
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rcx != 0x11223344) ||
+ (*res != 0x22334455) ||
+ ((regs.eflags & (EFLAGS_MASK & ~X86_EFLAGS(OF, AF)))
+ != (EFLAGS_ALWAYS_SET | X86_EFLAGS_PF)) ||
+ (regs.rip != (unsigned long)&instr[7]) )
+ goto fail;
+ printf("okay\n");
+
+ printf("%-40s", "Testing shld $1,%ecx,(%rdx),%edx...");
+ *res = 0x12345678;
+ regs.ecx = 0x9abcdef0;
+ instr[2] = 0x6c; instr[4] = 0x24; instr[5] = 0x0a; instr[6] = 0x01;
+ for ( ; ; )
+ {
+ uint32_t r = res[0];
+ const uint32_t m = X86_EFLAGS_ARITH_MASK & ~X86_EFLAGS_AF;
+ unsigned long f;
+
+ regs.rdx = (unsigned long)res;
+ asm ( "shld $1,%2,%0; pushf; pop %1"
+ : "+rm" (r), "=rm" (f) : "r" ((uint32_t)regs.ecx) );
+ regs.eflags = f ^ m;
+ regs.rip = (unsigned long)&instr[0];
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rdx != r) ||
+ (*res != 0x12345678) ||
+ ((regs.eflags ^ f) & m) ||
+ (regs.rip != (unsigned long)&instr[7]) )
+ goto fail;
+ if ( !regs.ecx )
+ break;
+ regs.ecx <<= 1;
+ }
+ printf("okay\n");
+
printf("%-40s", "Testing dec %cx,%ax...");
instr[2] = 0x7d; instr[4] = 0xff; instr[5] = 0xc9;
regs.eflags = EFLAGS_ALWAYS_SET;
--- a/xen/arch/x86/x86_emulate/decode.c
+++ b/xen/arch/x86/x86_emulate/decode.c
@@ -623,6 +623,12 @@ 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 },
},
+ [0x24] = { /* SHLD */
+ [vex_np] = { .to_mem = true, .imm8 = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ [vex_66] = { .to_mem = true, .imm8 = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ },
[0x28] = { /* SUB (byte) */
[vex_np] = { .to_mem = true, .byte = true, .nd = true, .nf = true,
.nci_ndd_nf = true },
@@ -641,6 +647,12 @@ 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 },
},
+ [0x2c] = { /* SHRD */
+ [vex_np] = { .to_mem = true, .imm8 = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ [vex_66] = { .to_mem = true, .imm8 = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ },
[0x30] = { /* XOR (byte) */
[vex_np] = { .to_mem = true, .byte = true, .nd = true, .nf = true,
.nci_ndd_nf = true },
@@ -675,6 +687,50 @@ static const struct map4_table {
[vex_66] = { .to_mem = true, .imm8 = true, .nd = true,
.nci_ndd_nf = true },
},
+ [0xa5] = { /* SHLD */
+ [vex_np] = { .to_mem = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ [vex_66] = { .to_mem = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ },
+ [0xad] = { /* SHRD */
+ [vex_np] = { .to_mem = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ [vex_66] = { .to_mem = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ },
+ [0xc0] = { /* Grp2 (byte, imm8); .nf depends on modrm_reg */
+ [vex_np] = { .to_mem = true, .byte = true, .imm8 = true, .nd = true,
+ .nci_ndd_nf = true },
+ [vex_66] = { .to_mem = true, .byte = true, .imm8 = true, .nd = true,
+ .nci_ndd_nf = true },
+ },
+ [0xc1] = { /* Grp2 ({,d,q}word, imm8); .nf depends on modrm_reg */
+ [vex_np] = { .to_mem = true, .imm8 = true, .nd = true,
+ .nci_ndd_nf = true },
+ [vex_66] = { .to_mem = true, .imm8 = true, .nd = true,
+ .nci_ndd_nf = true },
+ },
+ [0xd0] = { /* Grp2 (byte, 1); .nf depends on modrm_reg */
+ [vex_np] = { .to_mem = true, .byte = true, .nd = true,
+ .nci_ndd_nf = true },
+ [vex_66] = { .to_mem = true, .byte = true, .nd = true,
+ .nci_ndd_nf = true },
+ },
+ [0xd1] = { /* Grp2 ({,d,q}word, 1); .nf depends on modrm_reg */
+ [vex_np] = { .to_mem = true, .nd = true, .nci_ndd_nf = true },
+ [vex_66] = { .to_mem = true, .nd = true, .nci_ndd_nf = true },
+ },
+ [0xd2] = { /* Grp2 (byte, %cl); .nf depends on modrm_reg */
+ [vex_np] = { .to_mem = true, .byte = true, .nd = true,
+ .nci_ndd_nf = true },
+ [vex_66] = { .to_mem = true, .byte = true, .nd = true,
+ .nci_ndd_nf = true },
+ },
+ [0xd3] = { /* Grp2 ({,d,q}word, %cl); .nf depends on modrm_reg */
+ [vex_np] = { .to_mem = true, .nd = true },
+ [vex_66] = { .to_mem = true, .nd = true },
+ },
[0xf6] = { /* Grp3 (byte); most attributes depend on modrm_reg */
[vex_np] = { .byte = true, .nci_ndd_nf = true },
},
@@ -1222,6 +1278,11 @@ decode_map4(struct x86_emulate_state *s,
nf = map4_table[(s->modrm_reg & 7) << 3][s->evex.pfx].nf;
break;
+ case 0xc0: case 0xc1: case 0xd0 ... 0xd3: /* Grp2 */
+ /* rcl/rcr do not allow EVEX.nf to be set. */
+ nf = (s->modrm_reg & 6) != 2;
+ break;
+
case 0xf6: case 0xf7: /* Grp3 */
switch ( s->modrm_reg & 7 )
{
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -2442,14 +2442,19 @@ x86_emulate(
dst.val = src.val;
break;
- case 0xc0 ... 0xc1: grp2: /* Grp2 */
+ case 0xc0 ... 0xc1: /* Grp2 */
+ case X86EMUL_OPC_EVEX (4, 0xc0): /* <op> imm8,r/m{,r8} */
+ case X86EMUL_OPC_EVEX (4, 0xc1): /* <op> imm8,r/m{,reg} */
+ case X86EMUL_OPC_EVEX_66(4, 0xc1): /* <op> imm8,r/m{,r16} */
+ grp2:
generate_exception_if(lock_prefix, X86_EXC_UD);
switch ( modrm_reg & 7 )
{
#define GRP2(name, ext) \
case ext: \
- if ( ops->rmw && dst.type == OP_MEM ) \
+ if ( ops->rmw && dst.type == OP_MEM && \
+ (!evex_encoded() || !EVEX_ND(&evex)) ) \
state->rmw = rmw_##name; \
else \
emulate_2op_SrcB(#name, src, dst, _regs.eflags); \
@@ -2609,10 +2614,16 @@ x86_emulate(
}
case 0xd0 ... 0xd1: /* Grp2 */
+ case X86EMUL_OPC_EVEX (4, 0xd0): /* <op> $1,r/m{,r8} */
+ case X86EMUL_OPC_EVEX (4, 0xd1): /* <op> $1,r/m{,reg} */
+ case X86EMUL_OPC_EVEX_66(4, 0xd1): /* <op> $1,r/m{,r16} */
src.val = 1;
goto grp2;
case 0xd2 ... 0xd3: /* Grp2 */
+ case X86EMUL_OPC_EVEX (4, 0xd2): /* <op> %cl,r/m{,r8} */
+ case X86EMUL_OPC_EVEX (4, 0xd3): /* <op> %cl,r/m{,reg} */
+ case X86EMUL_OPC_EVEX_66(4, 0xd3): /* <op> %cl,r/m{,r16} */
src.val = _regs.cl;
goto grp2;
@@ -5335,7 +5346,15 @@ x86_emulate(
case X86EMUL_OPC(0x0f, 0xa4): /* shld imm8,r,r/m */
case X86EMUL_OPC(0x0f, 0xa5): /* shld %%cl,r,r/m */
case X86EMUL_OPC(0x0f, 0xac): /* shrd imm8,r,r/m */
- case X86EMUL_OPC(0x0f, 0xad): /* shrd %%cl,r,r/m */ {
+ case X86EMUL_OPC(0x0f, 0xad): /* shrd %%cl,r,r/m */
+ case X86EMUL_OPC_EVEX (4, 0x24): /* shld imm8,r/m{,reg} */
+ case X86EMUL_OPC_EVEX_66(4, 0x24): /* shld imm8,r/m{,r16} */
+ case X86EMUL_OPC_EVEX (4, 0x2c): /* shrd imm8,r/m{,reg} */
+ case X86EMUL_OPC_EVEX_66(4, 0x2c): /* shrd imm8,r/m{,r16} */
+ case X86EMUL_OPC_EVEX (4, 0xa5): /* shld %cl,r/m{,reg} */
+ case X86EMUL_OPC_EVEX_66(4, 0xa5): /* shld %cl,r/m{,r16} */
+ case X86EMUL_OPC_EVEX (4, 0xad): /* shrd %cl,r/m{,reg} */
+ case X86EMUL_OPC_EVEX_66(4, 0xad): /* shrd %cl,r/m{,r16} */ {
uint8_t shift;
generate_exception_if(lock_prefix, X86_EXC_UD);
@@ -5349,7 +5368,8 @@ x86_emulate(
src.val = truncate_word(*src.reg, dst.bytes);
}
- if ( ops->rmw && dst.type == OP_MEM )
+ if ( ops->rmw && dst.type == OP_MEM &&
+ (!evex_encoded() || !EVEX_ND(&evex)) )
{
ea.orig_val = shift;
state->rmw = b & 8 ? rmw_shrd : rmw_shld;
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 16/30] x86emul: support remaining basic APX EVEX-Map4 insns
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (14 preceding siblings ...)
2026-11-11 14:26 ` [PATCH 15/30] x86emul: support APX EVEX-encoded shift/rotate insns Jan Beulich
@ 2026-11-11 14:27 ` Jan Beulich
2026-11-11 14:27 ` [PATCH 17/30] x86emul: support APX SCC insns Jan Beulich
` (13 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:27 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
That is all which neither have a dependency on another CPUID bit nor
use the SCC sub-form of EVEX. I.e. remaining IMUL forms, CRC32, SETcc,
{,CF}CMOVcc, PUSH2, and POP2.
Note that the ZU forms of IMUL and SETcc are handled by re-using the
common EVEX.nd handling, via use of the (otherwise unused) EVEX.V[0-4]
bits.
To cover in particular PUSH2/POP2, build yet another blowfish flavor.
For PUSH2/POP2 to actually be usable the entry stub needs to adjust the
stack by one slot.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
---
An alternative to the HAVE_AS_SSE4_2 hackery would of course be to
determine the compiler capability in the test harness'es makefile,
and to define the symbol there accordingly.
--- a/tools/tests/x86_emulator/Makefile
+++ b/tools/tests/x86_emulator/Makefile
@@ -40,7 +40,7 @@ else
blowfish-cflags := ""
blowfish-cflags-x86_32 := "-mno-accumulate-outgoing-args -Dstatic="
-blowfish-cflags-x86_64 := "-DREX2 -Dstatic="
+blowfish-cflags-x86_64 := "-DREX2 -Dstatic=" "-mapxf -Dstatic="
3dnow-vecs := 8
3dnow-ints :=
--- a/tools/tests/x86_emulator/blowfish.c
+++ b/tools/tests/x86_emulator/blowfish.c
@@ -29,7 +29,9 @@ asm (
"addl $8,%esp; "
#else
"shlq $32,%rdx; movl %eax,%edi; orq %rdx,%rdi; "
+ "push %rcx; " /* Maintain ABI-conforming stack. */
"call blowfish_test; "
+ "pop %rcx; "
"movq %rax,%rdx; movl %eax,%eax; shrq $32,%rdx; "
#endif
"ret"
--- a/tools/tests/x86_emulator/evex-disp8.c
+++ b/tools/tests/x86_emulator/evex-disp8.c
@@ -896,6 +896,9 @@ static const struct test apx_f[] = {
INSN(andw, 66, map4, 23, el, w, no),
INSNX(andw, 66, map4, 81, 4, el, w, no),
INSNX(andw, 66, map4, 83, 4, el, w, no),
+ INSN(crc32, , map4, f1, el, dq, no),
+ INSN(crc32b, , map4, f0, el, b, no),
+ INSN(crc32w, 66, map4, f1, el, w, no),
INSNX(dec, , map4, ff, 1, el, dq, no),
INSNX(decb, , map4, fe, 1, el, b, no),
INSNX(decw, 66, map4, ff, 1, el, w, no),
@@ -905,8 +908,14 @@ static const struct test apx_f[] = {
INSNX(idiv, , map4, f7, 7, el, dq, no),
INSNX(idivb, , map4, f6, 7, el, b, no),
INSNX(idivw, 66, map4, f7, 7, el, w, no),
+ INSN(imul, , map4, 69, el, dq, no),
+ INSN(imul, , map4, 6b, el, dq, no),
+ INSN(imul, , map4, af, el, dq, no),
INSNX(imul, , map4, f7, 5, el, dq, no),
INSNX(imulb, , map4, f6, 5, el, b, no),
+ INSN(imulw, 66, map4, 69, el, w, no),
+ INSN(imulw, 66, map4, 6b, el, w, no),
+ INSN(imulw, 66, map4, af, el, w, no),
INSNX(imulw, 66, map4, f7, 5, el, w, no),
INSNX(inc, , map4, ff, 0, el, dq, no),
INSNX(incb, , map4, fe, 0, el, b, no),
@@ -931,6 +940,8 @@ static const struct test apx_f[] = {
INSN(orw, 66, map4, 0b, el, w, no),
INSNX(orw, 66, map4, 81, 1, el, w, no),
INSNX(orw, 66, map4, 83, 1, el, w, no),
+// pop2, , map4, 8f, 0, o
+// push2, , map4, ff, 6, o
INSNX(rcl, , map4, c1, 2, el, dq, no),
INSNX(rcl, , map4, d1, 2, el, dq, no),
INSNX(rcl, , map4, d3, 2, el, dq, no),
@@ -1046,6 +1057,30 @@ static const struct test apx_f[] = {
INSNX(xorw, 66, map4, 83, 6, el, w, no),
};
+/*
+ * Of the three features covered by APX-NCI-NDD-NF, the latter two only cover
+ * specific insn forms. Treat NCI as a separate feature for the purpose here.
+ */
+#define CONDS() \
+ COND( a, 7), COND( c, 2), COND( g, 15), COND( l, 12), \
+ COND(na, 6), COND(nc, 3), COND(ng, 14), COND(nl, 13), \
+ COND(no, 1), COND(np, 11), COND(ns, 9), COND(nz, 5), \
+ COND( o, 0), COND( p, 10), COND( s, 8), COND( z, 4)
+
+static const struct test apx_nci[] = {
+#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
+#define COND(c, n) \
+ INSN(set##c, f2, map4, 40 | (n), el, b, no)
+ CONDS(),
+#undef COND
+};
+
+#undef CONDS
+
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 };
@@ -1075,6 +1110,8 @@ static const uint16_t imm0f[16] = {
static const uint8_t immM4[256] = {
[0x24] = 1,
[0x2c] = 1,
+ [0x69] = 4,
+ [0x6b] = 1,
[0xc0] = 1,
[0xc1] = 1,
[0x80] = 1,
@@ -1265,6 +1302,7 @@ static void test_one(const struct test *
* without extra conditionals.
*/
instr[0] = 0x62;
+ again:
instr[1] = evex.raw[0];
instr[2] = evex.raw[1];
instr[3] = evex.raw[2];
@@ -1284,8 +1322,28 @@ static void test_one(const struct test *
}
else
{
+ static const uint16_t eflags[16] = {
+ [0x0 /* o */] = X86_EFLAGS_OF,
+ [0x2 /* c */] = X86_EFLAGS_CF,
+ [0x4 /* z */] = X86_EFLAGS_ZF,
+ [0x6 /* na */] = X86_EFLAGS_CF,
+ [0x8 /* s */] = X86_EFLAGS_SF,
+ [0xa /* p */] = X86_EFLAGS_PF,
+ [0xc /* l */] = X86_EFLAGS_SF,
+ [0xe /* ng */] = X86_EFLAGS_ZF,
+ };
+
switch ( test->opc )
{
+ case 0x40 ... 0x4f:
+ /*
+ * CFCMOVcc needs specific EFLAGS values for memory accesses to
+ * actually occur.
+ */
+ ctxt->regs->eflags = eflags[test->opc & 0xf] |
+ X86_EFLAGS_MBS | X86_EFLAGS_IF;
+ break;
+
case 0xf6:
/*
* For APX 8-bit DIV/IDIV %ah needs to be less than what read()
@@ -1326,7 +1384,21 @@ static void test_one(const struct test *
/* Also check the broadcast case, if available. */
if ( test->vsz >= VSZ_el || test->scale != SC_vl )
- return;
+ {
+ /*
+ * {CF,}CMOVcc really wants testing in all four flavors, controlled
+ * by EVEX.nd (aliasing EVEX.b, i.e. evex.bcst) and EVEX.nf (aliasing
+ * the top bit of EVEX.aaa, i.e. evex.opmsk).
+ */
+ if ( test->spc != SPC_map4 || (test->opc | 0xf) != 0x4f ||
+ test->pfx > PFX_66 || (evex.bcst && evex.opmsk) )
+ return;
+
+ evex.bcst = !evex.bcst;
+ if ( !evex.bcst )
+ evex.opmsk ^= 4;
+ goto again;
+ }
switch ( test->esz )
{
@@ -1558,6 +1630,11 @@ void evex_disp8_test(void *instr, struct
vl_128, ARRAY_SIZE(vl_128), instr, ctxt);
printf("okay\n");
+ printf("%-40s", "Testing APX-NCI-NDD-NF disp8 handling...");
+ test_group(apx_nci, ARRAY_SIZE(apx_nci),
+ vl_128, ARRAY_SIZE(vl_128), instr, ctxt);
+ printf("okay\n");
+
cpu_policy.apx.apx_nci_ndd_nf = nci_ndd_nf;
ctxt->force_writeback = wb;
}
--- a/tools/tests/x86_emulator/predicates.c
+++ b/tools/tests/x86_emulator/predicates.c
@@ -873,7 +873,6 @@ static const struct {
{ { 0xf8 }, { 2, 2 }, F, W, pfx_f2 }, /* enqcmd */
{ { 0xf9 }, { 2, 2 }, F, W }, /* movdiri */
};
-#undef CND
#undef REG
static const struct {
@@ -2177,6 +2176,14 @@ 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 */
+ 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> */
+ { { 0x69 }, 6, T, R, pfx_no, Wn, L0 }, /* imul */
+ { { 0x69 }, 4, T, R, pfx_66, W0, L0 }, /* imul */
+ { { 0x69 }, 6, T, R, pfx_66, W1, L0 }, /* imul */
+ { { 0x6b }, 3, T, R, pfx_no, Wn, L0 }, /* imul */
+ { { 0x6b }, 3, T, R, pfx_66, Wn, L0 }, /* imul */
{ { 0x80, 0x00 }, 3, T, W, pfx_no, WIG, L0, ND }, /* add */
{ { 0x80, 0x08 }, 3, T, W, pfx_no, WIG, L0, ND }, /* or */
{ { 0x80, 0x10 }, 3, T, W, pfx_no, WIG, L0, ND }, /* adc */
@@ -2219,10 +2226,13 @@ 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 */
+ { { 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 */
{ { 0xad }, 2, T, W, pfx_no, Wn, L0, ND }, /* shrd */
{ { 0xad }, 2, T, W, pfx_66, Wn, L0, ND }, /* shrd */
+ { { 0xaf }, 2, T, R, pfx_no, Wn, L0, ND }, /* imul */
+ { { 0xaf }, 2, T, R, pfx_66, Wn, L0, ND }, /* imul */
{ { 0xc0, 0x00 }, 3, T, W, pfx_no, WIG, L0, ND }, /* rol */
{ { 0xc0, 0x08 }, 3, T, W, pfx_no, WIG, L0, ND }, /* ror */
{ { 0xc0, 0x10 }, 3, T, W, pfx_no, WIG, L0, ND }, /* rcl */
@@ -2295,6 +2305,9 @@ static const struct evex {
{ { 0xd3, 0x30 }, 2, T, W, pfx_66, Wn, L0, ND }, /* sal */
{ { 0xd3, 0x38 }, 2, T, W, pfx_no, Wn, L0, ND }, /* sar */
{ { 0xd3, 0x38 }, 2, T, W, pfx_66, Wn, L0, ND }, /* sar */
+ { { 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, 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 */
@@ -2319,6 +2332,7 @@ static const struct evex {
{ { 0xff, 0x00 }, 2, T, W, pfx_66, Wn, L0, ND }, /* inc */
{ { 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 ND
#endif /* __x86_64__ */
}, evex_map5[] = {
@@ -2490,6 +2504,8 @@ static const struct evex {
{ { 0xd7 }, 2, T, R, pfx_f2, W0, LIG }, /* vfcmulcsh */
};
+#undef CND
+
static const struct {
const struct evex *tbl;
unsigned int num;
@@ -2889,7 +2905,26 @@ void predicates_test(void *instr, struct
do_test(instr, evex[x].tbl[t].len + ((void *)ptr - instr),
evex[x].tbl[t].modrm ? (void *)ptr - instr + 1 : 0,
- mem_read, ctxt, fetch);
+ evex[x].tbl[t].modrm ? mem_read : evex[x].tbl[t].mem,
+ ctxt, fetch);
+
+ /* Also test 3-operand and store form of CFCMOVcc. */
+ if ( (evex[x].tbl[t].opc[0] | 0xf) == 0x4f )
+ {
+ ptr[-1] |= 0x04; /* Set EVEX.nf. */
+ memcpy(ptr, evex[x].tbl[t].opc, evex[x].tbl[t].len);
+ do_test(instr,
+ evex[x].tbl[t].len + ((void *)ptr - instr),
+ (void *)ptr - instr + 1, mem_read, ctxt,
+ fetch);
+
+ ptr[-1] &= ~0x10; /* Clear EVEX.nd. */
+ memcpy(ptr, evex[x].tbl[t].opc, evex[x].tbl[t].len);
+ do_test(instr,
+ evex[x].tbl[t].len + ((void *)ptr - instr),
+ (void *)ptr - instr + 1, mem_write, ctxt,
+ fetch);
+ }
}
}
}
--- a/tools/tests/x86_emulator/test_x86_emulator.c
+++ b/tools/tests/x86_emulator/test_x86_emulator.c
@@ -129,6 +129,11 @@ static bool check_apx_f(void)
{
return cpu_has_apx_f;
}
+
+static bool check_apx_nci_ndd_nf(void)
+{
+ return cpu_has_apx_nci_ndd_nf;
+}
#endif /* __x86_64__ */
static bool simd_check__3dnow(void)
@@ -384,6 +389,13 @@ static const struct {
.check_cpu = check_apx_f,
.set_regs = blowfish_rex2_set_regs,
.check_regs = blowfish_rex2_check_regs },
+ { .code = blowfish_x86_64_mapxf,
+ .size = sizeof(blowfish_x86_64_mapxf),
+ .bitness = 64,
+ .name = "blowfish (APX)",
+ .check_cpu = check_apx_nci_ndd_nf,
+ .set_regs = blowfish_set_regs,
+ .check_regs = blowfish_check_regs },
#endif
BLOWFISH(32, blowfish, ),
BLOWFISH(32, blowfish (push), _mno_accumulate_outgoing_args),
@@ -1566,6 +1578,102 @@ int main(int argc, char **argv)
goto fail;
printf("okay\n");
+ printf("%-40s", "Testing {nf}imul $3,-4(%rdx),%ecx...");
+ instr[4] = 0x6b; instr[5] = 0x4a; instr[7] = 0x03;
+ regs.rip = (unsigned long)&instr[0];
+ regs.ecx = 0x12345678;
+ *res = 0x89abcdef;
+ regs.rdx = (unsigned long)(res + 1);
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rcx != 0x89abcdefU * 3) ||
+ (regs.eflags != EFLAGS_ALWAYS_SET) ||
+ (regs.rip != (unsigned long)&instr[8]) )
+ goto fail;
+ printf("okay\n");
+
+ printf("%-40s", "Testing cmovz %cx,%dx,%ax...");
+ instr[2] = 0x7d; instr[3] = 0x18;
+ instr[4] = 0x44; instr[5] = 0xd1;
+ regs.rip = (unsigned long)&instr[0];
+ regs.rax = 0x1111111122222222;
+ regs.rcx = 0x3333333344444444;
+ regs.rdx = 0x5555555566666666;
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rax != 0x0000000000006666) ||
+ (regs.rcx != 0x3333333344444444) ||
+ (regs.rdx != 0x5555555566666666) ||
+ (regs.eflags != EFLAGS_ALWAYS_SET) ||
+ (regs.rip != (unsigned long)&instr[6]) )
+ goto fail;
+ printf("okay\n");
+
+ printf("%-40s", "Testing cfcmovz %ecx,4(%rax)...");
+ instr[2] = 0x7c; instr[3] = 0x0c;
+ instr[4] = 0x44; instr[5] = 0x48; instr[6] = 0x04;
+ regs.rax = (unsigned long)res;
+ for ( regs.eflags = EFLAGS_ALWAYS_SET | X86_EFLAGS_ZF; ; )
+ {
+ regs.rip = (unsigned long)&instr[0];
+ memset(res, 0x55, 16);
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (res[0] != 0x55555555) ||
+ (res[1] != (regs.eflags & X86_EFLAGS_ZF ? 0x44444444
+ : 0x55555555)) ||
+ (res[2] != 0x55555555) ||
+ (res[3] != 0x55555555) ||
+ (regs.rip != (unsigned long)&instr[7]) )
+ goto fail;
+
+ if ( !(regs.eflags & X86_EFLAGS_ZF) )
+ break;
+ regs.rax ^= 1ULL << 63;
+ regs.eflags ^= X86_EFLAGS_ZF;
+ }
+ printf("okay\n");
+
+ printf("%-40s", "Testing cfcmovs 4(%rax),%ecx...");
+ instr[3] = 0x08; instr[4] = 0x48;
+ regs.rax = (unsigned long)res;
+ for ( regs.eflags = EFLAGS_ALWAYS_SET | X86_EFLAGS_SF; ; )
+ {
+ regs.rip = (unsigned long)&instr[0];
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rcx != (regs.eflags & X86_EFLAGS_SF ? 0x55555555 : 0)) ||
+ (regs.rip != (unsigned long)&instr[7]) )
+ goto fail;
+
+ if ( !(regs.eflags & X86_EFLAGS_SF) )
+ break;
+ regs.rax ^= 1ULL << 63;
+ regs.eflags ^= X86_EFLAGS_SF;
+ }
+ printf("okay\n");
+
+ printf("%-40s", "Testing cfcmovp 4(%rax),%edx,%ecx...");
+ instr[2] = 0x74; instr[3] = 0x1c;
+ instr[4] = 0x4a; instr[5] = 0x50;
+ regs.rax = (unsigned long)res;
+ for ( regs.eflags = EFLAGS_ALWAYS_SET | X86_EFLAGS_PF; ; )
+ {
+ regs.rip = (unsigned long)&instr[0];
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rcx != (regs.eflags & X86_EFLAGS_PF ? 0x55555555
+ : 0x66666666)) ||
+ (regs.rip != (unsigned long)&instr[7]) )
+ goto fail;
+
+ if ( !(regs.eflags & X86_EFLAGS_PF) )
+ break;
+ regs.rax ^= 1ULL << 63;
+ regs.eflags ^= X86_EFLAGS_PF;
+ }
+ printf("okay\n");
+
cpu_policy.apx.apx_nci_ndd_nf = nci_ndd_nf;
}
#endif
--- a/tools/tests/x86_emulator/x86-emulate.h
+++ b/tools/tests/x86_emulator/x86-emulate.h
@@ -240,6 +240,9 @@ void wrpkru(unsigned int val);
#define cpu_has_avx512_bmm (cpu_policy.extd.avx512_bmm && \
xcr0_mask(0xe6))
+#define cpu_has_apx_nci_ndd_nf (cpu_policy.apx.apx_nci_ndd_nf && \
+ xcr0_mask(X86_XCR0_APX))
+
int emul_test_cpuid(
uint32_t leaf,
uint32_t subleaf,
--- a/xen/arch/x86/x86_emulate/decode.c
+++ b/xen/arch/x86/x86_emulate/decode.c
@@ -671,6 +671,24 @@ 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 },
},
+ [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 },
+ [vex_f2] = { .to_mem = true, .byte = true, .nd = true,
+ .nci_ndd_nf = true },
+ },
+ [0x69] = { /* IMUL */
+ [vex_np] = { .immW = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ [vex_66] = { .immW = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ },
+ [0x6b] = { /* IMUL */
+ [vex_np] = { .imm8 = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ [vex_66] = { .imm8 = true, .nd = true, .nf = true,
+ .nci_ndd_nf = true },
+ },
[0x80] = { /* Grp1 (byte); .nf depends on modrm_reg */
[vex_np] = { .to_mem = true, .byte = true, .imm8 = true, .nd = true,
.nci_ndd_nf = true },
@@ -687,6 +705,9 @@ static const struct map4_table {
[vex_66] = { .to_mem = true, .imm8 = true, .nd = true,
.nci_ndd_nf = true },
},
+ [0x8f] = { /* Grp1a */
+ [vex_np] = { .nd = true },
+ },
[0xa5] = { /* SHLD */
[vex_np] = { .to_mem = true, .nd = true, .nf = true,
.nci_ndd_nf = true },
@@ -699,6 +720,10 @@ static const struct map4_table {
[vex_66] = { .to_mem = true, .nd = true, .nf = true,
.nci_ndd_nf = true },
},
+ [0xaf] = { /* IMUL */
+ [vex_np] = { .nd = true, .nf = true, .nci_ndd_nf = true },
+ [vex_66] = { .nd = true, .nf = true, .nci_ndd_nf = true },
+ },
[0xc0] = { /* Grp2 (byte, imm8); .nf depends on modrm_reg */
[vex_np] = { .to_mem = true, .byte = true, .imm8 = true, .nd = true,
.nci_ndd_nf = true },
@@ -731,6 +756,13 @@ static const struct map4_table {
[vex_np] = { .to_mem = true, .nd = true },
[vex_66] = { .to_mem = true, .nd = true },
},
+ [0xf0] = { /* CRC32 (byte) */
+ [vex_np] = { .byte = true },
+ },
+ [0xf1] = { /* CRC32 ({,d,q}word) */
+ [vex_np] = { },
+ [vex_66] = { },
+ },
[0xf6] = { /* Grp3 (byte); most attributes depend on modrm_reg */
[vex_np] = { .byte = true, .nci_ndd_nf = true },
},
@@ -1265,6 +1297,22 @@ decode_map4(struct x86_emulate_state *s,
switch ( ctxt->opcode & 0xff )
{
+ case 0x40 ... 0x4f:
+ if ( s->evex.pfx <= vex_66 ) /* {,cf}cmov<cc> */
+ {
+ if ( !EVEX_ND(&s->evex) && EVEX_NF(&s->evex) )
+ /*
+ * While the memory operand is only written if the condition
+ * is true, help x86_insn_is_mem_write() by setting DstMem.
+ */
+ s->desc = DstMem | SrcReg;
+ else if ( !EVEX_ND(&s->evex) || EVEX_NF(&s->evex) )
+ /* Memory operand is only read if the condition is true. */
+ s->desc = DstReg | SrcNone;
+ }
+ s->desc |= Mov;
+ break;
+
case 0x80: case 0x81: case 0x83: /* Grp1 */
if ( (s->modrm_reg & 7) == 7 ) /* ccmp */
{
@@ -1283,6 +1331,15 @@ decode_map4(struct x86_emulate_state *s,
nf = (s->modrm_reg & 6) != 2;
break;
+ case 0x8f: /* Grp1a */
+ switch ( s->modrm_reg & 7 )
+ {
+ case 0: /* pop2 */
+ s->desc |= Mov;
+ break;
+ }
+ break;
+
case 0xf6: case 0xf7: /* Grp3 */
switch ( s->modrm_reg & 7 )
{
@@ -1305,6 +1362,16 @@ decode_map4(struct x86_emulate_state *s,
break;
}
break;
+
+ case 0xff: /* Grp5 */
+ switch ( s->modrm_reg & 7 )
+ {
+ case 6: /* push2 */
+ s->desc |= Mov;
+ nf = false;
+ break;
+ }
+ break;
}
generate_exception_if((scc || nci_ndd_nf) && !vcpu_has_apx_nci_ndd_nf(),
--- a/xen/arch/x86/x86_emulate/util.c
+++ b/xen/arch/x86/x86_emulate/util.c
@@ -156,13 +156,15 @@ bool cf_check x86_insn_is_mem_access(con
case X86EMUL_OPC(0x0f, 0xf7): /* MASKMOVQ */
case X86EMUL_OPC_66(0x0f, 0xf7): /* MASKMOVDQU */
case X86EMUL_OPC_VEX_66(0x0f, 0xf7): /* VMASKMOVDQU */
+ case X86EMUL_OPC_EVEX(4, 0x8f): /* POP2 */
return true;
case 0xff:
+ case X86EMUL_OPC_EVEX(4, 0xff):
switch ( s->modrm_reg & 7 )
{
case 2: /* CALL (near, indirect) */
- case 6: /* PUSH r/m */
+ case 6: /* PUSH r/m, PUSH2 */
return true;
}
break;
@@ -196,6 +198,9 @@ bool cf_check x86_insn_is_mem_write(cons
case DstMem:
return (s->modrm_mod != 3 &&
(!s->promoted_evex || !EVEX_ND(&s->evex))) ||
+ /* PUSH2 is special. */
+ (ctxt->opcode == X86EMUL_OPC_EVEX(4, 0xff) &&
+ (s->modrm_reg & 7) == 6) ||
/* This is to cover {,V}MASKMOV{Q,DQU}. */
(s->desc & SrcMask) == SrcMem;
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -1947,6 +1947,18 @@ x86_emulate(
dst.mem.off = sp_pre_dec(dst.bytes);
break;
+ case X86EMUL_OPC_EVEX (4, 0x69): /* imul imm,r/m,reg */
+ case X86EMUL_OPC_EVEX_66(4, 0x69): /* imul{,zu} imm,r/m,r16 */
+ case X86EMUL_OPC_EVEX (4, 0x6b): /* imul imm8,r/m,reg */
+ case X86EMUL_OPC_EVEX_66(4, 0x6b): /* imul{,zu} imm8,r/m,r16 */
+ generate_exception_if(evex.reg != 0xf || !evex.RX, X86_EXC_UD);
+ /*
+ * For IMULZU engage common EVEX.nd handling, requiring to put
+ * the destination register in EVEX.V[0-4].
+ */
+ evex.reg = ~modrm_reg;
+ evex.RX = evex.r4;
+ fallthrough;
case 0x69: /* imul imm16/32 */
case 0x6b: /* imul imm8 */
if ( ea.type == OP_REG )
@@ -2992,9 +3004,29 @@ x86_emulate(
_regs.eflags |= X86_EFLAGS_DF;
break;
+ case X86EMUL_OPC_EVEX(4, 0xff): /* Grp5 */
+ if ( (modrm_reg & 7) == 6 ) /* push2 */
+ {
+ generate_exception_if((src.type != OP_REG || !EVEX_ND(&evex) ||
+ EVEX_NF(&evex)),
+ X86_EXC_UD);
+ n = (~evex.reg & 0xf) | (!evex.RX << 4);
+ generate_exception_if(modrm_rm == 4 || n == 4, X86_EXC_UD);
+ generate_exception_if(_regs.r(sp) & 0xf, X86_EXC_GP, 0);
+ fail_if(!ops->write);
+ if ( (rc = ops->write(x86_seg_ss, sp_pre_dec(8),
+ decode_evex_gpr(evex.reg | (evex.RX << 4),
+ &_regs, &state->egpr, ctxt),
+ 8, ctxt)) != X86EMUL_OKAY ||
+ (rc = ops->write(x86_seg_ss, sp_pre_dec(8), &dst.val, 8,
+ ctxt)) != X86EMUL_OKAY )
+ goto done;
+ dst.type = OP_NONE;
+ break;
+ }
+ fallthrough;
case 0xfe: /* Grp4 */
case X86EMUL_OPC_EVEX (4, 0xfe): /* inc/dec r/m{,r8} */
- case X86EMUL_OPC_EVEX (4, 0xff): /* inc/dec r/m{,reg} */
case X86EMUL_OPC_EVEX_66(4, 0xff): /* inc/dec r/m{,r16} */
generate_exception_if((modrm_reg & 7) >= 2, X86_EXC_UD);
/* Fallthrough. */
@@ -4164,6 +4196,59 @@ x86_emulate(
singlestep = _regs.eflags & X86_EFLAGS_TF;
break;
+ case X86EMUL_OPC_EVEX (4, 0x40) ...
+ X86EMUL_OPC_EVEX (4, 0x4f): /* {,cf}cmovcc */
+ case X86EMUL_OPC_EVEX_66(4, 0x40) ...
+ X86EMUL_OPC_EVEX_66(4, 0x4f): /* {,cf}cmovcc */
+ if ( !EVEX_ND(&evex) )
+ {
+ if ( EVEX_NF(&evex) ) /* cfcmovcc reg,rm */
+ {
+ /*
+ * Incoming:
+ * - dst.val set from modrm_rm (if modrm_mod == 3),
+ * - src.val set from modrm_reg.
+ */
+ if ( test_cc(b, _regs.eflags) )
+ dst.val = src.val;
+ else if ( dst.type == OP_REG )
+ dst.val = 0;
+ else
+ dst.type = OP_NONE;
+ }
+ else /* cfcmovcc rm,reg */
+ {
+ /* Incoming: dst.val set from modrm_reg. */
+ if ( !test_cc(b, _regs.eflags) )
+ dst.val = 0;
+ else if ( ea.type != OP_MEM )
+ dst.val = *decode_gpr(&_regs, &state->egpr, modrm_rm);
+ else if ( (rc = read_ulong(ea.mem.seg, ea.mem.off, &dst.val,
+ op_bytes, ctxt, ops)) != X86EMUL_OKAY )
+ goto done;
+ }
+
+ /* Upper bits of the destination register are uniformly zeroed. */
+ if ( dst.bytes == 2 && dst.type == OP_REG )
+ {
+ dst.val = (uint16_t)dst.val;
+ dst.bytes = 4;
+ }
+ break;
+ }
+ if ( EVEX_NF(&evex) ) /* cfcmovcc rm,reg,reg */
+ {
+ /* Incoming: dst.val set from modrm_reg. */
+ if ( !test_cc(b, _regs.eflags) )
+ break;
+ if ( ea.type != OP_MEM )
+ dst.val = *decode_gpr(&_regs, &state->egpr, modrm_rm);
+ else if ( (rc = read_ulong(ea.mem.seg, ea.mem.off, &dst.val,
+ op_bytes, ctxt, ops)) != X86EMUL_OKAY )
+ goto done;
+ break;
+ }
+ fallthrough;
case X86EMUL_OPC(0x0f, 0x40) ... X86EMUL_OPC(0x0f, 0x4f): /* cmovcc */
vcpu_must_have(cmov);
if ( test_cc(b, _regs.eflags) )
@@ -5144,6 +5229,21 @@ x86_emulate(
adjust_bnd(state, ctxt, ops, vex.pfx);
break;
+ case X86EMUL_OPC_EVEX_F2(4, 0x40) ...
+ X86EMUL_OPC_EVEX_F2(4, 0x4f): /* set{,zu}cc */
+ generate_exception_if(evex.reg != 0xf || !evex.RX, X86_EXC_UD);
+ if ( dst.type == OP_REG )
+ {
+ /*
+ * For SETZUcc engage common EVEX.nd handling, requiring to put
+ * the destination in EVEX.V[0-4].
+ */
+ evex.reg = ~modrm_rm;
+ evex.RX = !evex.b4;
+ }
+ else /* Clear EVEX.nd. */
+ evex.brs = 0;
+ fallthrough;
case X86EMUL_OPC(0x0f, 0x90) ... X86EMUL_OPC(0x0f, 0x9f): /* setcc */
dst.val = test_cc(b, _regs.eflags);
break;
@@ -5400,6 +5500,8 @@ x86_emulate(
goto dispatch_from_helper;
case X86EMUL_OPC(0x0f, 0xaf): /* imul */
+ case X86EMUL_OPC_EVEX (4, 0xaf): /* imul reg,r/m{,reg} */
+ case X86EMUL_OPC_EVEX_66(4, 0xaf): /* imul r16,r/m{,r16} */
emulate_2op_SrcV_srcmem("imul", src, dst, _regs.eflags);
break;
@@ -7164,6 +7266,10 @@ x86_emulate(
case X86EMUL_OPC_F2(0x0f38, 0xf0): /* crc32 r/m8, r{32,64} */
case X86EMUL_OPC_F2(0x0f38, 0xf1): /* crc32 r/m{16,32,64}, r{32,64} */
vcpu_must_have(sse4_2);
+ fallthrough;
+ case X86EMUL_OPC_EVEX (4, 0xf0): /* crc32 r/m8, r{32,64} */
+ case X86EMUL_OPC_EVEX (4, 0xf1): /* crc32 r/m, r{32,64} */
+ case X86EMUL_OPC_EVEX_66(4, 0xf1): /* crc32 r/m16, r{32,64} */
dst.bytes = rex_prefix & REX_W ? 8 : 4;
switch ( op_bytes )
{
@@ -8162,6 +8268,23 @@ x86_emulate(
asm ( "rorl %b1,%k0" : "=g" (dst.val) : "c" (imm1), "0" (src.val) );
break;
+ case X86EMUL_OPC_EVEX(4, 0x8f): /* POP2 */
+ generate_exception_if(((modrm_reg & 7) || src.type != OP_REG ||
+ !EVEX_ND(&evex) || EVEX_NF(&evex)),
+ X86_EXC_UD);
+ n = (~evex.reg & 0xf) | (!evex.RX << 4);
+ generate_exception_if(modrm_rm == 4 || n == 4 || modrm_rm == n,
+ X86_EXC_UD);
+ generate_exception_if(_regs.r(sp) & 0xf, X86_EXC_GP, 0);
+ if ( (rc = read_ulong(x86_seg_ss, sp_post_inc(8),
+ &dst.val, 8, ctxt, ops)) != X86EMUL_OKAY ||
+ (rc = read_ulong(x86_seg_ss, sp_post_inc(8),
+ src.reg, 8, ctxt, ops)) != X86EMUL_OKAY )
+ goto done;
+ dst.type = OP_REG;
+ dst.bytes = 8;
+ break;
+
#ifndef X86EMUL_NO_SIMD
case X86EMUL_OPC_EVEX_F3(5, 0x10): /* vmovsh m16,xmm{k} */
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 17/30] x86emul: support APX SCC insns
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (15 preceding siblings ...)
2026-11-11 14:27 ` [PATCH 16/30] x86emul: support remaining basic APX EVEX-Map4 insns Jan Beulich
@ 2026-11-11 14:27 ` Jan Beulich
2026-11-11 14:27 ` [PATCH 18/30] x86emul: support APX+ADX insns Jan Beulich
` (12 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:27 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
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@suse.com>
--- 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 &&
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 18/30] x86emul: support APX+ADX insns
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (16 preceding siblings ...)
2026-11-11 14:27 ` [PATCH 17/30] x86emul: support APX SCC insns Jan Beulich
@ 2026-11-11 14:27 ` Jan Beulich
2026-11-11 14:28 ` [PATCH 19/30] x86emul: extend copy_VEX() for APX use Jan Beulich
` (11 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:27 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
They're straight counterparts, just encoded differently.
While extending the test harness also replace the numeric literals by
more readable expressions involving #define-s we have available now.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
--- a/tools/tests/x86_emulator/evex-disp8.c
+++ b/tools/tests/x86_emulator/evex-disp8.c
@@ -1098,6 +1098,11 @@ static const struct test apx_nci[] = {
#undef CONDS
+static const struct test adx[] = {
+ INSN(adcx, 66, map4, 66, el, dq, no),
+ INSN(adox, f3, map4, 66, el, dq, 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 };
@@ -1662,6 +1667,20 @@ void evex_disp8_test(void *instr, struct
vl_128, ARRAY_SIZE(vl_128), instr, ctxt);
printf("okay\n");
+#define RUN(feat) do { \
+ if ( cpu_has_ ## feat ) \
+ { \
+ printf("%-40s", "Testing APX-F+" #feat " disp8 handling..."); \
+ test_group(feat, ARRAY_SIZE(feat), \
+ vl_128, ARRAY_SIZE(vl_128), instr, ctxt); \
+ printf("okay\n"); \
+ } \
+ } while ( false )
+
+ RUN(adx);
+
+#undef RUN
+
cpu_policy.apx.apx_nci_ndd_nf = nci_ndd_nf;
ctxt->force_writeback = wb;
}
--- a/tools/tests/x86_emulator/predicates.c
+++ b/tools/tests/x86_emulator/predicates.c
@@ -2187,6 +2187,8 @@ static const struct evex {
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> */
+ { { 0x66 }, 2, T, R, pfx_66, Wn, L0 }, /* adcx */
+ { { 0x66 }, 2, T, R, pfx_f3, Wn, L0 }, /* adox */
{ { 0x69 }, 6, T, R, pfx_no, Wn, L0 }, /* imul */
{ { 0x69 }, 4, T, R, pfx_66, W0, L0 }, /* imul */
{ { 0x69 }, 6, T, R, pfx_66, W1, L0 }, /* imul */
--- a/tools/tests/x86_emulator/test_x86_emulator.c
+++ b/tools/tests/x86_emulator/test_x86_emulator.c
@@ -2999,7 +2999,7 @@ int main(int argc, char **argv)
"[dst1]" (0), "[dst2]" (0) );
set_insn(adx);
- regs.eflags = 0x2d6;
+ regs.eflags = EFLAGS_MASK & ~X86_EFLAGS(CF, OF);
of = cf = i = 0;
while ( (rc = x86_emulate(&ctxt, &emulops)) == X86EMUL_OKAY )
{
@@ -3011,9 +3011,9 @@ int main(int argc, char **argv)
*/
if ( i < ARRAY_SIZE(data) * 2 && !(i % 4) )
{
- if ( regs.eflags & 0x001 )
+ if ( regs.eflags & X86_EFLAGS_CF )
++cf;
- if ( regs.eflags & 0x800 )
+ if ( regs.eflags & X86_EFLAGS_OF )
++of;
}
if ( !valid_eip(adx) )
@@ -3022,9 +3022,69 @@ int main(int argc, char **argv)
if ( (rc != X86EMUL_OKAY) ||
i != ARRAY_SIZE(data) * 4 + 2 || cf != 1 || of != 5 ||
regs.eax != 0xffffffff || regs.ecx || regs.edx != 0xffffffff ||
- !check_eip(adx) || regs.eflags != 0x2d6 )
+ !check_eip(adx) ||
+ regs.eflags != (EFLAGS_MASK & ~X86_EFLAGS(CF, OF)) )
goto fail;
printf("okay\n");
+
+#ifdef __x86_64__
+ if ( cpu_has_apx_nci_ndd_nf )
+ {
+ /* Same data, but using eGPR-s. */
+ decl_insn(adx_apx);
+
+ printf("%-40s", "Testing APX adcx/adox ...");
+ asm volatile ( put_insn(adx_apx,
+ ".Lloop%=:\n\t"
+ /* mov %[cnt], %r18 */
+ ".byte 0xd5, 0x18, 0x89, 0xca\n\t"
+ /* adcx (%[addr]), %r30d */
+ ".byte 0x62, 0x64, 0x7d, 0x08, 0x66, 0x36\n\t"
+ /* adox -%c[full]-%c[elem](%[addr],%r18,2*%c[elem]), %k[dst2] */
+ ".byte 0x62, 0xf4, 0x7a, 0x08, 0x66\n\t"
+ ".byte 0x54, 0xd6, -%c[full]-%c[elem]\n\t"
+ "lea %c[elem](%[addr]),%[addr]\n\t"
+ "loop .Lloop%=\n\t"
+ /* adcx %[cnt], %r30d, %eax */
+ ".byte 0x62, 0x64, 0x7d, 0x18, 0x66, 0xf1\n\t"
+ /* adox %[cnt], %k[dst2], %r17d */
+ ".byte 0x62, 0xf4, 0x76, 0x10, 0x66, 0xd1\n\t")
+ : [addr] "=S" (regs.esi), [dst2] "=d" (regs.edx),
+ [cnt] "=c" (regs.ecx)
+ : [full] "i" (sizeof(data)), [elem] "i" (sizeof(*data)),
+ "[addr]" (data), "[cnt]" (ARRAY_SIZE(data)),
+ "[dst2]" (0) );
+
+ set_insn(adx_apx);
+ load_egpr(30, 0);
+ of = cf = i = 0;
+ while ( (rc = x86_emulate(&ctxt, &emulops)) == X86EMUL_OKAY )
+ {
+ ++i;
+ /* See comment above. */
+ if ( i < ARRAY_SIZE(data) * 2 + ARRAY_SIZE(data) / 2 &&
+ !(i % 5) )
+ {
+ if ( regs.eflags & X86_EFLAGS_CF )
+ ++cf;
+ if ( regs.eflags & X86_EFLAGS_OF )
+ ++of;
+ }
+ if ( !valid_eip(adx_apx) )
+ break;
+ }
+ if ( (rc != X86EMUL_OKAY) ||
+ i != ARRAY_SIZE(data) * 5 + 2 || cf != 1 || of != 5 ||
+ !check_eip(adx_apx) ||
+ regs.rax != 0xffffffffU || regs.rcx ||
+ regs.rdx != 0xffffffffU ||
+ !check_egpr(17, 0xffffffffU) || !check_egpr(18, 1) ||
+ !check_egpr(30, 0xffffffffU) ||
+ regs.eflags != (EFLAGS_MASK & ~X86_EFLAGS(CF, OF)) )
+ goto fail;
+ printf("okay\n");
+ }
+#endif
}
printf("%-40s", "Testing bextr $0x0a03,(%ecx),%ebx...");
--- a/tools/tests/x86_emulator/x86-emulate.h
+++ b/tools/tests/x86_emulator/x86-emulate.h
@@ -180,6 +180,7 @@ void wrpkru(unsigned int val);
xcr0_mask(0xe6))
#define cpu_has_avx512dq (cpu_policy.feat.avx512dq && \
xcr0_mask(0xe6))
+#define cpu_has_adx cpu_policy.feat.adx
#define cpu_has_avx512_ifma (cpu_policy.feat.avx512_ifma && \
xcr0_mask(0xe6))
#define cpu_has_avx512cd (cpu_policy.feat.avx512cd && \
--- a/xen/arch/x86/x86_emulate/decode.c
+++ b/xen/arch/x86/x86_emulate/decode.c
@@ -691,6 +691,10 @@ static const struct map4_table {
[vex_f2] = { .to_mem = true, .byte = true, .nd = true,
.nci_ndd_nf = true },
},
+ [0x66] = {
+ [vex_66] = { .nd = true }, /* ADCX */
+ [vex_f3] = { .nd = true }, /* ADOX */
+ },
[0x69] = { /* IMUL */
[vex_np] = { .immW = true, .nd = true, .nf = true,
.nci_ndd_nf = true },
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -7433,6 +7433,8 @@ x86_emulate(
case X86EMUL_OPC_66(0x0f38, 0xf6): /* adcx r/m,r */
case X86EMUL_OPC_F3(0x0f38, 0xf6): /* adox r/m,r */
+ case X86EMUL_OPC_EVEX_66(4, 0x66): /* adcx r/m,r{,r} */
+ case X86EMUL_OPC_EVEX_F3(4, 0x66): /* adox r/m,r{,r} */
{
unsigned int mask = rep_prefix() ? X86_EFLAGS_OF : X86_EFLAGS_CF;
unsigned int aux = _regs.eflags & mask ? ~0 : 0;
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 19/30] x86emul: extend copy_VEX() for APX use
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (17 preceding siblings ...)
2026-11-11 14:27 ` [PATCH 18/30] x86emul: support APX+ADX insns Jan Beulich
@ 2026-11-11 14:28 ` Jan Beulich
2026-11-11 14:28 ` [PATCH 20/30] x86emul: support APX+AVX512 insns Jan Beulich
` (10 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:28 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
As long as we work just with non-eGPR-s internally, we can engage the
VEX counterpart encodings to carry out emulation of EVEX-from-VEX
promoted insns. VEX3 and EVEX are actually pretty similar in their 1st
and 2nd payload bytes:
VEX3 C4 R X B mmmmm W vvvv L pp
EVEX 62 R3 X3 B3 R4 B4 mmm W vvvv U pp z LL b V4 aaa
with the from-VEX form only altering the 3rd payload byte:
00 L 0 V4 NF 00
Therefore by clearing B4, R4, and U we can easily transform EVEX into
VEX. (Don't transform EVEX.LL just yet - we'd have to pass in the EVEX
struct [or a pointer to it], when in reality for now all promoted APX
insns are EVEX.128 anyway.)
Signed-off-by: Jan Beulich <jbeulich@suse.com>
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -115,9 +115,12 @@ static const uint8_t sse_prefix[] = { 0x
buf_ + 3; \
})
-#define copy_VEX(ptr, vex) ({ \
+#define copy_VEX(ptr, vex, from_evex) ({ \
if ( !mode_64bit() ) \
(vex).reg |= 8; \
+ if ( from_evex ) \
+ /* Clear what was EVEX.B4 and EVEX.R4. */ \
+ (vex).opcx &= 7; \
gcc11_wrap(ptr)[0 - PFX_BYTES] = ext < ext_8f08 ? 0xc4 : 0x8f; \
(ptr)[1 - PFX_BYTES] = (vex).raw[0]; \
(ptr)[2 - PFX_BYTES] = (vex).raw[1]; \
@@ -126,7 +129,7 @@ static const uint8_t sse_prefix[] = { 0x
#define copy_REX_VEX(ptr, rex, vex) do { \
if ( (vex).opcx != vex_none ) \
- copy_VEX(ptr, vex); \
+ copy_VEX(ptr, vex, false); \
else \
{ \
if ( (vex).pfx ) \
@@ -5378,7 +5381,7 @@ x86_emulate(
opc[1] = modrm & 0xf8;
place_ret(&opc[2]);
- copy_VEX(opc, vex);
+ copy_VEX(opc, vex, false);
ea.reg = decode_gpr(&_regs, NULL, modrm_rm);
invoke_stub("", "", "=m" (dummy) : "a" (*ea.reg));
@@ -5422,7 +5425,7 @@ x86_emulate(
opc[1] = modrm & 0xc7;
place_ret(&opc[2]);
- copy_VEX(opc, vex);
+ copy_VEX(opc, vex, false);
invoke_stub("", "", "=a" (dst.val) : [dummy] "i" (0));
put_stub(stub);
@@ -5450,7 +5453,7 @@ x86_emulate(
opc[1] = modrm;
place_ret(&opc[2]);
- copy_VEX(opc, vex);
+ copy_VEX(opc, vex, false);
_regs.eflags &= ~EFLAGS_MASK;
invoke_stub("",
_POST_EFLAGS("[eflags]", "[mask]", "[tmp]"),
@@ -6370,7 +6373,7 @@ x86_emulate(
* for that purpose.
*/
opc = init_prefixes(stub);
- pvex = copy_VEX(opc, vex);
+ pvex = copy_VEX(opc, vex, false);
pvex->opcx = vex_0f;
if ( elem_bytes == 4 )
pvex->pfx = vex_np;
@@ -6806,7 +6809,7 @@ x86_emulate(
/* Read destination, index, and mask registers. */
opc = init_prefixes(stub);
- pvex = copy_VEX(opc, vex);
+ pvex = copy_VEX(opc, vex, false);
pvex->opcx = vex_0f;
opc[0] = 0x7f; /* vmovdqa */
/* Use (%rax) as destination and modrm_reg as source. */
@@ -6876,7 +6879,7 @@ x86_emulate(
/* Write destination and mask registers. */
opc = init_prefixes(stub);
- pvex = copy_VEX(opc, vex);
+ pvex = copy_VEX(opc, vex, false);
pvex->opcx = vex_0f;
opc[0] = 0x6f; /* vmovdqa */
/* Use modrm_reg as destination and (%rax) as source. */
@@ -7942,7 +7945,7 @@ x86_emulate(
else
{
insn_bytes = PFX_BYTES + 3;
- copy_VEX(opc, vex);
+ copy_VEX(opc, vex, false);
}
place_ret(&opc[3]);
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 20/30] x86emul: support APX+AVX512 insns
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (18 preceding siblings ...)
2026-11-11 14:28 ` [PATCH 19/30] x86emul: extend copy_VEX() for APX use Jan Beulich
@ 2026-11-11 14:28 ` Jan Beulich
2026-11-11 14:29 ` [PATCH 21/30] x86emul: support APX+BMI1 insns Jan Beulich
` (9 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:28 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
It's KMOV{B,W,D,Q} which gain straight EVEX-encoded counterparts. Like
the VEX forms require special treatment, so do the EVEX ones. Respective
code is being cloned, with mode_64bit() conditionals dropped.
While adjusting the testharness'es Makefile, drop -Os from the non-APX
opmask test as well - for an assembly source that option is meaningless.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
---
The spec says EVEX.B4 is ignored for opcode 0x93. While that matches
the fact that EVEX.B3 is also ignored, other similar statments in spec
version 005 (especially as to EVEX.R4 vs EVEX.R3) aren't consistent.
Hence this may need adjustment.
The checking of the 4th EVEX byte is going to re-appear for a number of
further insns. It may therefore want folding into a wrapper macro. Just
that I don't seem to be able to think of a good name for such a macro.
Is simply forcing EVEX encodings (without also involving eGPR-s) enough
of a testing approach?
--- a/tools/tests/x86_emulator/Makefile
+++ b/tools/tests/x86_emulator/Makefile
@@ -172,7 +172,8 @@ $(1)-cflags := $(foreach vec,$($(1:-gf=)
"-D_$(vec) -mgfni -m$(1:-gf=) $(call non-sse,$(1)) -Os -DVEC_SIZE=$(vec)")
endef
define opmask-defs
-$(1)-opmask-cflags := $(foreach vec,$($(1)-opmask-vecs), "-D_$(vec) -m$(1) -Os -DSIZE=$(vec)")
+$(1)-opmask-cflags := $(foreach vec,$($(1)-opmask-vecs), "-D_$(vec) -m$(1) -DSIZE=$(vec)")
+$(1)-opmask-cflags-x86_64 := $(foreach vec,$($(1)-opmask-vecs), "-D_$(vec) -mapxf -m$(1) -DSIZE=$(vec)")
endef
$(foreach flavor,$(SIMD) $(FMA),$(eval $(call simd-defs,$(flavor))))
@@ -214,6 +215,9 @@ $(addsuffix -opmask.h,$(OPMASK)): %.h: o
for cflags in $($*-cflags) $($*-cflags-$(arch)); do \
$(MAKE) -f testcase.mk TESTCASE=$* XEN_TARGET_ARCH=$(arch) $*-cflags="$$cflags" all; \
prefix=$(shell echo $(subst -,_,$*) | sed -e 's,^\([0-9]\),_\1,'); \
+ if [ "$$cflags" != "$${cflags%apx*}" ]; then \
+ prefix=$${prefix%opmask}apx; \
+ fi; \
flavor=$$(echo $${cflags} | sed -e 's, .*,,' -e 'y,-=,__,') ; \
(echo 'static const unsigned int __attribute__((section(".test, \"ax\", @progbits #")))' \
"$${prefix}_$(arch)$${flavor}[] = {"; \
--- a/tools/tests/x86_emulator/evex-disp8.c
+++ b/tools/tests/x86_emulator/evex-disp8.c
@@ -1103,6 +1103,29 @@ static const struct test adx[] = {
INSN(adox, f3, map4, 66, el, dq, no),
};
+static const struct test avx512bw[] = {
+// kmov, f2, 0f, 92, dq
+// kmov, f2, 0f, 93, dq
+ INSN(kmovd, 66, 0f, 90, el, d, no),
+ INSN(kmovd, 66, 0f, 91, el, d, no),
+ INSN(kmovq, , 0f, 90, el, q, no),
+ INSN(kmovq, , 0f, 91, el, q, no),
+};
+
+static const struct test avx512dq[] = {
+ INSN(kmovb, 66, 0f, 90, el, b, no),
+ INSN(kmovb, 66, 0f, 91, el, b, no),
+// kmovb, 66, 0f, 92, b
+// kmovb, 66, 0f, 93, b
+};
+
+static const struct test avx512f[] = {
+ INSN(kmovw, , 0f, 90, el, w, no),
+ INSN(kmovw, , 0f, 91, el, w, no),
+// kmovw, , 0f, 92, w
+// kmovw, , 0f, 93, w
+};
+
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 };
@@ -1261,7 +1284,17 @@ static void test_one(const struct test *
evex.w = 1;
/* fall through */
#endif
- case ESZ_d: case ESZ_d_nb:
+ case ESZ_d:
+#ifdef __x86_64__
+ /*
+ * KMOVD don't follow the general pattern: They have EVEX.W set despite
+ * having a DWORD operand.
+ */
+ if ( test->spc == SPC_0f && (test->opc | 1) == 0x91 )
+ evex.w = 1;
+ /* fall through */
+#endif
+ case ESZ_d_nb:
esz = 4;
break;
@@ -1678,6 +1711,9 @@ void evex_disp8_test(void *instr, struct
} while ( false )
RUN(adx);
+ RUN(avx512f);
+ RUN(avx512dq);
+ RUN(avx512bw);
#undef RUN
--- a/tools/tests/x86_emulator/opmask.S
+++ b/tools/tests/x86_emulator/opmask.S
@@ -8,6 +8,12 @@
# define R(x) e##x
# endif
# define DATA(x) x(%rip)
+# ifdef __APX_F__
+# define kmovb {evex} kmovb
+# define kmovd {evex} kmovd
+# define kmovq {evex} kmovq
+# define kmovw {evex} kmovw
+# endif
#endif
#if SIZE == 1
--- a/tools/tests/x86_emulator/predicates.c
+++ b/tools/tests/x86_emulator/predicates.c
@@ -1787,6 +1787,18 @@ static const struct evex {
{ { 0x7f }, 2, T, W, pfx_66, Wn, Ln }, /* vmovdqa{32,64} */
{ { 0x7f }, 2, T, W, pfx_f3, Wn, Ln }, /* vmovdqu{32,64} */
{ { 0x7f }, 2, T, W, pfx_f2, Wn, Ln }, /* vmovdqu{8,16} */
+#ifdef __x86_64__
+ { { 0x90 }, 2, T, R, pfx_no, Wn, L0 }, /* kmov{w,q} */
+ { { 0x90 }, 2, T, R, pfx_66, Wn, L0 }, /* kmov{b,d} */
+ { { 0x91 }, 2, N, W, pfx_no, Wn, L0 }, /* kmov{w,q} */
+ { { 0x91 }, 2, N, W, pfx_66, Wn, L0 }, /* kmov{b,d} */
+ { { 0x92, 0xc0 }, 2, F, N, pfx_no, W0, L0 }, /* kmovw */
+ { { 0x92, 0xc0 }, 2, F, N, pfx_66, W0, L0 }, /* kmovb */
+ { { 0x92, 0xc0 }, 2, F, N, pfx_f2, Wn, L0 }, /* kmov{d,q} */
+ { { 0x93, 0xc0 }, 2, F, N, pfx_no, W0, L0 }, /* kmovw */
+ { { 0x93, 0xc0 }, 2, F, N, pfx_66, W0, L0 }, /* kmovb */
+ { { 0x93, 0xc0 }, 2, F, N, pfx_f2, Wn, L0 }, /* kmov{d,q} */
+#endif
{ { 0xc2 }, 3, T, R, pfx_no, W0, Ln }, /* vcmpps */
{ { 0xc2 }, 3, T, R, pfx_66, W1, Ln }, /* vcmppd */
{ { 0xc2 }, 3, T, R, pfx_f3, W0, LIG }, /* vcmpss */
--- a/tools/tests/x86_emulator/test_x86_emulator.c
+++ b/tools/tests/x86_emulator/test_x86_emulator.c
@@ -348,6 +348,23 @@ static bool simd_check_avx512fp16_vl(voi
cpu_has_avx10;
}
+#ifdef __x86_64__
+static bool simd_check_avx512f_apx(void)
+{
+ return simd_check_avx512f_opmask() && check_apx_f();
+}
+
+static bool simd_check_avx512bw_apx(void)
+{
+ return simd_check_avx512bw_opmask() && check_apx_f();
+}
+
+static bool simd_check_avx512dq_apx(void)
+{
+ return simd_check_avx512dq_opmask() && check_apx_f();
+}
+#endif /* __x86_64__ */
+
static void simd_set_regs(struct cpu_user_regs *regs)
{
if ( cpu_has_mmx )
@@ -532,6 +549,13 @@ static const struct {
SIMD(OPMASK+DQ/w, avx512dq_opmask, 2),
SIMD(OPMASK+BW/d, avx512bw_opmask, 4),
SIMD(OPMASK+BW/q, avx512bw_opmask, 8),
+#ifdef __x86_64__
+ SIMD_(64, OPMASK+APX/w, avx512f_apx, 2),
+ SIMD_(64, OPMASK+DQ+APX/b, avx512dq_apx, 1),
+ SIMD_(64, OPMASK+DQ+APX/w, avx512dq_apx, 2),
+ SIMD_(64, OPMASK+BW+APX/d, avx512bw_apx, 4),
+ SIMD_(64, OPMASK+BW+APX/q, avx512bw_apx, 8),
+#endif
SIMD(AVX512F f32 scalar, avx512f, f4),
SIMD(AVX512F f32x16, avx512f, 64f4),
SIMD(AVX512F f64 scalar, avx512f, f8),
--- a/xen/arch/x86/x86_emulate/decode.c
+++ b/xen/arch/x86/x86_emulate/decode.c
@@ -1100,18 +1100,35 @@ decode_twobyte(struct x86_emulate_state
}
break;
+ case X86EMUL_OPC_EVEX(0, 0x90): /* kmov{w,q} */
+ case X86EMUL_OPC_EVEX_66(0, 0x90): /* kmov{b,d} */
+ s->promoted_evex = true;
+ fallthrough;
case X86EMUL_OPC_VEX(0, 0x90): /* kmov{w,q} */
case X86EMUL_OPC_VEX_66(0, 0x90): /* kmov{b,d} */
s->desc = DstReg | SrcMem | Mov;
s->simd_size = simd_other;
break;
+ case X86EMUL_OPC_EVEX(0, 0x91): /* kmov{w,q} */
+ case X86EMUL_OPC_EVEX_66(0, 0x91): /* kmov{b,d} */
+ s->promoted_evex = true;
+ fallthrough;
case X86EMUL_OPC_VEX(0, 0x91): /* kmov{w,q} */
case X86EMUL_OPC_VEX_66(0, 0x91): /* kmov{b,d} */
s->desc = DstMem | SrcReg | Mov;
s->simd_size = simd_other;
break;
+ case X86EMUL_OPC_EVEX(0, 0x92): /* kmovw */
+ case X86EMUL_OPC_EVEX_66(0, 0x92): /* kmovb */
+ case X86EMUL_OPC_EVEX_F2(0, 0x92): /* kmov{d,q} */
+ case X86EMUL_OPC_EVEX(0, 0x93): /* kmovw */
+ case X86EMUL_OPC_EVEX_66(0, 0x93): /* kmovb */
+ case X86EMUL_OPC_EVEX_F2(0, 0x93): /* kmov{d,q} */
+ s->promoted_evex = true;
+ break;
+
case 0xae:
ctxt->opcode |= MASK_INSR(s->vex.pfx, X86EMUL_OPC_PFX_MASK);
/* fall through */
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -5316,6 +5316,16 @@ x86_emulate(
#ifndef X86EMUL_NO_SIMD
+ case X86EMUL_OPC_EVEX(0x0f, 0x91): /* kmov{w,q} k,mem */
+ case X86EMUL_OPC_EVEX_66(0x0f, 0x91): /* kmov{b,d} k,mem */
+ generate_exception_if(ea.type != OP_MEM, X86_EXC_UD);
+ fallthrough;
+ case X86EMUL_OPC_EVEX(0x0f, 0x90): /* kmov{w,q} k/mem,k */
+ case X86EMUL_OPC_EVEX_66(0x0f, 0x90): /* kmov{b,d} k/mem,k */
+ generate_exception_if((evex.lr || evex.brs || evex.opmsk ||
+ !evex.r3 || !evex.r4),
+ X86_EXC_UD);
+ goto kmov;
case X86EMUL_OPC_VEX(0x0f, 0x91): /* kmov{w,q} k,mem */
case X86EMUL_OPC_VEX_66(0x0f, 0x91): /* kmov{b,d} k,mem */
generate_exception_if(ea.type != OP_MEM, X86_EXC_UD);
@@ -5323,6 +5333,7 @@ x86_emulate(
case X86EMUL_OPC_VEX(0x0f, 0x90): /* kmov{w,q} k/mem,k */
case X86EMUL_OPC_VEX_66(0x0f, 0x90): /* kmov{b,d} k/mem,k */
generate_exception_if(vex.l || !vex.r, X86_EXC_UD);
+ kmov:
if ( vex.w )
{
visa_check(bw);
@@ -5353,6 +5364,13 @@ x86_emulate(
insn_bytes = PFX_BYTES + 2;
break;
+ case X86EMUL_OPC_EVEX(0x0f, 0x92): /* kmovw r32,k */
+ case X86EMUL_OPC_EVEX_66(0x0f, 0x92): /* kmovb r32,k */
+ case X86EMUL_OPC_EVEX_F2(0x0f, 0x92): /* kmov{d,q} reg,k */
+ generate_exception_if((evex.lr || evex.brs || evex.opmsk ||
+ !evex.r4 || !evex.RX),
+ X86_EXC_UD);
+ fallthrough;
case X86EMUL_OPC_VEX(0x0f, 0x92): /* kmovw r32,k */
case X86EMUL_OPC_VEX_66(0x0f, 0x92): /* kmovb r32,k */
case X86EMUL_OPC_VEX_F2(0x0f, 0x92): /* kmov{d,q} reg,k */
@@ -5381,8 +5399,8 @@ x86_emulate(
opc[1] = modrm & 0xf8;
place_ret(&opc[2]);
- copy_VEX(opc, vex, false);
- ea.reg = decode_gpr(&_regs, NULL, modrm_rm);
+ copy_VEX(opc, vex, state->promoted_evex);
+ ea.reg = decode_gpr(&_regs, &state->egpr, modrm_rm);
invoke_stub("", "", "=m" (dummy) : "a" (*ea.reg));
put_stub(stub);
@@ -5391,13 +5409,19 @@ x86_emulate(
dst.type = OP_NONE;
break;
+ case X86EMUL_OPC_EVEX(0x0f, 0x93): /* kmovw k,r32 */
+ case X86EMUL_OPC_EVEX_66(0x0f, 0x93): /* kmovb k,r32 */
+ case X86EMUL_OPC_EVEX_F2(0x0f, 0x93): /* kmov{d,q} k,reg */
+ generate_exception_if((evex.lr || evex.brs || evex.opmsk || !evex.RX),
+ X86_EXC_UD);
+ fallthrough;
case X86EMUL_OPC_VEX(0x0f, 0x93): /* kmovw k,r32 */
case X86EMUL_OPC_VEX_66(0x0f, 0x93): /* kmovb k,r32 */
case X86EMUL_OPC_VEX_F2(0x0f, 0x93): /* kmov{d,q} k,reg */
generate_exception_if(vex.l || vex.reg != 0xf || ea.type != OP_REG,
X86_EXC_UD);
dst = ea;
- dst.reg = decode_gpr(&_regs, NULL, modrm_reg);
+ dst.reg = decode_gpr(&_regs, &state->egpr, modrm_reg);
if ( vex.pfx == vex_f2 )
{
@@ -5425,7 +5449,7 @@ x86_emulate(
opc[1] = modrm & 0xc7;
place_ret(&opc[2]);
- copy_VEX(opc, vex, false);
+ copy_VEX(opc, vex, state->promoted_evex);
invoke_stub("", "", "=a" (dst.val) : [dummy] "i" (0));
put_stub(stub);
@@ -8950,7 +8974,12 @@ x86_emulate(
EXPECT(op_bytes);
EXPECT(opc);
- if ( evex_encoded() )
+ if ( state->promoted_evex )
+ {
+ place_ret(&opc[insn_bytes - PFX_BYTES]);
+ copy_VEX(opc, vex, true);
+ }
+ else if ( evex_encoded() )
{
place_ret(&opc[insn_bytes - EVEX_PFX_BYTES]);
copy_EVEX(opc, evex);
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 21/30] x86emul: support APX+BMI1 insns
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (19 preceding siblings ...)
2026-11-11 14:28 ` [PATCH 20/30] x86emul: support APX+AVX512 insns Jan Beulich
@ 2026-11-11 14:29 ` Jan Beulich
2026-11-11 14:29 ` [PATCH 22/30] x86emul: support APX+BMI2 insns Jan Beulich
` (8 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:29 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
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@suse.com>
---
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);
}
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 22/30] x86emul: support APX+BMI2 insns
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (20 preceding siblings ...)
2026-11-11 14:29 ` [PATCH 21/30] x86emul: support APX+BMI1 insns Jan Beulich
@ 2026-11-11 14:29 ` Jan Beulich
2026-11-11 14:29 ` [PATCH 23/30] x86emul: support APX+CMPccXADD insns Jan Beulich
` (7 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:29 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
These promoted-from-VEX insns are again handled by transforming them to
their VEX counterparts, re-using (where possible) logic already used for
BMI1. As before common EVEX.NF handling takes care of the EFLAGS aspect.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
--- a/tools/tests/x86_emulator/evex-disp8.c
+++ b/tools/tests/x86_emulator/evex-disp8.c
@@ -1136,6 +1136,17 @@ static const struct test bmi1[] = {
INSN(tzcntw, 66, map4, f4, el, w, no),
};
+static const struct test bmi2[] = {
+ INSN(bzhi, , 0f38, f5, el, dq, no),
+ INSN(mulx, f2, 0f38, f6, el, dq, no),
+ INSN(pdep, f2, 0f38, f5, el, dq, no),
+ INSN(pext, f3, 0f38, f5, el, dq, no),
+ INSN(rorx, f2, 0f3a, f0, el, dq, no),
+ INSN(sarx, f3, 0f38, f7, el, dq, no),
+ INSN(shlx, 66, 0f38, f7, el, dq, no),
+ INSN(shrx, f2, 0f38, f7, el, dq, 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 };
@@ -1725,6 +1736,7 @@ void evex_disp8_test(void *instr, struct
RUN(avx512dq);
RUN(avx512bw);
RUN(bmi1);
+ RUN(bmi2);
#undef RUN
--- a/tools/tests/x86_emulator/predicates.c
+++ b/tools/tests/x86_emulator/predicates.c
@@ -2071,7 +2071,14 @@ static const struct evex {
{ { 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 */
+ { { 0xf5 }, 2, T, R, pfx_no, Wn, L0 }, /* bzhi */
+ { { 0xf5 }, 2, T, R, pfx_f3, Wn, L0 }, /* pext */
+ { { 0xf5 }, 2, T, R, pfx_f2, Wn, L0 }, /* pdep */
+ { { 0xf6 }, 2, T, R, pfx_f2, Wn, L0 }, /* mulx */
{ { 0xf7 }, 2, T, R, pfx_no, Wn, L0 }, /* bextr */
+ { { 0xf7 }, 2, T, R, pfx_66, Wn, L0 }, /* shlx */
+ { { 0xf7 }, 2, T, R, pfx_f3, Wn, L0 }, /* sarx */
+ { { 0xf7 }, 2, T, R, pfx_f2, Wn, L0 }, /* shrx */
#endif
}, evex_0f3a[] = {
{ { 0x00 }, 3, T, R, pfx_66, W1, L1|L2 }, /* vpermq */
@@ -2147,6 +2154,9 @@ static const struct evex {
{ { 0xc2 }, 3, T, R, pfx_f2, W0, Ln }, /* vcmpbf16 */
{ { 0xce }, 3, T, R, pfx_66, W1, Ln }, /* vgf2p8affineqb */
{ { 0xcf }, 3, T, R, pfx_66, W1, Ln }, /* vgf2p8affineinvqb */
+#ifdef __x86_64__
+ { { 0xf0 }, 3, T, R, pfx_f2, Wn, L0 }, /* rorx */
+#endif
}, evex_map4[] = {
#ifdef __x86_64__
#define ND .nd = true
--- a/tools/tests/x86_emulator/test_x86_emulator.c
+++ b/tools/tests/x86_emulator/test_x86_emulator.c
@@ -2970,6 +2970,34 @@ int main(int argc, char **argv)
!check_eip(bzhi) )
goto fail;
printf("okay\n");
+
+#if defined(__x86_64__) && \
+ (defined(__clang__) ? __clang_major__ >= 18 : __GNUC__ >= 14)
+ if ( true /* cpu_has_apx_nci_ndd_nf */ )
+ {
+ decl_insn(bzhi_apx);
+
+ cpu_policy.apx.apx_nci_ndd_nf = true;
+
+ printf("%-40s", "Testing {nf}bzhi %edx,(%rcx),%ebx...");
+
+ asm volatile ( put_insn(bzhi_apx, "%{nf%} bzhi %%edx, (%0), %%ebx")
+ :: "c" (NULL) );
+ set_insn(bzhi_apx);
+
+ regs.rbx = 0;
+ regs.eflags = EFLAGS_ALWAYS_SET | X86_EFLAGS(OF, SF, ZF, CF);
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) || (regs.rbx != (*res & 0x7ffff)) ||
+ (regs.eflags !=
+ (EFLAGS_ALWAYS_SET | X86_EFLAGS(OF, SF, ZF, CF))) ||
+ !check_eip(bzhi_apx) )
+ goto fail;
+ printf("okay\n");
+
+ cpu_policy.apx.apx_nci_ndd_nf = nci_ndd_nf;
+ }
+#endif
}
else
printf("skipped\n");
@@ -2992,6 +3020,28 @@ int main(int argc, char **argv)
regs.eflags != 0xac3 || !check_eip(mulx) )
goto fail;
printf("okay\n");
+
+#if defined(__x86_64__) && \
+ (defined(__clang__) ? __clang_major__ >= 18 : __GNUC__ >= 14)
+ if ( cpu_has_apx_f )
+ {
+ decl_insn(mulx_apx);
+
+ printf("%-40s", "Testing mulx (%rax),%r25,%rbx...");
+
+ asm volatile ( put_insn(mulx_apx, "mulx (%0), %%r25, %%rbx")
+ :: "a" (NULL) );
+ set_insn(mulx_apx);
+
+ res[1] = 0;
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) || regs.rbx ||
+ !check_egpr(25, 0x121fa00a35068740UL) ||
+ !check_eip(mulx_apx) )
+ goto fail;
+ printf("okay\n");
+ }
+#endif
}
else
printf("skipped\n");
@@ -3014,6 +3064,28 @@ int main(int argc, char **argv)
regs.eflags != 0xa43 || !check_eip(pdep) )
goto fail;
printf("okay\n");
+
+#if defined(__x86_64__) && \
+ (defined(__clang__) ? __clang_major__ >= 18 : __GNUC__ >= 14)
+ if ( cpu_has_apx_f )
+ {
+ decl_insn(pdep_apx);
+
+ printf("%-40s", "Testing pdep (%rdx),%ecx,%r19d...");
+
+ asm volatile ( put_insn(pdep_apx, "pdep (%0), %%ecx, %%r19d")
+ :: "d" (NULL) );
+ set_insn(pdep_apx);
+
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ !check_egpr(19, 0x850b298) ||
+ (regs.ecx != 0x8cef) ||
+ !check_eip(pdep_apx) )
+ goto fail;
+ printf("okay\n");
+ }
+#endif
}
else
printf("skipped\n");
@@ -3036,6 +3108,30 @@ int main(int argc, char **argv)
regs.eflags != 0xa43 || !check_eip(pext) )
goto fail;
printf("okay\n");
+
+#if defined(__x86_64__) && \
+ (defined(__clang__) ? __clang_major__ >= 18 : __GNUC__ >= 14)
+ if ( cpu_has_apx_f )
+ {
+ decl_insn(pext_apx);
+
+ printf("%-40s", "Testing pext (%edx),%r17d,%ebx...");
+
+ asm volatile ( put_insn(pext_apx, "pext (%0), %%r17d, %%ebx")
+ :: "d" (NULL) );
+ set_insn(pext_apx);
+
+ regs.rcx = 0;
+ regs.rbx = 0;
+ load_egpr(17, 0x137f8cef);
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) || (regs.ebx != 0x12f95) ||
+ !check_egpr(17, 0x137f8cef) ||
+ !check_eip(pext_apx) )
+ goto fail;
+ printf("okay\n");
+ }
+#endif
}
else
printf("skipped\n");
@@ -3057,6 +3153,27 @@ int main(int argc, char **argv)
regs.eflags != 0xa43 || !check_eip(rorx) )
goto fail;
printf("okay\n");
+
+#if defined(__x86_64__) && \
+ (defined(__clang__) ? __clang_major__ >= 18 : __GNUC__ >= 14)
+ if ( cpu_has_apx_f )
+ {
+ decl_insn(rorx_apx);
+
+ printf("%-40s", "Testing rorx $8,(%r17),%ebx...");
+
+ asm volatile ( put_insn(rorx_apx, "rorx $8, (%r17), %ebx") );
+ set_insn(rorx_apx);
+
+ regs.ecx = 0;
+ load_egpr(17, (unsigned long)res);
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) || (regs.rbx != 0x98fedcbaU) ||
+ !check_eip(rorx_apx) )
+ goto fail;
+ printf("okay\n");
+ }
+#endif
}
else
printf("skipped\n");
@@ -3080,6 +3197,29 @@ int main(int argc, char **argv)
regs.eflags != 0xa43 || !check_eip(sarx) )
goto fail;
printf("okay\n");
+
+#if defined(__x86_64__) && \
+ (defined(__clang__) ? __clang_major__ >= 18 : __GNUC__ >= 14)
+ if ( cpu_has_apx_f )
+ {
+ decl_insn(sarx_apx);
+
+ printf("%-40s", "Testing sarx %r26d,(%rcx),%ebx...");
+
+ asm volatile ( put_insn(sarx_apx, "sarx %%r26d, (%0), %%ebx")
+ :: "c" (NULL) );
+ set_insn(sarx_apx);
+
+ regs.ebx = 0;
+ load_egpr(26, 0xff11);
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rbx != (unsigned)(((signed)*res >> 0x11))) ||
+ !check_eip(sarx_apx) )
+ goto fail;
+ printf("okay\n");
+ }
+#endif
}
else
printf("skipped\n");
@@ -3100,6 +3240,27 @@ int main(int argc, char **argv)
regs.eflags != 0xa43 || !check_eip(shlx) )
goto fail;
printf("okay\n");
+
+#if defined(__x86_64__) && \
+ (defined(__clang__) ? __clang_major__ >= 18 : __GNUC__ >= 14)
+ if ( cpu_has_apx_f )
+ {
+ decl_insn(shlx_apx);
+
+ printf("%-40s", "Testing shlx %rdx,(%rcx),%r19...");
+
+ asm volatile ( put_insn(shlx_apx, "shlx %%rdx, (%0), %%r19")
+ :: "c" (NULL) );
+ set_insn(shlx_apx);
+
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ !check_egpr(19, (unsigned long)*res << (regs.rdx & 0x1f)) ||
+ !check_eip(shlx_apx) )
+ goto fail;
+ printf("okay\n");
+ }
+#endif
}
else
printf("skipped\n");
@@ -3120,6 +3281,27 @@ int main(int argc, char **argv)
regs.eflags != 0xa43 || !check_eip(shrx) )
goto fail;
printf("okay\n");
+
+#if defined(__x86_64__) && \
+ (defined(__clang__) ? __clang_major__ >= 18 : __GNUC__ >= 14)
+ if ( cpu_has_apx_f )
+ {
+ decl_insn(shrx_apx);
+
+ printf("%-40s", "Testing shrx %edx,(%ecx),%r19d...");
+
+ asm volatile ( put_insn(shrx_apx, "shrx %%edx, (%0), %%r19d")
+ :: "c" (NULL) );
+ set_insn(shrx_apx);
+
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ !check_egpr(19, (*res >> (regs.edx & 0x1f))) ||
+ !check_eip(shrx_apx) )
+ goto fail;
+ printf("okay\n");
+ }
+#endif
}
else
printf("skipped\n");
--- a/xen/arch/x86/x86_emulate/decode.c
+++ b/xen/arch/x86/x86_emulate/decode.c
@@ -1269,7 +1269,14 @@ decode_0f38(struct x86_emulate_state *s,
case X86EMUL_OPC_EVEX(0, 0xf2): /* andn */
case X86EMUL_OPC_EVEX(0, 0xf3): /* Grp 17 */
+ case X86EMUL_OPC_EVEX(0, 0xf5): /* bzhi */
+ case X86EMUL_OPC_EVEX_F3(0, 0xf5): /* pext */
+ case X86EMUL_OPC_EVEX_F2(0, 0xf5): /* pdep */
+ case X86EMUL_OPC_EVEX_F2(0, 0xf6): /* mulx */
case X86EMUL_OPC_EVEX(0, 0xf7): /* bextr */
+ case X86EMUL_OPC_EVEX_66(0, 0xf7): /* shlx */
+ case X86EMUL_OPC_EVEX_F3(0, 0xf7): /* sarx */
+ case X86EMUL_OPC_EVEX_F2(0, 0xf7): /* shrx */
s->promoted_evex = true;
break;
@@ -1326,6 +1333,10 @@ decode_0f3a(struct x86_emulate_state *s,
s->desc = DstImplicit | SrcMem;
break;
+ case X86EMUL_OPC_EVEX_F2(0, 0xf0): /* rorx */
+ s->promoted_evex = true;
+ break;
+
default:
s->op_bytes = 0;
break;
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -7390,7 +7390,15 @@ x86_emulate(
}
break;
+ case X86EMUL_OPC_EVEX_F3(0x0f38, 0xf5): /* pext r/m,r,r */
+ case X86EMUL_OPC_EVEX_F2(0x0f38, 0xf5): /* pdep r/m,r,r */
+ case X86EMUL_OPC_EVEX_66(0x0f38, 0xf7): /* shlx r,r/m,r */
+ case X86EMUL_OPC_EVEX_F3(0x0f38, 0xf7): /* sarx r,r/m,r */
+ case X86EMUL_OPC_EVEX_F2(0x0f38, 0xf7): /* shrx r,r/m,r */
+ generate_exception_if(evex.opmsk, X86_EXC_UD);
+ fallthrough;
case X86EMUL_OPC_EVEX(0x0f38, 0xf2): /* andn r/m,r,r */
+ case X86EMUL_OPC_EVEX(0x0f38, 0xf5): /* bzhi r,r/m,r */
case X86EMUL_OPC_EVEX(0x0f38, 0xf7): /* bextr r,r/m,r */
generate_exception_if((evex.z || evex.lr || evex.brs ||
(evex.opmsk & 3)),
@@ -7524,10 +7532,19 @@ x86_emulate(
break;
}
+ case X86EMUL_OPC_EVEX_F2(0x0f38, 0xf6): /* mulx r/m,r,r */
+ generate_exception_if(evex.lr || evex.brs || evex.opmsk, X86_EXC_UD);
+ fallthrough;
case X86EMUL_OPC_VEX_F2(0x0f38, 0xf6): /* mulx r/m,r,r */
vcpu_must_have(bmi2);
- generate_exception_if(vex.l, X86_EXC_UD);
- ea.reg = decode_vex_gpr(vex.reg, &_regs, ctxt);
+ if ( state->promoted_evex )
+ ea.reg = decode_evex_gpr(evex.reg | (evex.RX << 4), &_regs,
+ &state->egpr, ctxt);
+ else
+ {
+ generate_exception_if(vex.l, X86_EXC_UD);
+ ea.reg = decode_vex_gpr(vex.reg, &_regs, ctxt);
+ }
if ( mode_64bit() && vex.w )
asm ( "mulq %3" : "=a" (*ea.reg), "=d" (dst.val)
: "0" (src.val), "rm" (_regs.r(dx)) );
@@ -8382,6 +8399,10 @@ x86_emulate(
#endif /* X86EMUL_NO_SIMD */
+ case X86EMUL_OPC_EVEX_F2(0x0f3a, 0xf0): /* rorx imm,r/m,r */
+ generate_exception_if((evex.lr || evex.brs || evex.opmsk || !evex.RX),
+ X86_EXC_UD);
+ fallthrough;
case X86EMUL_OPC_VEX_F2(0x0f3a, 0xf0): /* rorx imm,r/m,r */
vcpu_must_have(bmi2);
generate_exception_if(vex.l || vex.reg != 0xf, X86_EXC_UD);
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 23/30] x86emul: support APX+CMPccXADD insns
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (21 preceding siblings ...)
2026-11-11 14:29 ` [PATCH 22/30] x86emul: support APX+BMI2 insns Jan Beulich
@ 2026-11-11 14:29 ` Jan Beulich
2026-11-11 14:30 ` [PATCH 24/30] x86emul: support APX+{LZ,POP}CNT insns Jan Beulich
` (6 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:29 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
This group of promoted-from-VEX insns is again handled by transforming
to the VEX counterparts.
In the test harness'es predicates tables also replace the original (VEX)
entries to use the available CND().
Signed-off-by: Jan Beulich <jbeulich@suse.com>
--- a/tools/tests/x86_emulator/evex-disp8.c
+++ b/tools/tests/x86_emulator/evex-disp8.c
@@ -1096,6 +1096,13 @@ static const struct test apx_nci[] = {
#undef COND
};
+static const struct test cmpccxadd[] = {
+#define COND(c, n) \
+ INSN(cmp##c##xadd, 66, 0f38, e0 | (n), el, dq, no)
+ CONDS(),
+#undef COND
+};
+
#undef CONDS
static const struct test adx[] = {
@@ -1737,6 +1744,7 @@ void evex_disp8_test(void *instr, struct
RUN(avx512bw);
RUN(bmi1);
RUN(bmi2);
+ RUN(cmpccxadd);
#undef RUN
--- a/tools/tests/x86_emulator/predicates.c
+++ b/tools/tests/x86_emulator/predicates.c
@@ -1422,22 +1422,7 @@ static const struct vex {
{ { 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 */
- { { 0xe0 }, 2, F, W, pfx_66, Wn, L0 }, /* cmpoxadd */
- { { 0xe1 }, 2, F, W, pfx_66, Wn, L0 }, /* cmpnoxadd */
- { { 0xe2 }, 2, F, W, pfx_66, Wn, L0 }, /* cmpbxadd */
- { { 0xe3 }, 2, F, W, pfx_66, Wn, L0 }, /* cmpnbxadd */
- { { 0xe4 }, 2, F, W, pfx_66, Wn, L0 }, /* cmpexadd */
- { { 0xe5 }, 2, F, W, pfx_66, Wn, L0 }, /* cmpnexadd */
- { { 0xe6 }, 2, F, W, pfx_66, Wn, L0 }, /* cmpbexadd */
- { { 0xe7 }, 2, F, W, pfx_66, Wn, L0 }, /* cmpaxadd */
- { { 0xe8 }, 2, F, W, pfx_66, Wn, L0 }, /* cmpsxadd */
- { { 0xe9 }, 2, F, W, pfx_66, Wn, L0 }, /* cmpnsxadd */
- { { 0xea }, 2, F, W, pfx_66, Wn, L0 }, /* cmppxadd */
- { { 0xeb }, 2, F, W, pfx_66, Wn, L0 }, /* cmpnpxadd */
- { { 0xec }, 2, F, W, pfx_66, Wn, L0 }, /* cmplxadd */
- { { 0xed }, 2, F, W, pfx_66, Wn, L0 }, /* cmpgexadd */
- { { 0xee }, 2, F, W, pfx_66, Wn, L0 }, /* cmplexadd */
- { { 0xef }, 2, F, W, pfx_66, Wn, L0 }, /* cmpgxadd */
+ CND(0xe0, 2, F, W, pfx_66, Wn, L0 ), /* cmp<cc>xadd */
{ { 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 */
@@ -2067,6 +2052,7 @@ static const struct evex {
{ { 0xde }, 2, T, R, pfx_66, WIG, Ln }, /* vaesdec */
{ { 0xdf }, 2, T, R, pfx_66, WIG, Ln }, /* vaesdeclast */
#ifdef __x86_64__
+ CND(0xe0, 2, F, W, pfx_66, Wn, L0 ), /* cmp<cc>xadd */
{ { 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 */
--- a/tools/tests/x86_emulator/test_x86_emulator.c
+++ b/tools/tests/x86_emulator/test_x86_emulator.c
@@ -2293,6 +2293,41 @@ int main(int argc, char **argv)
(res[2] + 1) )
goto fail;
printf("okay\n");
+
+ if ( cpu_has_apx_f )
+ {
+ printf("%-40s", "Testing cmpnpxadd %ebx,%r9d,-4(%r18)...");
+ instr[0] = 0x62; instr[1] = 0x7a; instr[2] = 0x65; instr[3] = 0x08;
+ instr[4] = 0xeb; instr[5] = 0x4a; instr[6] = 0xfc;
+ regs.rip = (unsigned long)&instr[0];
+ load_egpr(18, (unsigned long)(res + 2));
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rip != (unsigned long)&instr[7]) ||
+ (regs.r9 != 0x13253749) ||
+ (regs.rbx != 0x02030405) ||
+ ((regs.eflags & EFLAGS_MASK) !=
+ (EFLAGS_ALWAYS_SET | X86_EFLAGS_PF)) ||
+ (res[0] + 1) ||
+ (res[1] != 0x13253749) ||
+ (res[2] + 1) )
+ goto fail;
+
+ regs.rip = (unsigned long)&instr[0];
+ regs.r9 <<= 8;
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rip != (unsigned long)&instr[7]) ||
+ (regs.r9 != 0x13253749) ||
+ (regs.rbx != 0x02030405) ||
+ ((regs.eflags & EFLAGS_MASK) !=
+ (EFLAGS_ALWAYS_SET | X86_EFLAGS(CF, SF))) ||
+ (res[0] + 1) ||
+ (res[1] != 0x15283b4e) ||
+ (res[2] + 1) )
+ goto fail;
+ printf("okay\n");
+ }
}
else
printf("skipped\n");
--- a/xen/arch/x86/x86_emulate/decode.c
+++ b/xen/arch/x86/x86_emulate/decode.c
@@ -1267,6 +1267,8 @@ decode_0f38(struct x86_emulate_state *s,
case X86EMUL_OPC_VEX_F2(0, 0xf7): /* shrx */
break;
+ case X86EMUL_OPC_EVEX_66(0, 0xe0) ...
+ X86EMUL_OPC_EVEX_66(0, 0xef): /* cmp<cc>xadd */
case X86EMUL_OPC_EVEX(0, 0xf2): /* andn */
case X86EMUL_OPC_EVEX(0, 0xf3): /* Grp 17 */
case X86EMUL_OPC_EVEX(0, 0xf5): /* bzhi */
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -7323,6 +7323,10 @@ x86_emulate(
#endif /* !X86EMUL_NO_SIMD */
+ case X86EMUL_OPC_EVEX_66(0x0f38, 0xe0) ...
+ X86EMUL_OPC_EVEX_66(0x0f38, 0xef): /* cmp<cc>xadd r,r,m */
+ generate_exception_if(evex.lr || evex.brs || evex.opmsk, X86_EXC_UD);
+ fallthrough;
case X86EMUL_OPC_VEX_66(0x0f38, 0xe0) ...
X86EMUL_OPC_VEX_66(0x0f38, 0xef): /* cmp<cc>xadd r,r,m */
generate_exception_if(!mode_64bit() || dst.type != OP_MEM || vex.l,
@@ -9355,6 +9359,7 @@ x86_emulate(
#undef rex_prefix
#undef lock_prefix
#undef vex
+#undef evex
#undef ea
int x86_emul_rmw(
@@ -9443,6 +9448,8 @@ int x86_emul_rmw(
pvex->b = 1;
pvex->r = 1;
pvex->reg = 0xf; /* rAX */
+ if ( s->promoted_evex )
+ pvex->opcx &= 7; /* See copy_VEX(). */
buf[3] = ctxt->opcode;
buf[4] = 0x11; /* reg=rDX r/m=(%RCX) */
place_ret(&buf[5]);
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 24/30] x86emul: support APX+{LZ,POP}CNT insns
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (22 preceding siblings ...)
2026-11-11 14:29 ` [PATCH 23/30] x86emul: support APX+CMPccXADD insns Jan Beulich
@ 2026-11-11 14:30 ` Jan Beulich
2026-11-11 14:30 ` [PATCH 25/30] x86emul: support APX+MOVBE insn Jan Beulich
` (5 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:30 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
Besides adding table entries and case labels there's very little to do
here; generic map 4 handling takes care of things.
To aid making the connection between ABM and LZCNT (the sole insn the
feature bit covers), amend the comment in cpufeatures.h at the same
time.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
--- a/tools/tests/x86_emulator/evex-disp8.c
+++ b/tools/tests/x86_emulator/evex-disp8.c
@@ -1154,6 +1154,16 @@ static const struct test bmi2[] = {
INSN(shrx, f2, 0f38, f7, el, dq, no),
};
+static const struct test lzcnt[] = {
+ INSN(lzcnt, , map4, f5, el, dq, no),
+ INSN(lzcntw, 66, map4, f5, el, w, no),
+};
+
+static const struct test popcnt[] = {
+ INSN(popcnt, , map4, 88, el, dq, no),
+ INSN(popcntw, 66, map4, 88, 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 };
@@ -1745,6 +1755,8 @@ void evex_disp8_test(void *instr, struct
RUN(bmi1);
RUN(bmi2);
RUN(cmpccxadd);
+ RUN(lzcnt);
+ RUN(popcnt);
#undef RUN
--- a/tools/tests/x86_emulator/predicates.c
+++ b/tools/tests/x86_emulator/predicates.c
@@ -2260,6 +2260,8 @@ static const struct evex {
{ { 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> */
+ { { 0x88 }, 2, T, R, pfx_no, Wn, L0 }, /* popcnt */
+ { { 0x88 }, 2, T, R, pfx_66, Wn, L0 }, /* popcnt */
{ { 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 */
@@ -2344,6 +2346,8 @@ static const struct evex {
{ { 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 */
+ { { 0xf5 }, 2, T, R, pfx_no, Wn, L0 }, /* lzcnt */
+ { { 0xf5 }, 2, T, R, pfx_66, Wn, L0 }, /* lzcnt */
{ { 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
@@ -2516,6 +2516,26 @@ int main(int argc, char **argv)
(regs.eip != (unsigned long)&instr[5]) )
goto fail;
printf("okay\n");
+
+ if ( true /* cpu_has_apx_nci_ndd_nf */ )
+ {
+ cpu_policy.apx.apx_nci_ndd_nf = true;
+
+ printf("%-40s", "Testing {nf}popcnt (%rdx),%rcx...");
+ instr[0] = 0x62; instr[1] = 0xf4; instr[2] = 0xfc; instr[3] = 0x0c;
+ instr[4] = 0x88; instr[5] = 0x0a;
+ res[1] = 0x23456789;
+ regs.eflags = EFLAGS_MASK;
+ regs.rip = (unsigned long)&instr[0];
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) || regs.rcx != 34 ||
+ (regs.eflags != EFLAGS_MASK) ||
+ (regs.rip != (unsigned long)&instr[6]) )
+ goto fail;
+ printf("okay\n");
+
+ cpu_policy.apx.apx_nci_ndd_nf = nci_ndd_nf;
+ }
#endif
}
else
--- a/tools/tests/x86_emulator/x86-emulate.h
+++ b/tools/tests/x86_emulator/x86-emulate.h
@@ -234,6 +234,7 @@ void wrpkru(unsigned int val);
#define cpu_has_xgetbv1 (cpu_has_xsave && cpu_policy.xstate.xgetbv1)
#define cpu_has_3dnow_ext cpu_policy.extd._3dnowext
+#define cpu_has_lzcnt cpu_policy.extd.abm
#define cpu_has_sse4a cpu_policy.extd.sse4a
#define cpu_has_xop (cpu_policy.extd.xop && xcr0_mask(6))
#define cpu_has_fma4 (cpu_policy.extd.fma4 && xcr0_mask(6))
--- a/xen/arch/x86/x86_emulate/decode.c
+++ b/xen/arch/x86/x86_emulate/decode.c
@@ -730,6 +730,10 @@ static const struct map4_table {
[vex_np] = { },
[vex_66] = { },
},
+ [0x88] = { /* POPCNT ({,d,q}word) */
+ [vex_np] = { .nf = true, .nci_ndd_nf = true },
+ [vex_66] = { .nf = true, .nci_ndd_nf = true },
+ },
[0x8f] = { /* Grp1a */
[vex_np] = { .nd = true },
},
@@ -792,6 +796,10 @@ static const struct map4_table {
[vex_np] = { .nf = true, .nci_ndd_nf = true },
[vex_66] = { .nf = true, .nci_ndd_nf = true },
},
+ [0xf5] = { /* LZCNT ({,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 },
},
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -5668,7 +5668,9 @@ x86_emulate(
dst.val = (uint16_t)src.val;
break;
- case X86EMUL_OPC_F3(0x0f, 0xb8): /* popcnt r/m,r */
+ case X86EMUL_OPC_F3 (0x0f, 0xb8): /* popcnt r/m,r */
+ case X86EMUL_OPC_EVEX (4, 0x88): /* popcnt r/m,reg */
+ case X86EMUL_OPC_EVEX_66(4, 0x88): /* popcnt r/m,r16 */
vcpu_must_have(popcnt);
asm ( "popcnt %1,%0" : "=r" (dst.val) : "rm" (src.val) );
_regs.eflags &= ~EFLAGS_MASK;
@@ -5709,8 +5711,12 @@ x86_emulate(
}
break;
+ case X86EMUL_OPC_EVEX (4, 0xf5): /* lzcnt r/m,reg */
+ case X86EMUL_OPC_EVEX_66(4, 0xf5): /* lzcnt r/m,r16 */
+ vcpu_must_have(lzcnt);
+ fallthrough;
case X86EMUL_OPC(0x0f, 0xbd): /* bsr or lzcnt */
- if ( vex.pfx == vex_f3 )
+ if ( vex.pfx == vex_f3 || state->promoted_evex )
emulate_2op_SrcV_srcmem("lzcnt", src, dst, _regs.eflags);
else
{
--- a/xen/include/public/arch-x86/cpufeatureset.h
+++ b/xen/include/public/arch-x86/cpufeatureset.h
@@ -159,7 +159,7 @@ XEN_CPUFEATURE(CMP_LEGACY, 3*32+ 1) /
XEN_CPUFEATURE(SVM, 3*32+ 2) /*h Secure virtual machine */
XEN_CPUFEATURE(EXTAPIC, 3*32+ 3) /* Extended APIC space */
XEN_CPUFEATURE(CR8_LEGACY, 3*32+ 4) /*S CR8 in 32-bit mode */
-XEN_CPUFEATURE(ABM, 3*32+ 5) /*A Advanced bit manipulation */
+XEN_CPUFEATURE(ABM, 3*32+ 5) /*A LZCNT insn (Advanced Bit Manipulation) */
XEN_CPUFEATURE(SSE4A, 3*32+ 6) /*A SSE-4A */
XEN_CPUFEATURE(MISALIGNSSE, 3*32+ 7) /*A Misaligned SSE mode */
XEN_CPUFEATURE(3DNOWPREFETCH, 3*32+ 8) /*A 3DNow prefetch instructions */
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 25/30] x86emul: support APX+MOVBE insn
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (23 preceding siblings ...)
2026-11-11 14:30 ` [PATCH 24/30] x86emul: support APX+{LZ,POP}CNT insns Jan Beulich
@ 2026-11-11 14:30 ` Jan Beulich
2026-11-11 14:31 ` [PATCH 26/30] x86emul: support APX+MOVDIR/ENQCMD insns Jan Beulich
` (4 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:30 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
Besides adding table entries and case labels there's very little to do
here; generic map 4 handling takes care of things.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
--- a/tools/tests/x86_emulator/evex-disp8.c
+++ b/tools/tests/x86_emulator/evex-disp8.c
@@ -1159,6 +1159,13 @@ static const struct test lzcnt[] = {
INSN(lzcntw, 66, map4, f5, el, w, no),
};
+static const struct test movbe[] = {
+ INSN(movbe, , map4, 60, el, dq, no),
+ INSN(movbe, , map4, 61, el, dq, no),
+ INSN(movbew, 66, map4, 60, el, w, no),
+ INSN(movbew, 66, map4, 61, el, w, no),
+};
+
static const struct test popcnt[] = {
INSN(popcnt, , map4, 88, el, dq, no),
INSN(popcntw, 66, map4, 88, el, w, no),
@@ -1756,6 +1763,7 @@ void evex_disp8_test(void *instr, struct
RUN(bmi2);
RUN(cmpccxadd);
RUN(lzcnt);
+ RUN(movbe);
RUN(popcnt);
#undef RUN
--- a/tools/tests/x86_emulator/predicates.c
+++ b/tools/tests/x86_emulator/predicates.c
@@ -2202,6 +2202,10 @@ static const struct evex {
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> */
+ { { 0x60 }, 2, T, R, pfx_no, Wn, L0 }, /* movbe */
+ { { 0x60 }, 2, T, R, pfx_66, Wn, L0 }, /* movbe */
+ { { 0x61 }, 2, T, W, pfx_no, Wn, L0 }, /* movbe */
+ { { 0x61 }, 2, T, W, pfx_66, Wn, L0 }, /* movbe */
{ { 0x66 }, 2, T, R, pfx_66, Wn, L0 }, /* adcx */
{ { 0x66 }, 2, T, R, pfx_f3, Wn, L0 }, /* adox */
{ { 0x69 }, 6, T, R, pfx_no, Wn, L0 }, /* imul */
--- a/tools/tests/x86_emulator/test_x86_emulator.c
+++ b/tools/tests/x86_emulator/test_x86_emulator.c
@@ -2475,6 +2475,35 @@ int main(int argc, char **argv)
goto fail;
printf("okay\n");
+#ifdef __x86_64__
+ printf("%-40s", "Testing movbe %rax,%rcx...");
+ instr[0] = 0x62; instr[1] = 0xf4; instr[2] = 0xfc; instr[3] = 0x08;
+ instr[4] = 0x60; instr[5] = 0xc8;
+ regs.rip = (unsigned long)&instr[0];
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rcx != 0x1234567800000000UL) ||
+ (regs.rip != (unsigned long)&instr[6]) )
+ goto fail;
+ printf("okay\n");
+
+ if ( cpu_has_apx_f )
+ {
+ printf("%-40s", "Testing movbe %r30w,2(%r31)...");
+ instr[1] = 0x4c; instr[2] = 0x7d;
+ instr[4] = 0x61; instr[5] = 0x77; instr[6] = 0x02;
+ regs.rip = (unsigned long)&instr[0];
+ load_egpr(30, 0x12345678);
+ load_egpr(31, (unsigned long)res);
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (*res != 0x78561234) ||
+ (regs.rip != (unsigned long)&instr[7]) )
+ goto fail;
+ printf("okay\n");
+ }
+#endif
+
printf("%-40s", "Testing popcnt (%edx),%cx...");
if ( cpu_has_popcnt )
{
--- a/tools/tests/x86_emulator/x86-emulate.h
+++ b/tools/tests/x86_emulator/x86-emulate.h
@@ -168,6 +168,7 @@ void wrpkru(unsigned int val);
#define cpu_has_fma (cpu_policy.basic.fma && xcr0_mask(6))
#define cpu_has_sse4_1 cpu_policy.basic.sse4_1
#define cpu_has_sse4_2 cpu_policy.basic.sse4_2
+#define cpu_has_movbe cpu_policy.basic.movbe
#define cpu_has_popcnt cpu_policy.basic.popcnt
#define cpu_has_aesni cpu_policy.basic.aesni
#define cpu_has_avx (cpu_policy.basic.avx && xcr0_mask(6))
--- a/xen/arch/x86/x86_emulate/decode.c
+++ b/xen/arch/x86/x86_emulate/decode.c
@@ -691,6 +691,14 @@ static const struct map4_table {
[vex_f2] = { .to_mem = true, .byte = true, .nd = true,
.nci_ndd_nf = true },
},
+ [0x60] = { /* MOVBE ({,d,q}word) */
+ [vex_np] = { },
+ [vex_66] = { },
+ },
+ [0x61] = { /* MOVBE ({,d,q}word) */
+ [vex_np] = { .to_mem = true },
+ [vex_66] = { .to_mem = true },
+ },
[0x66] = {
[vex_66] = { .nd = true }, /* ADCX */
[vex_f3] = { .nd = true }, /* ADOX */
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -7345,6 +7345,11 @@ x86_emulate(
case X86EMUL_OPC(0x0f38, 0xf0): /* movbe m,r */
case X86EMUL_OPC(0x0f38, 0xf1): /* movbe r,m */
generate_exception_if(ea.type != OP_MEM, X86_EXC_UD);
+ fallthrough;
+ case X86EMUL_OPC_EVEX (4, 0x60): /* movbe r/m,reg */
+ case X86EMUL_OPC_EVEX_66(4, 0x60): /* movbe r/m,r16 */
+ case X86EMUL_OPC_EVEX (4, 0x61): /* movbe reg,r/m */
+ case X86EMUL_OPC_EVEX_66(4, 0x61): /* movbe r16,r/m */
vcpu_must_have(movbe);
switch ( op_bytes )
{
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 26/30] x86emul: support APX+MOVDIR/ENQCMD insns
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (24 preceding siblings ...)
2026-11-11 14:30 ` [PATCH 25/30] x86emul: support APX+MOVBE insn Jan Beulich
@ 2026-11-11 14:31 ` Jan Beulich
2026-11-11 14:31 ` [PATCH 27/30] x86emul: support APX+MOVRS insn Jan Beulich
` (3 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:31 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
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@suse.com>
--- 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) ...
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 27/30] x86emul: support APX+MOVRS insn
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (25 preceding siblings ...)
2026-11-11 14:31 ` [PATCH 26/30] x86emul: support APX+MOVDIR/ENQCMD insns Jan Beulich
@ 2026-11-11 14:31 ` Jan Beulich
2026-11-11 14:31 ` [PATCH 28/30] x86emul: support APX MSR access insns Jan Beulich
` (2 subsequent siblings)
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:31 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
Besides adding table entries and case labels there's once again very
little to do here; generic map 4 handling takes care of things.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
---
SDE: -dmr
--- a/tools/tests/x86_emulator/evex-disp8.c
+++ b/tools/tests/x86_emulator/evex-disp8.c
@@ -1181,6 +1181,12 @@ static const struct test movdiri[] = {
INSN(movdiri, , map4, f9, el, dq, no),
};
+static const struct test movrs[] = {
+ INSN(movrs, , map4, 8a, el, b, no),
+ INSN(movrs, , map4, 8b, el, dq, no),
+ INSN(movrsw, 66, map4, 8b, el, w, no),
+};
+
static const struct test popcnt[] = {
INSN(popcnt, , map4, 88, el, dq, no),
INSN(popcntw, 66, map4, 88, el, w, no),
@@ -1801,6 +1807,7 @@ void evex_disp8_test(void *instr, struct
RUN(movbe);
RUN(movdir64b);
RUN(movdiri);
+ RUN(movrs);
RUN(popcnt);
#undef RUN
--- a/tools/tests/x86_emulator/predicates.c
+++ b/tools/tests/x86_emulator/predicates.c
@@ -2266,6 +2266,9 @@ static const struct evex {
{ { 0x85 }, 2, T, R, pfx_66, Wn, L0, SCC }, /* ctest<scc> */
{ { 0x88 }, 2, T, R, pfx_no, Wn, L0 }, /* popcnt */
{ { 0x88 }, 2, T, R, pfx_66, Wn, L0 }, /* popcnt */
+ { { 0x8a }, 2, T, R, pfx_no, WIG, L0 }, /* movrs */
+ { { 0x8b }, 2, T, R, pfx_no, Wn, L0 }, /* movrs */
+ { { 0x8b }, 2, T, R, pfx_66, Wn, L0 }, /* movrs */
{ { 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 */
--- a/tools/tests/x86_emulator/test_x86_emulator.c
+++ b/tools/tests/x86_emulator/test_x86_emulator.c
@@ -2356,6 +2356,22 @@ int main(int argc, char **argv)
goto fail;
printf("okay\n");
+ if ( cpu_has_apx_f )
+ {
+ printf("%-40s", "Testing movrs 6(%r31),%r30w...");
+ instr[0] = 0x62; instr[1] = 0x4c; instr[2] = 0x7d; instr[3] = 0x08;
+ instr[4] = 0x8b; instr[5] = 0x77; instr[6] = 0x06;
+ regs.rip = (unsigned long)&instr[0];
+ load_egpr(30, 0x8888777766665555UL);
+ load_egpr(31, (unsigned long)res);
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rip != (unsigned long)&instr[7]) ||
+ !check_egpr(30, 0x8888777766668877UL) )
+ goto fail;
+ printf("okay\n");
+ }
+
cpu_policy.feat.movrs = movrs;
}
--- a/xen/arch/x86/x86_emulate/decode.c
+++ b/xen/arch/x86/x86_emulate/decode.c
@@ -738,6 +738,13 @@ static const struct map4_table {
[vex_np] = { },
[vex_66] = { },
},
+ [0x8a] = { /* MOVRS (byte) */
+ [vex_np] = { .byte = true },
+ },
+ [0x8b] = { /* MOVRS ({,d,q}word) */
+ [vex_np] = { },
+ [vex_66] = { },
+ },
[0x88] = { /* POPCNT ({,d,q}word) */
[vex_np] = { .nf = true, .nci_ndd_nf = true },
[vex_66] = { .nf = true, .nci_ndd_nf = true },
@@ -1420,6 +1427,11 @@ decode_map4(struct x86_emulate_state *s,
nf = map4_table[(s->modrm_reg & 7) << 3][s->evex.pfx].nf;
break;
+ case 0x8a: case 0x8b: /* movrs */
+ case 0xf9: /* movdiri */
+ s->desc |= Mov;
+ break;
+
case 0xc0: case 0xc1: case 0xd0 ... 0xd3: /* Grp2 */
/* rcl/rcr do not allow EVEX.nf to be set. */
nf = (s->modrm_reg & 6) != 2;
@@ -1463,10 +1475,6 @@ decode_map4(struct x86_emulate_state *s,
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
@@ -6807,6 +6807,9 @@ x86_emulate(
case X86EMUL_OPC(0x0f38, 0x8a)
... X86EMUL_OPC(0x0f38, 0x8b): /* movrs */
+ case X86EMUL_OPC_EVEX (4, 0x8a): /* movrs r/m,r8 */
+ case X86EMUL_OPC_EVEX (4, 0x8b): /* movrs r/m,reg */
+ case X86EMUL_OPC_EVEX_66(4, 0x8b): /* movrs r/m,r16 */
vcpu_must_have(movrs);
dst.val = src.val;
break;
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 28/30] x86emul: support APX MSR access insns
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (26 preceding siblings ...)
2026-11-11 14:31 ` [PATCH 27/30] x86emul: support APX+MOVRS insn Jan Beulich
@ 2026-11-11 14:31 ` Jan Beulich
2026-11-11 14:32 ` [PATCH 29/30] x86: permit guests to use APX Jan Beulich
2026-11-11 14:33 ` [PATCH 30/30] x86emul: use latched XCR0 in x86emul_get_fpu() Jan Beulich
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:31 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
Them being direct counterparts of the original VEX-encoded insns, there's
nothing really noteworthy here; code is being re-used as much as possible.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
--- a/tools/tests/x86_emulator/predicates.c
+++ b/tools/tests/x86_emulator/predicates.c
@@ -2558,6 +2558,13 @@ static const struct evex {
{ { 0xd6 }, 2, T, R, pfx_f2, W0, Ln }, /* vfcmulcph */
{ { 0xd7 }, 2, T, R, pfx_f3, W0, LIG }, /* vfmulcsh */
{ { 0xd7 }, 2, T, R, pfx_f2, W0, LIG }, /* vfcmulcsh */
+}, evex_map7[] = {
+#ifdef __x86_64__
+ { { 0xf6, 0xc0 }, 6, F, N, pfx_f3, W0, L0 }, /* wrmsrns */
+ { { 0xf6, 0xc0 }, 6, F, N, pfx_f2, W0, L0 }, /* rdmsr */
+ { { 0xf8, 0xc0 }, 6, F, N, pfx_f3, W0, L0 }, /* uwrmsr */
+ { { 0xf8, 0xc0 }, 6, F, N, pfx_f2, W0, L0 }, /* urdmsr */
+#endif
};
#undef CND
@@ -2572,6 +2579,7 @@ static const struct {
{ evex_map4, ARRAY_SIZE(evex_map4) },
{ evex_map5, ARRAY_SIZE(evex_map5) },
{ evex_map6, ARRAY_SIZE(evex_map6) },
+ { evex_map7, ARRAY_SIZE(evex_map7) },
};
#undef Wn
--- a/tools/tests/x86_emulator/test_x86_emulator.c
+++ b/tools/tests/x86_emulator/test_x86_emulator.c
@@ -1960,6 +1960,22 @@ int main(int argc, char **argv)
goto fail;
printf("okay\n");
+ if ( cpu_has_apx_f )
+ {
+ printf("%-40s", "Testing urdmsr %r26,%r25...");
+ instr[0] = 0x62; instr[1] = 0x4c; instr[2] = 0x7f; instr[3] = 0x08;
+ instr[4] = 0xf8; instr[5] = 0xd1;
+ regs.rip = (unsigned long)&instr[0];
+ load_egpr(26, MSR_IA32_APERF);
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rip != (unsigned long)&instr[6]) ||
+ !check_egpr(25,
+ (((uint64_t)APERF_HI_VALUE << 32) | APERF_LO_VALUE)) )
+ goto fail;
+ printf("okay\n");
+ }
+
printf("%-40s", "Testing urdmsr $MSR_IA32_APERF,%rdx...");
instr[0] = 0xc4; instr[1] = 0xe7; instr[2] = 0x7b; instr[3] = 0xf8; instr[4] = 0xc2;
*(uint32_t *)&instr[5] = MSR_IA32_APERF;
@@ -1975,6 +1991,22 @@ int main(int argc, char **argv)
i = cpu_policy.feat.utmr;
cpu_policy.feat.utmr = true;
+ if ( cpu_has_apx_f )
+ {
+ printf("%-40s", "Testing urdmsr $MSR_IA32_APERF,%r18...");
+ instr[0] = 0x62; instr[1] = 0xff; instr[2] = 0x7f; instr[3] = 0x08;
+ instr[4] = 0xf8; instr[5] = 0xc2;
+ *(uint32_t *)&instr[6] = MSR_IA32_APERF;
+ regs.rip = (unsigned long)&instr[0];
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rip != (unsigned long)&instr[10]) ||
+ !check_egpr(18,
+ (((uint64_t)APERF_HI_VALUE << 32) | APERF_LO_VALUE)) )
+ goto fail;
+ printf("okay\n");
+ }
+
printf("%-40s", "Testing uwrmsr %rdi,%rsi...");
instr[0] = 0xf3; instr[1] = 0x0f; instr[2] = 0x38; instr[3] = 0xf8; instr[4] = 0xf7;
regs.rip = (unsigned long)&instr[0];
@@ -1987,6 +2019,22 @@ int main(int argc, char **argv)
goto fail;
printf("okay\n");
+ if ( cpu_has_apx_f )
+ {
+ printf("%-40s", "Testing uwrmsr %r31,%r30...");
+ instr[0] = 0x62; instr[1] = 0x4c; instr[2] = 0x7e; instr[3] = 0x08;
+ instr[4] = 0xf8; instr[5] = 0xf7;
+ regs.rip = (unsigned long)&instr[0];
+ load_egpr(30, MSR_UINTR_TIMER);
+ load_egpr(31, 0x1122334455667788UL);
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rip != (unsigned long)&instr[6]) ||
+ (uintr_timer != 0x1122334455667788UL) )
+ goto fail;
+ printf("okay\n");
+ }
+
printf("%-40s", "Testing uwrmsr %rsi,$MSR_UINTR_TIMER...");
instr[0] = 0xc4; instr[1] = 0xe7; instr[2] = 0x7a; instr[3] = 0xf8; instr[4] = 0xc6;
*(uint32_t *)&instr[5] = MSR_UINTR_TIMER;
@@ -1999,8 +2047,6 @@ int main(int argc, char **argv)
goto fail;
printf("okay\n");
- cpu_policy.feat.utmr = i;
-
printf("%-40s", "Testing uwrmsr %rsi,$MSR_UARCH_MISC_CTRL...");
*(uint32_t *)&instr[5] = MSR_UARCH_MISC_CTRL;
regs.rip = (unsigned long)&instr[0];
@@ -2011,6 +2057,24 @@ int main(int argc, char **argv)
goto fail;
printf("okay\n");
+ if ( cpu_has_apx_f )
+ {
+ printf("%-40s", "Testing uwrmsr %r30,$MSR_UINTR_TIMER...");
+ instr[0] = 0x62; instr[1] = 0xdf; instr[2] = 0x7e; instr[3] = 0x08;
+ instr[4] = 0xf8; instr[5] = 0xc6;
+ *(uint32_t *)&instr[6] = MSR_UINTR_TIMER;
+ regs.rip = (unsigned long)&instr[0];
+ load_egpr(30, 0x9988776655443322UL);
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rip != (unsigned long)&instr[10]) ||
+ (uintr_timer != 0x9988776655443322UL) )
+ goto fail;
+ printf("okay\n");
+ }
+
+ cpu_policy.feat.utmr = i;
+
printf("%-40s", "Testing rdmsr $MSR_GS_BASE,%rdx...");
instr[0] = 0xc4; instr[1] = 0xe7; instr[2] = 0x7b; instr[3] = 0xf6; instr[4] = 0xc2;
*(uint32_t *)&instr[5] = MSR_GS_BASE;
@@ -2023,6 +2087,35 @@ int main(int argc, char **argv)
goto fail;
printf("okay\n");
+ if ( cpu_has_apx_f )
+ {
+ printf("%-40s", "Testing rdmsr $MSR_GS_BASE,%r26...");
+ instr[0] = 0x62; instr[1] = 0xdf; instr[2] = 0x7f; instr[3] = 0x08;
+ instr[4] = 0xf6; instr[5] = 0xc2;
+ *(uint32_t *)&instr[6] = MSR_GS_BASE;
+ regs.rip = (unsigned long)&instr[0];
+ load_egpr(26, ~gs_base);
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rip != (unsigned long)&instr[10]) ||
+ !check_egpr(26, gs_base) )
+ goto fail;
+ printf("okay\n");
+
+ printf("%-40s", "Testing wrmsrns %r22,$MSR_SHADOW_GS_BASE...");
+ instr[0] = 0x62; instr[1] = 0xff; instr[2] = 0x7e; instr[3] = 0x08;
+ instr[4] = 0xf6; instr[5] = 0xc6;
+ *(uint32_t *)&instr[6] = MSR_SHADOW_GS_BASE;
+ regs.rip = (unsigned long)&instr[0];
+ load_egpr(22, 0x776655443322UL);
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.rip != (unsigned long)&instr[10]) ||
+ (gs_base_shadow != 0x776655443322UL) )
+ goto fail;
+ printf("okay\n");
+ }
+
printf("%-40s", "Testing wrmsrns %rsi,$MSR_SHADOW_GS_BASE...");
instr[0] = 0xc4; instr[1] = 0xe7; instr[2] = 0x7a; instr[3] = 0xf6; instr[4] = 0xc6;
*(uint32_t *)&instr[5] = MSR_SHADOW_GS_BASE;
--- a/xen/arch/x86/x86_emulate/decode.c
+++ b/xen/arch/x86/x86_emulate/decode.c
@@ -1469,8 +1469,15 @@ decode_map4(struct x86_emulate_state *s,
}
break;
- case 0xf8: /* movdir64b / enqcmd{,s} */
+ case 0xf8: /* movdir64b / enqcmd{,s} / u{rd,wr}msr */
/* Make these match their legacy forms. */
+ if ( s->modrm_mod == 3 )
+ {
+ s->desc = DstMem | SrcReg | Mov;
+ s->op_bytes = 8;
+ s->simd_size = simd_none;
+ break;
+ }
s->desc = DstReg | Mov;
s->op_bytes = 0;
break;
@@ -1892,6 +1899,8 @@ int x86emul_decode(struct x86_emulate_st
break;
case vex_map7:
+ if ( evex_encoded() )
+ s->promoted_evex = true;
opcode |= MASK_INSR(7, X86EMUL_OPC_EXT_MASK);
/*
* No table lookup here for now, as there are only two
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -7589,6 +7589,11 @@ x86_emulate(
state->simd_size = simd_none;
break;
+ case X86EMUL_OPC_EVEX_F3(7, 0xf6): /* wrmsrns r64,imm32 */
+ case X86EMUL_OPC_EVEX_F2(7, 0xf6): /* rdmsr imm32,r64 */
+ generate_exception_if((evex.lr || evex.brs || evex.opmsk || !evex.RX),
+ X86_EXC_UD);
+ fallthrough;
case X86EMUL_OPC_VEX_F3(7, 0xf6): /* wrmsrns r64,imm32 */
case X86EMUL_OPC_VEX_F2(7, 0xf6): /* rdmsr imm32,r64 */
generate_exception_if((!mode_64bit() || ea.type != OP_REG ||
@@ -7620,16 +7625,26 @@ x86_emulate(
case X86EMUL_OPC_F3(0x0f38, 0xf8): /* enqcmds r,m512 / uwrmsr r64,r32 */
case X86EMUL_OPC_F2(0x0f38, 0xf8): /* enqcmd r,m512 / urdmsr r32,r64 */
+ case X86EMUL_OPC_EVEX_F3(4, 0xf8): /* enqcmds r,m512 / uwrmsr r64,r32 */
+ case X86EMUL_OPC_EVEX_F2(4, 0xf8): /* enqcmd r,m512 / urdmsr r32,r64 */
if ( ea.type == OP_MEM )
goto enqcmd;
imm1 = src.val;
fallthrough;
+ case X86EMUL_OPC_EVEX_F3(7, 0xf8): /* uwrmsr r64,imm32 */
+ case X86EMUL_OPC_EVEX_F2(7, 0xf8): /* urdmsr imm32,r64 */
+ if ( evex.opcx == vex_map7 )
+ generate_exception_if((evex.lr || evex.brs || evex.opmsk ||
+ !evex.RX),
+ X86_EXC_UD);
+ fallthrough;
case X86EMUL_OPC_VEX_F3(7, 0xf8): /* uwrmsr r64,imm32 */
case X86EMUL_OPC_VEX_F2(7, 0xf8): /* urdmsr imm32,r64 */
generate_exception_if(!mode_64bit() || ea.type != OP_REG, X86_EXC_UD);
generate_exception_if(vex.l || vex.w, X86_EXC_UD);
- generate_exception_if(vex.opcx && ((modrm_reg & 7) || vex.reg != 0xf),
+ generate_exception_if(vex.opcx == vex_map7 && (modrm_reg & 7),
X86_EXC_UD);
+ generate_exception_if(vex.opcx && vex.reg != 0xf, X86_EXC_UD);
vcpu_must_have(user_msr);
fail_if(!ops->read_msr);
if ( ops->read_msr(MSR_USER_MSR_CTL, &msr_val, ctxt) != X86EMUL_OKAY )
@@ -7684,9 +7699,6 @@ 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);
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 29/30] x86: permit guests to use APX
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (27 preceding siblings ...)
2026-11-11 14:31 ` [PATCH 28/30] x86emul: support APX MSR access insns Jan Beulich
@ 2026-11-11 14:32 ` Jan Beulich
2026-11-11 14:33 ` [PATCH 30/30] x86emul: use latched XCR0 in x86emul_get_fpu() Jan Beulich
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:32 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné,
Oleksii Kurochko, community.manager@xenproject.org
The feature is marked experimental (for only parts of the code actually
having got tested yet, while other parts require respective hardware)
and opt-in for guests.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@ The format is based on [Keep a Changelog
- On x86:
- Support for AVX10.2. (Experimental)
- Support for AVX10-V2-AUX. (Experimental)
+ - Support for APX. (Experimental)
### Removed
- On Arm:
--- a/SUPPORT.md
+++ b/SUPPORT.md
@@ -87,6 +87,10 @@ For the Cortex A77 r0p0 - r1p0, see Erra
Status: Tech Preview
+### x86 APX
+
+ Status: Experimental
+
### IOMMU
Status, AMD IOMMU: Supported
--- a/xen/include/public/arch-x86/cpufeatureset.h
+++ b/xen/include/public/arch-x86/cpufeatureset.h
@@ -367,7 +367,7 @@ XEN_CPUFEATURE(USER_MSR, 15*32
XEN_CPUFEATURE(UIRET_UIF, 15*32+17) /* UIRET updates UIF */
XEN_CPUFEATURE(CET_SSS, 15*32+18) /* CET Supervisor Shadow Stacks safe to use */
XEN_CPUFEATURE(AVX10, 15*32+19) /*a AVX10 Converged Vector ISA */
-XEN_CPUFEATURE(APX_F, 15*32+21) /* APX Foundation Instructions */
+XEN_CPUFEATURE(APX_F, 15*32+21) /*a APX Foundation Instructions */
XEN_CPUFEATURE(SLSM, 15*32+24) /* Static Lockstep Mode */
/* Intel-defined CPU features, MSR_ARCH_CAPS 0x10a.eax, word 16 */
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 30/30] x86emul: use latched XCR0 in x86emul_get_fpu()
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
` (28 preceding siblings ...)
2026-11-11 14:32 ` [PATCH 29/30] x86: permit guests to use APX Jan Beulich
@ 2026-11-11 14:33 ` Jan Beulich
29 siblings, 0 replies; 31+ messages in thread
From: Jan Beulich @ 2026-11-11 14:33 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Teddy Astie, Roger Pau Monné
Avoid re-reading, and instead pass state into the function. For
get_fpu() to use "s" as new argument, we need a new local variable in
x86_emulate() though.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
---
That new local "s" can likely be leveraged to replace explicit uses of
"state" in the function (past the point where the alias #define is).
Long term we probably want to aim at consistently using "s" everywhere
where a struct x86_emulate_state * variable/parameter is needed.
x86emul_get_fpu() isn't using "s" right away just because that would end
up inconsistent with adjacent code (put_fpu() first and foremost). I
could certainly change that.
--- a/xen/arch/x86/x86_emulate/private.h
+++ b/xen/arch/x86/x86_emulate/private.h
@@ -749,12 +749,13 @@ int x86emul_get_cpl(struct x86_emulate_c
const struct x86_emulate_ops *ops);
int x86emul_get_fpu(enum x86_emulate_fpu_type type,
+ const struct x86_emulate_state *state,
struct x86_emulate_ctxt *ctxt,
const struct x86_emulate_ops *ops);
#define get_fpu(type) \
do { \
- rc = x86emul_get_fpu(fpu_type = (type), ctxt, ops); \
+ rc = x86emul_get_fpu(fpu_type = (type), s, ctxt, ops); \
if ( rc ) goto done; \
} while (0)
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -398,36 +398,30 @@ do {
int x86emul_get_fpu(
enum x86_emulate_fpu_type type,
+ const struct x86_emulate_state *state,
struct x86_emulate_ctxt *ctxt,
const struct x86_emulate_ops *ops)
{
- uint64_t xcr0;
int rc;
fail_if(!ops->get_fpu);
ASSERT(type != X86EMUL_FPU_none);
- if ( type < X86EMUL_FPU_ymm || !ops->read_xcr ||
- ops->read_xcr(0, &xcr0, ctxt) != X86EMUL_OKAY )
- {
- ASSERT(!ctxt->event_pending);
- xcr0 = 0;
- }
-
switch ( type )
{
case X86EMUL_FPU_zmm:
- if ( !(xcr0 & X86_XCR0_ZMM) || !(xcr0 & X86_XCR0_HI_ZMM) ||
- !(xcr0 & X86_XCR0_OPMASK) )
+ if ( !(state->xcr0 & X86_XCR0_ZMM) ||
+ !(state->xcr0 & X86_XCR0_HI_ZMM) ||
+ !(state->xcr0 & X86_XCR0_OPMASK) )
return X86EMUL_UNHANDLEABLE;
/* fall through */
case X86EMUL_FPU_ymm:
- if ( !(xcr0 & X86_XCR0_SSE) || !(xcr0 & X86_XCR0_YMM) )
+ if ( !(state->xcr0 & X86_XCR0_SSE) || !(state->xcr0 & X86_XCR0_YMM) )
return X86EMUL_UNHANDLEABLE;
break;
case X86EMUL_FPU_opmask:
- if ( !(xcr0 & X86_XCR0_SSE) || !(xcr0 & X86_XCR0_OPMASK) )
+ if ( !(state->xcr0 & X86_XCR0_SSE) || !(state->xcr0 & X86_XCR0_OPMASK) )
return X86EMUL_UNHANDLEABLE;
break;
@@ -1361,7 +1355,7 @@ x86_emulate(
/* Shadow copy of register state. Committed on successful emulation. */
struct cpu_user_regs _regs = *ctxt->regs;
const struct cpu_policy *__maybe_unused cp = ctxt->cpu_policy;
- struct x86_emulate_state state;
+ struct x86_emulate_state state, *s = &state;
int rc;
uint8_t b, d, *opc = NULL;
unsigned int first_byte = 0, elem_bytes, insn_bytes = 0;
@@ -1506,7 +1500,7 @@ x86_emulate(
/* With a memory operand, fetch the mask register in use (if any). */
if ( ea.type == OP_MEM && !state->promoted_evex && evex.opmsk &&
x86emul_get_fpu(fpu_type = X86EMUL_FPU_opmask,
- ctxt, ops) == X86EMUL_OKAY )
+ s, ctxt, ops) == X86EMUL_OKAY )
{
uint8_t *stb = get_stub(stub);
^ permalink raw reply [flat|nested] 31+ messages in thread
end of thread, other threads:[~2026-11-11 14:33 UTC | newest]
Thread overview: 31+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-11 14:19 [PATCH 00/30] x86: permit guests to use APX Jan Beulich
2026-11-11 14:20 ` [PATCH 01/30] x86/CPUID: introduce APX_F Jan Beulich
2026-11-11 14:21 ` [PATCH 02/30] x86/CPUID: enable APX sub-leaf Jan Beulich
2026-11-11 14:21 ` [PATCH 03/30] x86/xstate: enable APX_F Jan Beulich
2026-11-11 14:22 ` [PATCH 04/30] x86emul: add eGPR parameter to decode_gpr() Jan Beulich
2026-11-11 14:22 ` [PATCH 05/30] x86emul: replace earlier relaxation of decode_gpr() Jan Beulich
2026-11-11 14:22 ` [PATCH 06/30] VMX: support APX Jan Beulich
2026-11-11 14:23 ` [PATCH 07/30] x86emul: latch XCR0 early during decode Jan Beulich
2026-11-11 14:23 ` [PATCH 08/30] x86emul: support REX2-prefixed insns Jan Beulich
2026-11-11 14:24 ` [PATCH 09/30] x86emul: APX_F REX2 testing Jan Beulich
2026-11-11 14:24 ` [PATCH 10/30] x86emul: support JMPABS Jan Beulich
2026-11-11 14:25 ` [PATCH 11/30] x86emul: switch to EVEX.R{3,4} nomenclature Jan Beulich
2026-11-11 14:25 ` [PATCH 12/30] x86emul: switch to EVEX.B{3,4} nomenclature Jan Beulich
2026-11-11 14:25 ` [PATCH 13/30] x86emul: switch to EVEX.X3 nomenclature Jan Beulich
2026-11-11 14:26 ` [PATCH 14/30] x86emul: support basic APX EVEX-encoded ALU insns Jan Beulich
2026-11-11 14:26 ` [PATCH 15/30] x86emul: support APX EVEX-encoded shift/rotate insns Jan Beulich
2026-11-11 14:27 ` [PATCH 16/30] x86emul: support remaining basic APX EVEX-Map4 insns Jan Beulich
2026-11-11 14:27 ` [PATCH 17/30] x86emul: support APX SCC insns Jan Beulich
2026-11-11 14:27 ` [PATCH 18/30] x86emul: support APX+ADX insns Jan Beulich
2026-11-11 14:28 ` [PATCH 19/30] x86emul: extend copy_VEX() for APX use Jan Beulich
2026-11-11 14:28 ` [PATCH 20/30] x86emul: support APX+AVX512 insns Jan Beulich
2026-11-11 14:29 ` [PATCH 21/30] x86emul: support APX+BMI1 insns Jan Beulich
2026-11-11 14:29 ` [PATCH 22/30] x86emul: support APX+BMI2 insns Jan Beulich
2026-11-11 14:29 ` [PATCH 23/30] x86emul: support APX+CMPccXADD insns Jan Beulich
2026-11-11 14:30 ` [PATCH 24/30] x86emul: support APX+{LZ,POP}CNT insns Jan Beulich
2026-11-11 14:30 ` [PATCH 25/30] x86emul: support APX+MOVBE insn Jan Beulich
2026-11-11 14:31 ` [PATCH 26/30] x86emul: support APX+MOVDIR/ENQCMD insns Jan Beulich
2026-11-11 14:31 ` [PATCH 27/30] x86emul: support APX+MOVRS insn Jan Beulich
2026-11-11 14:31 ` [PATCH 28/30] x86emul: support APX MSR access insns Jan Beulich
2026-11-11 14:32 ` [PATCH 29/30] x86: permit guests to use APX Jan Beulich
2026-11-11 14:33 ` [PATCH 30/30] x86emul: use latched XCR0 in x86emul_get_fpu() Jan Beulich
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.