* [PATCH] eal/x86: check OS support for AVX and AVX512 state
@ 2026-10-06 8:20 Dragos Tarcatu
2026-10-07 13:01 ` Bruce Richardson
0 siblings, 1 reply; 3+ messages in thread
From: Dragos Tarcatu @ 2026-10-06 8:20 UTC (permalink / raw)
To: dev; +Cc: bruce.richardson, konstantin.v.ananyev, Dragos Tarcatu, stable
rte_cpu_get_flag_enabled() reads only the CPUID feature bits.
CPUID tells what the CPU implements, not what the OS enables.
If the OS does not enable the related state components in XCR0,
the first AVX or AVX512 instruction raises #UD.
CPUID can report AVX512 while XCR0 has no AVX512 state. Old kernels
without AVX512 state support leave XCR0 at 0x7. A kernel booted with
clearcpuid=avx512f removes the AVX512 state from XCR0, but CPUID still
reports AVX512F. Similarly, gather_data_sampling=force removes the AVX
state.
On such a system, the rte_net_crc constructor selects the AVX512 code
from CPUID alone. The dynamic loader runs this constructor before
main(), so the application gets SIGILL before EAL can parse any
argument.
For the AVX-class and AVX512-class flags, also read XCR0 with XGETBV.
Report the flag only when the OS has enabled the related state.
Use XGETBV only when CPUID reports OSXSAVE, because the instruction
faults otherwise.
This change also affects rte_cpu_is_supported(). If DPDK is built for
AVX512 and the OS does not enable AVX512 state, EAL can now stop with
an error that names the missing flag. This is possible only if no
AVX512 instruction executes before rte_eal_init(), for example in a
constructor.
Fixes: 3934bb741354 ("eal/x86: identify AVX512 CPU flag")
Cc: stable@dpdk.org
Signed-off-by: Dragos Tarcatu <dragos.tarcatu@keysight.com>
---
lib/eal/include/generic/rte_cpuflags.h | 3 +
lib/eal/x86/rte_cpuflags.c | 102 ++++++++++++++++++++-----
2 files changed, 86 insertions(+), 19 deletions(-)
diff --git a/lib/eal/include/generic/rte_cpuflags.h b/lib/eal/include/generic/rte_cpuflags.h
index 26d5229b8e..af7eff6bb6 100644
--- a/lib/eal/include/generic/rte_cpuflags.h
+++ b/lib/eal/include/generic/rte_cpuflags.h
@@ -63,6 +63,9 @@ rte_cpu_get_flag_name(enum rte_cpu_flag_t feature);
/**
* Function for checking a CPU flag availability
*
+ * A flag is available only if the CPU implements the feature
+ * and the OS enables the feature for use.
+ *
* @param feature
* CPU flag to query CPU for
* @return
diff --git a/lib/eal/x86/rte_cpuflags.c b/lib/eal/x86/rte_cpuflags.c
index 80b71a3ce0..925add6375 100644
--- a/lib/eal/x86/rte_cpuflags.c
+++ b/lib/eal/x86/rte_cpuflags.c
@@ -14,6 +14,23 @@
#include "rte_cpuid.h"
#include "rte_atomic.h"
+#ifdef RTE_TOOLCHAIN_MSVC
+#include <immintrin.h>
+#endif
+
+/*
+ * XCR0 state components that the OS must enable before
+ * the related instructions can execute without faulting.
+ */
+#define XSTATE_SSE (UINT64_C(1) << 1)
+#define XSTATE_YMM (UINT64_C(1) << 2)
+#define XSTATE_OPMASK (UINT64_C(1) << 5)
+#define XSTATE_ZMM_H256 (UINT64_C(1) << 6)
+#define XSTATE_HI16_ZMM (UINT64_C(1) << 7)
+
+#define XSTATE_AVX (XSTATE_SSE | XSTATE_YMM)
+#define XSTATE_AVX512 (XSTATE_AVX | XSTATE_OPMASK | XSTATE_ZMM_H256 | XSTATE_HI16_ZMM)
+
/**
* Struct to hold a processor feature entry
*/
@@ -26,11 +43,19 @@ struct feature_entry {
char name[CPU_FLAG_NAME_MAX_LEN]; /**< String for printing */
bool has_value;
bool value;
+ uint64_t xstate; /**< XCR0 bits the OS must enable */
};
#define FEAT_DEF(name, leaf, subleaf, reg, bit) \
[RTE_CPUFLAG_##name] = {leaf, subleaf, reg, bit, #name },
+/*
+ * A VEX or EVEX encoded feature also needs OS support for its register state.
+ * Use FEAT_DEF_XSTATE for such a feature, with the XCR0 bits that it needs.
+ */
+#define FEAT_DEF_XSTATE(name, leaf, subleaf, reg, bit, xs) \
+ [RTE_CPUFLAG_##name] = {leaf, subleaf, reg, bit, #name, .xstate = xs },
+
struct feature_entry rte_cpu_feature_table[] = {
FEAT_DEF(SSE3, 0x00000001, 0, RTE_REG_ECX, 0)
FEAT_DEF(PCLMULQDQ, 0x00000001, 0, RTE_REG_ECX, 1)
@@ -43,7 +68,7 @@ struct feature_entry rte_cpu_feature_table[] = {
FEAT_DEF(TM2, 0x00000001, 0, RTE_REG_ECX, 8)
FEAT_DEF(SSSE3, 0x00000001, 0, RTE_REG_ECX, 9)
FEAT_DEF(CNXT_ID, 0x00000001, 0, RTE_REG_ECX, 10)
- FEAT_DEF(FMA, 0x00000001, 0, RTE_REG_ECX, 12)
+ FEAT_DEF_XSTATE(FMA, 0x00000001, 0, RTE_REG_ECX, 12, XSTATE_AVX)
FEAT_DEF(CMPXCHG16B, 0x00000001, 0, RTE_REG_ECX, 13)
FEAT_DEF(XTPR, 0x00000001, 0, RTE_REG_ECX, 14)
FEAT_DEF(PDCM, 0x00000001, 0, RTE_REG_ECX, 15)
@@ -58,8 +83,8 @@ struct feature_entry rte_cpu_feature_table[] = {
FEAT_DEF(AES, 0x00000001, 0, RTE_REG_ECX, 25)
FEAT_DEF(XSAVE, 0x00000001, 0, RTE_REG_ECX, 26)
FEAT_DEF(OSXSAVE, 0x00000001, 0, RTE_REG_ECX, 27)
- FEAT_DEF(AVX, 0x00000001, 0, RTE_REG_ECX, 28)
- FEAT_DEF(F16C, 0x00000001, 0, RTE_REG_ECX, 29)
+ FEAT_DEF_XSTATE(AVX, 0x00000001, 0, RTE_REG_ECX, 28, XSTATE_AVX)
+ FEAT_DEF_XSTATE(F16C, 0x00000001, 0, RTE_REG_ECX, 29, XSTATE_AVX)
FEAT_DEF(RDRAND, 0x00000001, 0, RTE_REG_ECX, 30)
FEAT_DEF(HYPERVISOR, 0x00000001, 0, RTE_REG_ECX, 31)
@@ -107,34 +132,34 @@ struct feature_entry rte_cpu_feature_table[] = {
FEAT_DEF(FSGSBASE, 0x00000007, 0, RTE_REG_EBX, 0)
FEAT_DEF(BMI1, 0x00000007, 0, RTE_REG_EBX, 3)
FEAT_DEF(HLE, 0x00000007, 0, RTE_REG_EBX, 4)
- FEAT_DEF(AVX2, 0x00000007, 0, RTE_REG_EBX, 5)
+ FEAT_DEF_XSTATE(AVX2, 0x00000007, 0, RTE_REG_EBX, 5, XSTATE_AVX)
FEAT_DEF(SMEP, 0x00000007, 0, RTE_REG_EBX, 7)
FEAT_DEF(BMI2, 0x00000007, 0, RTE_REG_EBX, 8)
FEAT_DEF(ERMS, 0x00000007, 0, RTE_REG_EBX, 9)
FEAT_DEF(INVPCID, 0x00000007, 0, RTE_REG_EBX, 10)
FEAT_DEF(RTM, 0x00000007, 0, RTE_REG_EBX, 11)
- FEAT_DEF(AVX512F, 0x00000007, 0, RTE_REG_EBX, 16)
- FEAT_DEF(AVX512DQ, 0x00000007, 0, RTE_REG_EBX, 17)
+ FEAT_DEF_XSTATE(AVX512F, 0x00000007, 0, RTE_REG_EBX, 16, XSTATE_AVX512)
+ FEAT_DEF_XSTATE(AVX512DQ, 0x00000007, 0, RTE_REG_EBX, 17, XSTATE_AVX512)
FEAT_DEF(RDSEED, 0x00000007, 0, RTE_REG_EBX, 18)
- FEAT_DEF(AVX512IFMA, 0x00000007, 0, RTE_REG_EBX, 21)
- FEAT_DEF(AVX512CD, 0x00000007, 0, RTE_REG_EBX, 28)
- FEAT_DEF(AVX512BW, 0x00000007, 0, RTE_REG_EBX, 30)
- FEAT_DEF(AVX512VL, 0x00000007, 0, RTE_REG_EBX, 31)
+ FEAT_DEF_XSTATE(AVX512IFMA, 0x00000007, 0, RTE_REG_EBX, 21, XSTATE_AVX512)
+ FEAT_DEF_XSTATE(AVX512CD, 0x00000007, 0, RTE_REG_EBX, 28, XSTATE_AVX512)
+ FEAT_DEF_XSTATE(AVX512BW, 0x00000007, 0, RTE_REG_EBX, 30, XSTATE_AVX512)
+ FEAT_DEF_XSTATE(AVX512VL, 0x00000007, 0, RTE_REG_EBX, 31, XSTATE_AVX512)
- FEAT_DEF(AVX512VBMI, 0x00000007, 0, RTE_REG_ECX, 1)
+ FEAT_DEF_XSTATE(AVX512VBMI, 0x00000007, 0, RTE_REG_ECX, 1, XSTATE_AVX512)
FEAT_DEF(WAITPKG, 0x00000007, 0, RTE_REG_ECX, 5)
- FEAT_DEF(AVX512VBMI2, 0x00000007, 0, RTE_REG_ECX, 6)
+ FEAT_DEF_XSTATE(AVX512VBMI2, 0x00000007, 0, RTE_REG_ECX, 6, XSTATE_AVX512)
FEAT_DEF(GFNI, 0x00000007, 0, RTE_REG_ECX, 8)
- FEAT_DEF(VAES, 0x00000007, 0, RTE_REG_ECX, 9)
- FEAT_DEF(VPCLMULQDQ, 0x00000007, 0, RTE_REG_ECX, 10)
- FEAT_DEF(AVX512VNNI, 0x00000007, 0, RTE_REG_ECX, 11)
- FEAT_DEF(AVX512BITALG, 0x00000007, 0, RTE_REG_ECX, 12)
- FEAT_DEF(AVX512VPOPCNTDQ, 0x00000007, 0, RTE_REG_ECX, 14)
+ FEAT_DEF_XSTATE(VAES, 0x00000007, 0, RTE_REG_ECX, 9, XSTATE_AVX)
+ FEAT_DEF_XSTATE(VPCLMULQDQ, 0x00000007, 0, RTE_REG_ECX, 10, XSTATE_AVX)
+ FEAT_DEF_XSTATE(AVX512VNNI, 0x00000007, 0, RTE_REG_ECX, 11, XSTATE_AVX512)
+ FEAT_DEF_XSTATE(AVX512BITALG, 0x00000007, 0, RTE_REG_ECX, 12, XSTATE_AVX512)
+ FEAT_DEF_XSTATE(AVX512VPOPCNTDQ, 0x00000007, 0, RTE_REG_ECX, 14, XSTATE_AVX512)
FEAT_DEF(CLDEMOTE, 0x00000007, 0, RTE_REG_ECX, 25)
FEAT_DEF(MOVDIRI, 0x00000007, 0, RTE_REG_ECX, 27)
FEAT_DEF(MOVDIR64B, 0x00000007, 0, RTE_REG_ECX, 28)
- FEAT_DEF(AVX512VP2INTERSECT, 0x00000007, 0, RTE_REG_EDX, 8)
+ FEAT_DEF_XSTATE(AVX512VP2INTERSECT, 0x00000007, 0, RTE_REG_EDX, 8, XSTATE_AVX512)
FEAT_DEF(LAHF_SAHF, 0x80000001, 0, RTE_REG_ECX, 0)
FEAT_DEF(LZCNT, 0x80000001, 0, RTE_REG_ECX, 5)
@@ -149,6 +174,38 @@ struct feature_entry rte_cpu_feature_table[] = {
FEAT_DEF(INVTSC, 0x80000007, 0, RTE_REG_EDX, 8)
};
+static uint64_t
+xcr0_read(void)
+{
+#ifdef RTE_TOOLCHAIN_MSVC
+ return _xgetbv(0);
+#else
+ uint32_t eax, edx;
+
+ /* use the raw mnemonic: _xgetbv() would need -mxsave */
+ asm volatile("xgetbv" : "=a" (eax), "=d" (edx) : "c" (0));
+ return ((uint64_t)edx << 32) | eax;
+#endif
+}
+
+/*
+ * CPUID reports what the CPU implements, not what the OS enables.
+ * Check that the OS saves the register state that the feature uses.
+ */
+static bool
+xstate_enabled(uint64_t xstate)
+{
+ /*
+ * XGETBV faults unless the OS has set CR4.OSXSAVE.
+ * The OSXSAVE entry must not have an xstate mask,
+ * else this call recurses without end.
+ */
+ if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_OSXSAVE) != 1)
+ return false;
+
+ return (xcr0_read() & xstate) == xstate;
+}
+
RTE_EXPORT_SYMBOL(rte_cpu_get_flag_enabled)
int
rte_cpu_get_flag_enabled(enum rte_cpu_flag_t feature)
@@ -156,6 +213,7 @@ rte_cpu_get_flag_enabled(enum rte_cpu_flag_t feature)
struct feature_entry *feat;
cpuid_registers_t regs;
unsigned int maxleaf;
+ bool value;
if ((unsigned int)feature >= RTE_DIM(rte_cpu_feature_table))
/* Flag does not match anything in the feature tables */
@@ -185,7 +243,13 @@ rte_cpu_get_flag_enabled(enum rte_cpu_flag_t feature)
#endif
/* check if the feature is enabled */
- feat->value = (regs[feat->reg] >> feat->bit) & 1;
+ value = (regs[feat->reg] >> feat->bit) & 1;
+
+ /* check if the OS enabled the register state for the feature */
+ if (value && feat->xstate != 0)
+ value = xstate_enabled(feat->xstate);
+
+ feat->value = value;
out:
rte_compiler_barrier();
feat->has_value = true;
--
2.34.1
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [PATCH] eal/x86: check OS support for AVX and AVX512 state
2026-10-06 8:20 [PATCH] eal/x86: check OS support for AVX and AVX512 state Dragos Tarcatu
@ 2026-10-07 13:01 ` Bruce Richardson
2026-10-07 13:03 ` Bruce Richardson
0 siblings, 1 reply; 3+ messages in thread
From: Bruce Richardson @ 2026-10-07 13:01 UTC (permalink / raw)
To: Dragos Tarcatu; +Cc: dev, konstantin.v.ananyev, stable
On Tue, Oct 06, 2026 at 08:20:56AM +0000, Dragos Tarcatu wrote:
> rte_cpu_get_flag_enabled() reads only the CPUID feature bits. CPUID
> tells what the CPU implements, not what the OS enables. If the OS does
> not enable the related state components in XCR0, the first AVX or AVX512
> instruction raises #UD.
>
> CPUID can report AVX512 while XCR0 has no AVX512 state. Old kernels
> without AVX512 state support leave XCR0 at 0x7. A kernel booted with
> clearcpuid=avx512f removes the AVX512 state from XCR0, but CPUID still
> reports AVX512F. Similarly, gather_data_sampling=force removes the AVX
> state.
>
> On such a system, the rte_net_crc constructor selects the AVX512 code
> from CPUID alone. The dynamic loader runs this constructor before main(),
> so the application gets SIGILL before EAL can parse any argument.
>
> For the AVX-class and AVX512-class flags, also read XCR0 with XGETBV.
> Report the flag only when the OS has enabled the related state. Use
> XGETBV only when CPUID reports OSXSAVE, because the instruction faults
> otherwise.
>
> This change also affects rte_cpu_is_supported(). If DPDK is built for
> AVX512 and the OS does not enable AVX512 state, EAL can now stop with an
> error that names the missing flag. This is possible only if no AVX512
> instruction executes before rte_eal_init(), for example in a constructor.
>
> Fixes: 3934bb741354 ("eal/x86: identify AVX512 CPU flag") Cc:
> stable@dpdk.org
>
> Signed-off-by: Dragos Tarcatu <dragos.tarcatu@keysight.com> ---
> lib/eal/include/generic/rte_cpuflags.h | 3 + lib/eal/x86/rte_cpuflags.c
> | 102 ++++++++++++++++++++----- 2 files changed, 86 insertions(+), 19
> deletions(-)
>
Tested this using two "x86-generic" builds from test-meson-builds.sh, one
with this patch and one without. The test app from the one without the
patch gives illegal instruction on an initializer for avx512 crc, while the
one with runs successfully.
Code looks correct to me also.
Tested-by: Bruce Richardson <bruce.richardson@intel.com>
Acked-by: Bruce Richardson <bruce.richardson@intel.com>
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH] eal/x86: check OS support for AVX and AVX512 state
2026-10-07 13:01 ` Bruce Richardson
@ 2026-10-07 13:03 ` Bruce Richardson
0 siblings, 0 replies; 3+ messages in thread
From: Bruce Richardson @ 2026-10-07 13:03 UTC (permalink / raw)
To: Dragos Tarcatu; +Cc: dev, konstantin.v.ananyev, stable
On Wed, Oct 07, 2026 at 02:01:13PM +0100, Bruce Richardson wrote:
> On Tue, Oct 06, 2026 at 08:20:56AM +0000, Dragos Tarcatu wrote:
> > rte_cpu_get_flag_enabled() reads only the CPUID feature bits. CPUID
> > tells what the CPU implements, not what the OS enables. If the OS does
> > not enable the related state components in XCR0, the first AVX or AVX512
> > instruction raises #UD.
> >
> > CPUID can report AVX512 while XCR0 has no AVX512 state. Old kernels
> > without AVX512 state support leave XCR0 at 0x7. A kernel booted with
> > clearcpuid=avx512f removes the AVX512 state from XCR0, but CPUID still
> > reports AVX512F. Similarly, gather_data_sampling=force removes the AVX
> > state.
> >
> > On such a system, the rte_net_crc constructor selects the AVX512 code
> > from CPUID alone. The dynamic loader runs this constructor before main(),
> > so the application gets SIGILL before EAL can parse any argument.
> >
> > For the AVX-class and AVX512-class flags, also read XCR0 with XGETBV.
> > Report the flag only when the OS has enabled the related state. Use
> > XGETBV only when CPUID reports OSXSAVE, because the instruction faults
> > otherwise.
> >
> > This change also affects rte_cpu_is_supported(). If DPDK is built for
> > AVX512 and the OS does not enable AVX512 state, EAL can now stop with an
> > error that names the missing flag. This is possible only if no AVX512
> > instruction executes before rte_eal_init(), for example in a constructor.
> >
> > Fixes: 3934bb741354 ("eal/x86: identify AVX512 CPU flag") Cc:
> > stable@dpdk.org
> >
> > Signed-off-by: Dragos Tarcatu <dragos.tarcatu@keysight.com> ---
> > lib/eal/include/generic/rte_cpuflags.h | 3 + lib/eal/x86/rte_cpuflags.c
> > | 102 ++++++++++++++++++++----- 2 files changed, 86 insertions(+), 19
> > deletions(-)
> >
> Tested this using two "x86-generic" builds from test-meson-builds.sh, one
> with this patch and one without. The test app from the one without the
> patch gives illegal instruction on an initializer for avx512 crc, while the
> one with runs successfully.
Just for clarification, the test was run on a system where I'd passed
clearcpuid=avx512f to the kernel (as described above in the commit log message).
>
> Code looks correct to me also.
>
> Tested-by: Bruce Richardson <bruce.richardson@intel.com>
> Acked-by: Bruce Richardson <bruce.richardson@intel.com>
>
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-10-07 13:03 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-06 8:20 [PATCH] eal/x86: check OS support for AVX and AVX512 state Dragos Tarcatu
2026-10-07 13:01 ` Bruce Richardson
2026-10-07 13:03 ` Bruce Richardson
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox