[PATCH v6 17/90] x86/cpuid: Parse CPUID(0x80000000)

From: Ahmed S. Darwish

Date: Thu Mar 26 2026 - 22:38:05 EST


Add CPUID parser logic for CPUID(0x80000000).

Verify the CPUID output since legacy Intel machines without an extended
range will repeat the highest standard CPUID leaf output instead.

This verification is similar to what is done at arch/x86/kernel/head_32.S
and arch/x86/kernel/cpu/common.c.

References: 8a50e5135af0 ("x86-32: Use symbolic constants, safer CPUID when enabling EFER.NX")
References: 67ad24e6d39c ("- pre5: - Rasmus Andersen: add proper...") # Historical git
Signed-off-by: Ahmed S. Darwish <darwi@xxxxxxxxxxxxx>
Cc: "H. Peter Anvin" <hpa@xxxxxxxxx>
Link: https://lore.kernel.org/r/d4fcfd91-cc92-4b3c-9dd2-56ecd754cecc@xxxxxxxxxx
---
arch/x86/include/asm/cpuid/types.h | 4 ++++
arch/x86/kernel/cpu/cpuid_parser.c | 21 +++++++++++++++++++++
arch/x86/kernel/cpu/cpuid_parser.h | 1 +
3 files changed, 26 insertions(+)

diff --git a/arch/x86/include/asm/cpuid/types.h b/arch/x86/include/asm/cpuid/types.h
index 3d0e611c97ba..c020fb8fed59 100644
--- a/arch/x86/include/asm/cpuid/types.h
+++ b/arch/x86/include/asm/cpuid/types.h
@@ -36,7 +36,10 @@ enum cpuid_regs_idx {
#define CPUID_RANGE_MAX(idx) (CPUID_RANGE(idx) + 0xffff)

#define CPUID_BASE_START 0x00000000
+#define CPUID_EXT_START 0x80000000
+
#define CPUID_BASE_END CPUID_RANGE_MAX(CPUID_BASE_START)
+#define CPUID_EXT_END CPUID_RANGE_MAX(CPUID_EXT_START)

/*
* Types for CPUID(0x2) parsing:
@@ -203,6 +206,7 @@ struct cpuid_leaves {
/* Leaf Subleaf number (or max number of subleaves) */
CPUID_LEAF ( 0x0, 0 );
CPUID_LEAF ( 0x1, 0 );
+ CPUID_LEAF ( 0x80000000, 0 );
};

/*
diff --git a/arch/x86/kernel/cpu/cpuid_parser.c b/arch/x86/kernel/cpu/cpuid_parser.c
index 898b0c441431..2cebe15f75d4 100644
--- a/arch/x86/kernel/cpu/cpuid_parser.c
+++ b/arch/x86/kernel/cpu/cpuid_parser.c
@@ -38,6 +38,24 @@ cpuid_read_generic(const struct cpuid_parse_entry *e, const struct cpuid_read_ou
cpuid_read_subleaf(e->leaf, e->subleaf + i, regs);
}

+static void
+cpuid_read_0x80000000(const struct cpuid_parse_entry *e, const struct cpuid_read_output *output)
+{
+ struct leaf_0x80000000_0 *el0 = (struct leaf_0x80000000_0 *)output->regs;
+
+ cpuid_read_subleaf(e->leaf, e->subleaf, el0);
+
+ /*
+ * Protect against Intel 32-bit CPUs lacking an extended CPUID range. A
+ * CPUID(0x80000000) query on such machines will repeat the output of the
+ * highest standard CPUID leaf instead.
+ */
+ if (CPUID_RANGE(el0->max_ext_leaf) != CPUID_EXT_START)
+ return;
+
+ output->info->nr_entries = 1;
+}
+
/*
* CPUID parser table:
*/
@@ -53,9 +71,11 @@ static const struct cpuid_parse_entry cpuid_parse_entries[] = {
static unsigned int cpuid_range_max_leaf(const struct cpuid_table *t, unsigned int range)
{
const struct leaf_0x0_0 *l0 = __cpuid_table_subleaf(t, 0x0, 0);
+ const struct leaf_0x80000000_0 *el0 = __cpuid_table_subleaf(t, 0x80000000, 0);

switch (range) {
case CPUID_BASE_START: return l0 ? l0->max_std_leaf : 0;
+ case CPUID_EXT_START: return el0 ? el0->max_ext_leaf : 0;
default: return 0;
}
}
@@ -113,6 +133,7 @@ cpuid_fill_table(struct cpuid_table *t, const struct cpuid_parse_entry entries[]
unsigned int end;
} ranges[] = {
{ CPUID_BASE_START, CPUID_BASE_END },
+ { CPUID_EXT_START, CPUID_EXT_END },
};

for (unsigned int i = 0; i < ARRAY_SIZE(ranges); i++)
diff --git a/arch/x86/kernel/cpu/cpuid_parser.h b/arch/x86/kernel/cpu/cpuid_parser.h
index df627306cc8c..7d41bde0c0ec 100644
--- a/arch/x86/kernel/cpu/cpuid_parser.h
+++ b/arch/x86/kernel/cpu/cpuid_parser.h
@@ -116,5 +116,6 @@ struct cpuid_parse_entry {
/* Leaf Subleaf Reader function */ \
CPUID_PARSE_ENTRY ( 0x0, 0, generic ), \
CPUID_PARSE_ENTRY ( 0x1, 0, generic ), \
+ CPUID_PARSE_ENTRY ( 0x80000000, 0, 0x80000000 ), \

#endif /* _ARCH_X86_CPUID_PARSER_H */
--
2.53.0