From: Dragos Tarcatu <dragos.tarcatu@keysight.com>
To: dev@dpdk.org
Cc: bruce.richardson@intel.com, konstantin.v.ananyev@yandex.ru,
Dragos Tarcatu <dragos.tarcatu@keysight.com>,
stable@dpdk.org
Subject: [PATCH] eal/x86: check OS support for AVX and AVX512 state
Date: Tue, 6 Oct 2026 08:20:56 +0000 [thread overview]
Message-ID: <20261006082056.46426-1-dragos.tarcatu@keysight.com> (raw)
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
next reply other threads:[~2026-10-06 8:21 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-06 8:20 Dragos Tarcatu [this message]
2026-10-07 13:01 ` [PATCH] eal/x86: check OS support for AVX and AVX512 state Bruce Richardson
2026-10-07 13:03 ` Bruce Richardson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261006082056.46426-1-dragos.tarcatu@keysight.com \
--to=dragos.tarcatu@keysight.com \
--cc=bruce.richardson@intel.com \
--cc=dev@dpdk.org \
--cc=konstantin.v.ananyev@yandex.ru \
--cc=stable@dpdk.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox