2021-03-05 15:21:18 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
|
2025-03-24 15:20:31 +01:00
|
|
|
#include <cpuid.h>
|
2025-03-24 15:20:22 +01:00
|
|
|
#include <err.h>
|
|
|
|
#include <getopt.h>
|
2021-03-05 15:21:18 +08:00
|
|
|
#include <stdbool.h>
|
2025-03-24 15:20:22 +01:00
|
|
|
#include <stdio.h>
|
2021-03-05 15:21:18 +08:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
2024-07-18 15:47:44 +02:00
|
|
|
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
|
|
|
#define min(a, b) (((a) < (b)) ? (a) : (b))
|
2025-03-24 15:20:24 +01:00
|
|
|
#define __noreturn __attribute__((__noreturn__))
|
2021-03-05 15:21:18 +08:00
|
|
|
|
|
|
|
typedef unsigned int u32;
|
|
|
|
typedef unsigned long long u64;
|
|
|
|
|
|
|
|
char *def_csv = "/usr/share/misc/cpuid.csv";
|
|
|
|
char *user_csv;
|
|
|
|
|
|
|
|
|
|
|
|
/* Cover both single-bit flag and multiple-bits fields */
|
|
|
|
struct bits_desc {
|
|
|
|
/* start and end bits */
|
|
|
|
int start, end;
|
|
|
|
/* 0 or 1 for 1-bit flag */
|
|
|
|
int value;
|
|
|
|
char simp[32];
|
|
|
|
char detail[256];
|
|
|
|
};
|
|
|
|
|
|
|
|
/* descriptor info for eax/ebx/ecx/edx */
|
|
|
|
struct reg_desc {
|
|
|
|
/* number of valid entries */
|
|
|
|
int nr;
|
|
|
|
struct bits_desc descs[32];
|
|
|
|
};
|
|
|
|
|
2023-02-06 08:18:32 -06:00
|
|
|
enum cpuid_reg {
|
2021-03-05 15:21:18 +08:00
|
|
|
R_EAX = 0,
|
|
|
|
R_EBX,
|
|
|
|
R_ECX,
|
|
|
|
R_EDX,
|
|
|
|
NR_REGS
|
|
|
|
};
|
|
|
|
|
2023-02-06 08:18:32 -06:00
|
|
|
static const char * const reg_names[] = {
|
|
|
|
"EAX", "EBX", "ECX", "EDX",
|
|
|
|
};
|
|
|
|
|
2021-03-05 15:21:18 +08:00
|
|
|
struct subleaf {
|
|
|
|
u32 index;
|
|
|
|
u32 sub;
|
2025-03-24 15:20:25 +01:00
|
|
|
u32 output[NR_REGS];
|
2021-03-05 15:21:18 +08:00
|
|
|
struct reg_desc info[NR_REGS];
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Represent one leaf (basic or extended) */
|
|
|
|
struct cpuid_func {
|
|
|
|
/*
|
|
|
|
* Array of subleafs for this func, if there is no subleafs
|
|
|
|
* then the leafs[0] is the main leaf
|
|
|
|
*/
|
|
|
|
struct subleaf *leafs;
|
|
|
|
int nr;
|
|
|
|
};
|
|
|
|
|
2025-03-24 15:20:32 +01:00
|
|
|
enum range_index {
|
|
|
|
RANGE_STD = 0, /* Standard */
|
|
|
|
RANGE_EXT = 0x80000000, /* Extended */
|
2025-03-24 15:20:36 +01:00
|
|
|
RANGE_TSM = 0x80860000, /* Transmeta */
|
|
|
|
RANGE_CTR = 0xc0000000, /* Centaur/Zhaoxin */
|
2025-03-24 15:20:32 +01:00
|
|
|
};
|
|
|
|
|
2025-03-24 15:20:33 +01:00
|
|
|
#define CPUID_INDEX_MASK 0xffff0000
|
2025-03-24 15:20:32 +01:00
|
|
|
#define CPUID_FUNCTION_MASK (~CPUID_INDEX_MASK)
|
|
|
|
|
2021-03-05 15:21:18 +08:00
|
|
|
struct cpuid_range {
|
|
|
|
/* array of main leafs */
|
|
|
|
struct cpuid_func *funcs;
|
|
|
|
/* number of valid leafs */
|
|
|
|
int nr;
|
2025-03-24 15:20:32 +01:00
|
|
|
enum range_index index;
|
2021-03-05 15:21:18 +08:00
|
|
|
};
|
|
|
|
|
2025-03-24 15:20:32 +01:00
|
|
|
static struct cpuid_range ranges[] = {
|
|
|
|
{ .index = RANGE_STD, },
|
|
|
|
{ .index = RANGE_EXT, },
|
2025-03-24 15:20:36 +01:00
|
|
|
{ .index = RANGE_TSM, },
|
|
|
|
{ .index = RANGE_CTR, },
|
2025-03-24 15:20:32 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static char *range_to_str(struct cpuid_range *range)
|
|
|
|
{
|
|
|
|
switch (range->index) {
|
|
|
|
case RANGE_STD: return "Standard";
|
|
|
|
case RANGE_EXT: return "Extended";
|
2025-03-24 15:20:36 +01:00
|
|
|
case RANGE_TSM: return "Transmeta";
|
|
|
|
case RANGE_CTR: return "Centaur";
|
2025-03-24 15:20:32 +01:00
|
|
|
default: return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
tools/x86/kcpuid: Filter valid CPUID ranges
Next commits will introduce vendor-specific CPUID ranges like Transmeta's
0x8086000 range and Centaur's 0xc0000000.
Initially explicit vendor detection was implemented, but it turned out to
be not strictly necessary. As Dave Hansen noted, even established tools
like cpuid(1) just tries all ranges indices, and see if the CPU responds
back with something sensible.
Do something similar at setup_cpuid_range(). Query the range's index,
and check the maximum range function value returned. If it's within an
expected interval of [range_index, range_index + MAX_RANGE_INDEX_OFFSET],
accept the range as valid and further query its leaves.
Set MAX_RANGE_INDEX_OFFSET to a heuristic of 0xff. That should be
sensible enough since all the ranges covered by x86-cpuid-db XML database
are:
0x00000000 0x00000023
0x40000000 0x40000000
0x80000000 0x80000026
0x80860000 0x80860007
0xc0000000 0xc0000001
At setup_cpuid_range(), if the range's returned maximum function was not
sane, mark it as invalid by setting its number of leaves, range->nr, to
zero.
Introduce the for_each_valid_cpuid_range() iterator instead of sprinkling
"range->nr != 0" checks throughout the code.
Suggested-by: Dave Hansen <dave.hansen@intel.com>
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lore.kernel.org/r/20250324142042.29010-15-darwi@linutronix.de
2025-03-24 15:20:35 +01:00
|
|
|
#define __for_each_cpuid_range(range, __condition) \
|
|
|
|
for (unsigned int i = 0; \
|
|
|
|
i < ARRAY_SIZE(ranges) && ((range) = &ranges[i]) && (__condition); \
|
|
|
|
i++)
|
|
|
|
|
|
|
|
#define for_each_valid_cpuid_range(range) __for_each_cpuid_range(range, (range)->nr != 0)
|
|
|
|
#define for_each_cpuid_range(range) __for_each_cpuid_range(range, true)
|
2025-03-24 15:20:32 +01:00
|
|
|
|
|
|
|
struct cpuid_range *index_to_cpuid_range(u32 index)
|
|
|
|
{
|
2025-03-24 15:20:34 +01:00
|
|
|
u32 func_idx = index & CPUID_FUNCTION_MASK;
|
|
|
|
u32 range_idx = index & CPUID_INDEX_MASK;
|
2025-03-24 15:20:32 +01:00
|
|
|
struct cpuid_range *range;
|
|
|
|
|
tools/x86/kcpuid: Filter valid CPUID ranges
Next commits will introduce vendor-specific CPUID ranges like Transmeta's
0x8086000 range and Centaur's 0xc0000000.
Initially explicit vendor detection was implemented, but it turned out to
be not strictly necessary. As Dave Hansen noted, even established tools
like cpuid(1) just tries all ranges indices, and see if the CPU responds
back with something sensible.
Do something similar at setup_cpuid_range(). Query the range's index,
and check the maximum range function value returned. If it's within an
expected interval of [range_index, range_index + MAX_RANGE_INDEX_OFFSET],
accept the range as valid and further query its leaves.
Set MAX_RANGE_INDEX_OFFSET to a heuristic of 0xff. That should be
sensible enough since all the ranges covered by x86-cpuid-db XML database
are:
0x00000000 0x00000023
0x40000000 0x40000000
0x80000000 0x80000026
0x80860000 0x80860007
0xc0000000 0xc0000001
At setup_cpuid_range(), if the range's returned maximum function was not
sane, mark it as invalid by setting its number of leaves, range->nr, to
zero.
Introduce the for_each_valid_cpuid_range() iterator instead of sprinkling
"range->nr != 0" checks throughout the code.
Suggested-by: Dave Hansen <dave.hansen@intel.com>
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lore.kernel.org/r/20250324142042.29010-15-darwi@linutronix.de
2025-03-24 15:20:35 +01:00
|
|
|
for_each_valid_cpuid_range(range) {
|
2025-03-24 15:20:34 +01:00
|
|
|
if (range->index == range_idx && (u32)range->nr > func_idx)
|
2025-03-24 15:20:32 +01:00
|
|
|
return range;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2021-03-05 15:21:18 +08:00
|
|
|
|
|
|
|
static bool show_details;
|
|
|
|
static bool show_raw;
|
|
|
|
static bool show_flags_only = true;
|
|
|
|
static u32 user_index = 0xFFFFFFFF;
|
|
|
|
static u32 user_sub = 0xFFFFFFFF;
|
|
|
|
static int flines;
|
|
|
|
|
2025-03-24 15:20:31 +01:00
|
|
|
/*
|
|
|
|
* Force using <cpuid.h> __cpuid_count() instead of __cpuid(). The
|
|
|
|
* latter leaves ECX uninitialized, which can break CPUID queries.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define cpuid(leaf, a, b, c, d) \
|
|
|
|
__cpuid_count(leaf, 0, a, b, c, d)
|
|
|
|
|
|
|
|
#define cpuid_count(leaf, subleaf, a, b, c, d) \
|
|
|
|
__cpuid_count(leaf, subleaf, a, b, c, d)
|
2021-03-05 15:21:18 +08:00
|
|
|
|
|
|
|
static inline bool has_subleafs(u32 f)
|
|
|
|
{
|
tools/x86/kcpuid: Recognize all leaves with subleaves
cpuid.csv will be extended in further commits with all-publicly-known
CPUID leaves and bitfields. Thus, modify has_subleafs() to identify all
known leaves with subleaves.
Remove the redundant "is_amd" check since all x86 vendors already report
the maxium supported extended leaf at leaf 0x80000000 EAX register.
The extra mentioned leaves are:
- Leaf 0x12, Intel Software Guard Extensions (SGX) enumeration
- Leaf 0x14, Intel process trace (PT) enumeration
- Leaf 0x17, Intel SoC vendor attributes enumeration
- Leaf 0x1b, Intel PCONFIG (Platform configuration) enumeration
- Leaf 0x1d, Intel AMX (Advanced Matrix Extensions) tile information
- Leaf 0x1f, Intel v2 extended topology enumeration
- Leaf 0x23, Intel ArchPerfmonExt (Architectural PMU ext) enumeration
- Leaf 0x80000020, AMD Platform QoS extended features enumeration
- Leaf 0x80000026, AMD v2 extended topology enumeration
Set the 'max_subleaf' variable for all the newly marked leaves with extra
subleaves. Ideally, this should be fetched from the CSV file instead,
but the current kcpuid code architecture has two runs: one run to
serially invoke the cpuid instructions and save all the output in-memory,
and one run to parse this in-memory output through the CSV specification.
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20240718134755.378115-7-darwi@linutronix.de
2024-07-18 15:47:46 +02:00
|
|
|
u32 with_subleaves[] = {
|
|
|
|
0x4, 0x7, 0xb, 0xd, 0xf, 0x10, 0x12,
|
|
|
|
0x14, 0x17, 0x18, 0x1b, 0x1d, 0x1f, 0x23,
|
|
|
|
0x8000001d, 0x80000020, 0x80000026,
|
|
|
|
};
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < ARRAY_SIZE(with_subleaves); i++)
|
|
|
|
if (f == with_subleaves[i])
|
2021-03-05 15:21:18 +08:00
|
|
|
return true;
|
|
|
|
|
tools/x86/kcpuid: Recognize all leaves with subleaves
cpuid.csv will be extended in further commits with all-publicly-known
CPUID leaves and bitfields. Thus, modify has_subleafs() to identify all
known leaves with subleaves.
Remove the redundant "is_amd" check since all x86 vendors already report
the maxium supported extended leaf at leaf 0x80000000 EAX register.
The extra mentioned leaves are:
- Leaf 0x12, Intel Software Guard Extensions (SGX) enumeration
- Leaf 0x14, Intel process trace (PT) enumeration
- Leaf 0x17, Intel SoC vendor attributes enumeration
- Leaf 0x1b, Intel PCONFIG (Platform configuration) enumeration
- Leaf 0x1d, Intel AMX (Advanced Matrix Extensions) tile information
- Leaf 0x1f, Intel v2 extended topology enumeration
- Leaf 0x23, Intel ArchPerfmonExt (Architectural PMU ext) enumeration
- Leaf 0x80000020, AMD Platform QoS extended features enumeration
- Leaf 0x80000026, AMD v2 extended topology enumeration
Set the 'max_subleaf' variable for all the newly marked leaves with extra
subleaves. Ideally, this should be fetched from the CSV file instead,
but the current kcpuid code architecture has two runs: one run to
serially invoke the cpuid instructions and save all the output in-memory,
and one run to parse this in-memory output through the CSV specification.
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20240718134755.378115-7-darwi@linutronix.de
2024-07-18 15:47:46 +02:00
|
|
|
return false;
|
2021-03-05 15:21:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void leaf_print_raw(struct subleaf *leaf)
|
|
|
|
{
|
|
|
|
if (has_subleafs(leaf->index)) {
|
|
|
|
if (leaf->sub == 0)
|
|
|
|
printf("0x%08x: subleafs:\n", leaf->index);
|
|
|
|
|
2025-03-24 15:20:25 +01:00
|
|
|
printf(" %2d: EAX=0x%08x, EBX=0x%08x, ECX=0x%08x, EDX=0x%08x\n", leaf->sub,
|
|
|
|
leaf->output[0], leaf->output[1], leaf->output[2], leaf->output[3]);
|
2021-03-05 15:21:18 +08:00
|
|
|
} else {
|
2025-03-24 15:20:25 +01:00
|
|
|
printf("0x%08x: EAX=0x%08x, EBX=0x%08x, ECX=0x%08x, EDX=0x%08x\n", leaf->index,
|
|
|
|
leaf->output[0], leaf->output[1], leaf->output[2], leaf->output[3]);
|
2021-03-05 15:21:18 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return true is the input eax/ebx/ecx/edx are all zero */
|
|
|
|
static bool cpuid_store(struct cpuid_range *range, u32 f, int subleaf,
|
|
|
|
u32 a, u32 b, u32 c, u32 d)
|
|
|
|
{
|
|
|
|
struct cpuid_func *func;
|
|
|
|
struct subleaf *leaf;
|
|
|
|
int s = 0;
|
|
|
|
|
|
|
|
if (a == 0 && b == 0 && c == 0 && d == 0)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Cut off vendor-prefix from CPUID function as we're using it as an
|
|
|
|
* index into ->funcs.
|
|
|
|
*/
|
2025-03-24 15:20:33 +01:00
|
|
|
func = &range->funcs[f & CPUID_FUNCTION_MASK];
|
2021-03-05 15:21:18 +08:00
|
|
|
|
|
|
|
if (!func->leafs) {
|
|
|
|
func->leafs = malloc(sizeof(struct subleaf));
|
|
|
|
if (!func->leafs)
|
2025-03-24 15:20:22 +01:00
|
|
|
err(EXIT_FAILURE, NULL);
|
2021-03-05 15:21:18 +08:00
|
|
|
|
|
|
|
func->nr = 1;
|
|
|
|
} else {
|
|
|
|
s = func->nr;
|
|
|
|
func->leafs = realloc(func->leafs, (s + 1) * sizeof(*leaf));
|
|
|
|
if (!func->leafs)
|
2025-03-24 15:20:22 +01:00
|
|
|
err(EXIT_FAILURE, NULL);
|
2021-03-05 15:21:18 +08:00
|
|
|
|
|
|
|
func->nr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
leaf = &func->leafs[s];
|
|
|
|
|
|
|
|
leaf->index = f;
|
|
|
|
leaf->sub = subleaf;
|
2025-03-24 15:20:25 +01:00
|
|
|
leaf->output[R_EAX] = a;
|
|
|
|
leaf->output[R_EBX] = b;
|
|
|
|
leaf->output[R_ECX] = c;
|
|
|
|
leaf->output[R_EDX] = d;
|
2021-03-05 15:21:18 +08:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void raw_dump_range(struct cpuid_range *range)
|
|
|
|
{
|
2025-03-24 15:20:32 +01:00
|
|
|
printf("%s Leafs :\n", range_to_str(range));
|
2021-03-05 15:21:18 +08:00
|
|
|
printf("================\n");
|
|
|
|
|
2025-03-24 15:20:30 +01:00
|
|
|
for (u32 f = 0; (int)f < range->nr; f++) {
|
2021-03-05 15:21:18 +08:00
|
|
|
struct cpuid_func *func = &range->funcs[f];
|
|
|
|
|
|
|
|
/* Skip leaf without valid items */
|
|
|
|
if (!func->nr)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* First item is the main leaf, followed by all subleafs */
|
2025-03-24 15:20:30 +01:00
|
|
|
for (int i = 0; i < func->nr; i++)
|
2021-03-05 15:21:18 +08:00
|
|
|
leaf_print_raw(&func->leafs[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-18 15:47:43 +02:00
|
|
|
#define MAX_SUBLEAF_NUM 64
|
tools/x86/kcpuid: Filter valid CPUID ranges
Next commits will introduce vendor-specific CPUID ranges like Transmeta's
0x8086000 range and Centaur's 0xc0000000.
Initially explicit vendor detection was implemented, but it turned out to
be not strictly necessary. As Dave Hansen noted, even established tools
like cpuid(1) just tries all ranges indices, and see if the CPU responds
back with something sensible.
Do something similar at setup_cpuid_range(). Query the range's index,
and check the maximum range function value returned. If it's within an
expected interval of [range_index, range_index + MAX_RANGE_INDEX_OFFSET],
accept the range as valid and further query its leaves.
Set MAX_RANGE_INDEX_OFFSET to a heuristic of 0xff. That should be
sensible enough since all the ranges covered by x86-cpuid-db XML database
are:
0x00000000 0x00000023
0x40000000 0x40000000
0x80000000 0x80000026
0x80860000 0x80860007
0xc0000000 0xc0000001
At setup_cpuid_range(), if the range's returned maximum function was not
sane, mark it as invalid by setting its number of leaves, range->nr, to
zero.
Introduce the for_each_valid_cpuid_range() iterator instead of sprinkling
"range->nr != 0" checks throughout the code.
Suggested-by: Dave Hansen <dave.hansen@intel.com>
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lore.kernel.org/r/20250324142042.29010-15-darwi@linutronix.de
2025-03-24 15:20:35 +01:00
|
|
|
#define MAX_RANGE_INDEX_OFFSET 0xff
|
2025-03-24 15:20:32 +01:00
|
|
|
void setup_cpuid_range(struct cpuid_range *range)
|
2021-03-05 15:21:18 +08:00
|
|
|
{
|
tools/x86/kcpuid: Filter valid CPUID ranges
Next commits will introduce vendor-specific CPUID ranges like Transmeta's
0x8086000 range and Centaur's 0xc0000000.
Initially explicit vendor detection was implemented, but it turned out to
be not strictly necessary. As Dave Hansen noted, even established tools
like cpuid(1) just tries all ranges indices, and see if the CPU responds
back with something sensible.
Do something similar at setup_cpuid_range(). Query the range's index,
and check the maximum range function value returned. If it's within an
expected interval of [range_index, range_index + MAX_RANGE_INDEX_OFFSET],
accept the range as valid and further query its leaves.
Set MAX_RANGE_INDEX_OFFSET to a heuristic of 0xff. That should be
sensible enough since all the ranges covered by x86-cpuid-db XML database
are:
0x00000000 0x00000023
0x40000000 0x40000000
0x80000000 0x80000026
0x80860000 0x80860007
0xc0000000 0xc0000001
At setup_cpuid_range(), if the range's returned maximum function was not
sane, mark it as invalid by setting its number of leaves, range->nr, to
zero.
Introduce the for_each_valid_cpuid_range() iterator instead of sprinkling
"range->nr != 0" checks throughout the code.
Suggested-by: Dave Hansen <dave.hansen@intel.com>
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lore.kernel.org/r/20250324142042.29010-15-darwi@linutronix.de
2025-03-24 15:20:35 +01:00
|
|
|
u32 max_func, range_funcs_sz;
|
2025-03-24 15:20:30 +01:00
|
|
|
u32 eax, ebx, ecx, edx;
|
2021-03-05 15:21:18 +08:00
|
|
|
|
2025-03-24 15:20:32 +01:00
|
|
|
cpuid(range->index, max_func, ebx, ecx, edx);
|
2021-03-05 15:21:18 +08:00
|
|
|
|
tools/x86/kcpuid: Filter valid CPUID ranges
Next commits will introduce vendor-specific CPUID ranges like Transmeta's
0x8086000 range and Centaur's 0xc0000000.
Initially explicit vendor detection was implemented, but it turned out to
be not strictly necessary. As Dave Hansen noted, even established tools
like cpuid(1) just tries all ranges indices, and see if the CPU responds
back with something sensible.
Do something similar at setup_cpuid_range(). Query the range's index,
and check the maximum range function value returned. If it's within an
expected interval of [range_index, range_index + MAX_RANGE_INDEX_OFFSET],
accept the range as valid and further query its leaves.
Set MAX_RANGE_INDEX_OFFSET to a heuristic of 0xff. That should be
sensible enough since all the ranges covered by x86-cpuid-db XML database
are:
0x00000000 0x00000023
0x40000000 0x40000000
0x80000000 0x80000026
0x80860000 0x80860007
0xc0000000 0xc0000001
At setup_cpuid_range(), if the range's returned maximum function was not
sane, mark it as invalid by setting its number of leaves, range->nr, to
zero.
Introduce the for_each_valid_cpuid_range() iterator instead of sprinkling
"range->nr != 0" checks throughout the code.
Suggested-by: Dave Hansen <dave.hansen@intel.com>
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lore.kernel.org/r/20250324142042.29010-15-darwi@linutronix.de
2025-03-24 15:20:35 +01:00
|
|
|
/*
|
|
|
|
* If the CPUID range's maximum function value is garbage, then it
|
|
|
|
* is not recognized by this CPU. Set the range's number of valid
|
|
|
|
* leaves to zero so that for_each_valid_cpu_range() can ignore it.
|
|
|
|
*/
|
|
|
|
if (max_func < range->index || max_func > (range->index + MAX_RANGE_INDEX_OFFSET)) {
|
|
|
|
range->nr = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
range->nr = (max_func & CPUID_FUNCTION_MASK) + 1;
|
|
|
|
range_funcs_sz = range->nr * sizeof(struct cpuid_func);
|
|
|
|
|
|
|
|
range->funcs = malloc(range_funcs_sz);
|
2021-03-05 15:21:18 +08:00
|
|
|
if (!range->funcs)
|
2025-03-24 15:20:22 +01:00
|
|
|
err(EXIT_FAILURE, NULL);
|
2021-03-05 15:21:18 +08:00
|
|
|
|
tools/x86/kcpuid: Filter valid CPUID ranges
Next commits will introduce vendor-specific CPUID ranges like Transmeta's
0x8086000 range and Centaur's 0xc0000000.
Initially explicit vendor detection was implemented, but it turned out to
be not strictly necessary. As Dave Hansen noted, even established tools
like cpuid(1) just tries all ranges indices, and see if the CPU responds
back with something sensible.
Do something similar at setup_cpuid_range(). Query the range's index,
and check the maximum range function value returned. If it's within an
expected interval of [range_index, range_index + MAX_RANGE_INDEX_OFFSET],
accept the range as valid and further query its leaves.
Set MAX_RANGE_INDEX_OFFSET to a heuristic of 0xff. That should be
sensible enough since all the ranges covered by x86-cpuid-db XML database
are:
0x00000000 0x00000023
0x40000000 0x40000000
0x80000000 0x80000026
0x80860000 0x80860007
0xc0000000 0xc0000001
At setup_cpuid_range(), if the range's returned maximum function was not
sane, mark it as invalid by setting its number of leaves, range->nr, to
zero.
Introduce the for_each_valid_cpuid_range() iterator instead of sprinkling
"range->nr != 0" checks throughout the code.
Suggested-by: Dave Hansen <dave.hansen@intel.com>
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lore.kernel.org/r/20250324142042.29010-15-darwi@linutronix.de
2025-03-24 15:20:35 +01:00
|
|
|
memset(range->funcs, 0, range_funcs_sz);
|
2021-03-05 15:21:18 +08:00
|
|
|
|
2025-03-24 15:20:32 +01:00
|
|
|
for (u32 f = range->index; f <= max_func; f++) {
|
2025-03-24 15:20:30 +01:00
|
|
|
u32 max_subleaf = MAX_SUBLEAF_NUM;
|
|
|
|
bool allzero;
|
2021-03-05 15:21:18 +08:00
|
|
|
|
2025-03-24 15:20:31 +01:00
|
|
|
cpuid(f, eax, ebx, ecx, edx);
|
2025-03-24 15:20:30 +01:00
|
|
|
|
|
|
|
allzero = cpuid_store(range, f, 0, eax, ebx, ecx, edx);
|
2021-03-05 15:21:18 +08:00
|
|
|
if (allzero)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!has_subleafs(f))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Some can provide the exact number of subleafs,
|
|
|
|
* others have to be tried (0xf)
|
|
|
|
*/
|
tools/x86/kcpuid: Recognize all leaves with subleaves
cpuid.csv will be extended in further commits with all-publicly-known
CPUID leaves and bitfields. Thus, modify has_subleafs() to identify all
known leaves with subleaves.
Remove the redundant "is_amd" check since all x86 vendors already report
the maxium supported extended leaf at leaf 0x80000000 EAX register.
The extra mentioned leaves are:
- Leaf 0x12, Intel Software Guard Extensions (SGX) enumeration
- Leaf 0x14, Intel process trace (PT) enumeration
- Leaf 0x17, Intel SoC vendor attributes enumeration
- Leaf 0x1b, Intel PCONFIG (Platform configuration) enumeration
- Leaf 0x1d, Intel AMX (Advanced Matrix Extensions) tile information
- Leaf 0x1f, Intel v2 extended topology enumeration
- Leaf 0x23, Intel ArchPerfmonExt (Architectural PMU ext) enumeration
- Leaf 0x80000020, AMD Platform QoS extended features enumeration
- Leaf 0x80000026, AMD v2 extended topology enumeration
Set the 'max_subleaf' variable for all the newly marked leaves with extra
subleaves. Ideally, this should be fetched from the CSV file instead,
but the current kcpuid code architecture has two runs: one run to
serially invoke the cpuid instructions and save all the output in-memory,
and one run to parse this in-memory output through the CSV specification.
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20240718134755.378115-7-darwi@linutronix.de
2024-07-18 15:47:46 +02:00
|
|
|
if (f == 0x7 || f == 0x14 || f == 0x17 || f == 0x18 || f == 0x1d)
|
2024-07-18 15:47:44 +02:00
|
|
|
max_subleaf = min((eax & 0xff) + 1, max_subleaf);
|
2021-03-05 15:21:18 +08:00
|
|
|
if (f == 0xb)
|
|
|
|
max_subleaf = 2;
|
tools/x86/kcpuid: Recognize all leaves with subleaves
cpuid.csv will be extended in further commits with all-publicly-known
CPUID leaves and bitfields. Thus, modify has_subleafs() to identify all
known leaves with subleaves.
Remove the redundant "is_amd" check since all x86 vendors already report
the maxium supported extended leaf at leaf 0x80000000 EAX register.
The extra mentioned leaves are:
- Leaf 0x12, Intel Software Guard Extensions (SGX) enumeration
- Leaf 0x14, Intel process trace (PT) enumeration
- Leaf 0x17, Intel SoC vendor attributes enumeration
- Leaf 0x1b, Intel PCONFIG (Platform configuration) enumeration
- Leaf 0x1d, Intel AMX (Advanced Matrix Extensions) tile information
- Leaf 0x1f, Intel v2 extended topology enumeration
- Leaf 0x23, Intel ArchPerfmonExt (Architectural PMU ext) enumeration
- Leaf 0x80000020, AMD Platform QoS extended features enumeration
- Leaf 0x80000026, AMD v2 extended topology enumeration
Set the 'max_subleaf' variable for all the newly marked leaves with extra
subleaves. Ideally, this should be fetched from the CSV file instead,
but the current kcpuid code architecture has two runs: one run to
serially invoke the cpuid instructions and save all the output in-memory,
and one run to parse this in-memory output through the CSV specification.
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20240718134755.378115-7-darwi@linutronix.de
2024-07-18 15:47:46 +02:00
|
|
|
if (f == 0x1f)
|
|
|
|
max_subleaf = 6;
|
|
|
|
if (f == 0x23)
|
|
|
|
max_subleaf = 4;
|
|
|
|
if (f == 0x80000020)
|
|
|
|
max_subleaf = 4;
|
|
|
|
if (f == 0x80000026)
|
|
|
|
max_subleaf = 5;
|
2021-03-05 15:21:18 +08:00
|
|
|
|
2025-03-24 15:20:30 +01:00
|
|
|
for (u32 subleaf = 1; subleaf < max_subleaf; subleaf++) {
|
2025-03-24 15:20:31 +01:00
|
|
|
cpuid_count(f, subleaf, eax, ebx, ecx, edx);
|
2025-03-24 15:20:30 +01:00
|
|
|
|
|
|
|
allzero = cpuid_store(range, f, subleaf, eax, ebx, ecx, edx);
|
2021-03-05 15:21:18 +08:00
|
|
|
if (allzero)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The basic row format for cpuid.csv is
|
|
|
|
* LEAF,SUBLEAF,register_name,bits,short name,long description
|
|
|
|
*
|
|
|
|
* like:
|
|
|
|
* 0, 0, EAX, 31:0, max_basic_leafs, Max input value for supported subleafs
|
|
|
|
* 1, 0, ECX, 0, sse3, Streaming SIMD Extensions 3(SSE3)
|
|
|
|
*/
|
2025-03-24 15:20:29 +01:00
|
|
|
static void parse_line(char *line)
|
2021-03-05 15:21:18 +08:00
|
|
|
{
|
|
|
|
char *str;
|
|
|
|
struct cpuid_range *range;
|
|
|
|
struct cpuid_func *func;
|
|
|
|
struct subleaf *leaf;
|
|
|
|
u32 index;
|
|
|
|
char buffer[512];
|
|
|
|
char *buf;
|
|
|
|
/*
|
|
|
|
* Tokens:
|
|
|
|
* 1. leaf
|
|
|
|
* 2. subleaf
|
|
|
|
* 3. register
|
|
|
|
* 4. bits
|
|
|
|
* 5. short name
|
|
|
|
* 6. long detail
|
|
|
|
*/
|
|
|
|
char *tokens[6];
|
|
|
|
struct reg_desc *reg;
|
|
|
|
struct bits_desc *bdesc;
|
|
|
|
int reg_index;
|
|
|
|
char *start, *end;
|
tools/x86/kcpuid: Parse subleaf ranges if provided
It's a common pattern in cpuid leaves to have the same bitfields format
repeated across a number of subleaves. Typically, this is used for
enumerating hierarchial structures like cache and TLB levels, CPU
topology levels, etc.
Modify kcpuid.c to handle subleaf ranges in the CSV file subleaves
column. For example, make it able to parse lines in the form:
# LEAF, SUBLEAVES, reg, bits, short_name , ...
0xb, 1:0, eax, 4:0, x2apic_id_shift , ...
0xb, 1:0, ebx, 15:0, domain_lcpus_count , ...
0xb, 1:0, ecx, 7:0, domain_nr , ...
This way, full output can be printed to the user.
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20240718134755.378115-8-darwi@linutronix.de
2024-07-18 15:47:47 +02:00
|
|
|
u32 subleaf_start, subleaf_end;
|
|
|
|
unsigned bit_start, bit_end;
|
2021-03-05 15:21:18 +08:00
|
|
|
|
|
|
|
/* Skip comments and NULL line */
|
|
|
|
if (line[0] == '#' || line[0] == '\n')
|
2025-03-24 15:20:29 +01:00
|
|
|
return;
|
2021-03-05 15:21:18 +08:00
|
|
|
|
|
|
|
strncpy(buffer, line, 511);
|
|
|
|
buffer[511] = 0;
|
|
|
|
str = buffer;
|
2025-03-24 15:20:30 +01:00
|
|
|
for (int i = 0; i < 5; i++) {
|
2021-03-05 15:21:18 +08:00
|
|
|
tokens[i] = strtok(str, ",");
|
|
|
|
if (!tokens[i])
|
|
|
|
goto err_exit;
|
|
|
|
str = NULL;
|
|
|
|
}
|
|
|
|
tokens[5] = strtok(str, "\n");
|
2021-03-15 13:08:35 +01:00
|
|
|
if (!tokens[5])
|
|
|
|
goto err_exit;
|
2021-03-05 15:21:18 +08:00
|
|
|
|
|
|
|
/* index/main-leaf */
|
|
|
|
index = strtoull(tokens[0], NULL, 0);
|
|
|
|
|
2025-03-24 15:20:34 +01:00
|
|
|
/*
|
|
|
|
* Skip line parsing if the index is not covered by known-valid
|
|
|
|
* CPUID ranges on this CPU.
|
|
|
|
*/
|
2025-03-24 15:20:32 +01:00
|
|
|
range = index_to_cpuid_range(index);
|
|
|
|
if (!range)
|
|
|
|
return;
|
2021-03-05 15:21:18 +08:00
|
|
|
|
2025-03-24 15:20:29 +01:00
|
|
|
/* Skip line parsing if the index CPUID output is all zero */
|
2025-03-24 15:20:34 +01:00
|
|
|
index &= CPUID_FUNCTION_MASK;
|
2021-03-05 15:21:18 +08:00
|
|
|
func = &range->funcs[index];
|
|
|
|
if (!func->nr)
|
2025-03-24 15:20:29 +01:00
|
|
|
return;
|
2021-03-05 15:21:18 +08:00
|
|
|
|
|
|
|
/* subleaf */
|
tools/x86/kcpuid: Parse subleaf ranges if provided
It's a common pattern in cpuid leaves to have the same bitfields format
repeated across a number of subleaves. Typically, this is used for
enumerating hierarchial structures like cache and TLB levels, CPU
topology levels, etc.
Modify kcpuid.c to handle subleaf ranges in the CSV file subleaves
column. For example, make it able to parse lines in the form:
# LEAF, SUBLEAVES, reg, bits, short_name , ...
0xb, 1:0, eax, 4:0, x2apic_id_shift , ...
0xb, 1:0, ebx, 15:0, domain_lcpus_count , ...
0xb, 1:0, ecx, 7:0, domain_nr , ...
This way, full output can be printed to the user.
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20240718134755.378115-8-darwi@linutronix.de
2024-07-18 15:47:47 +02:00
|
|
|
buf = tokens[1];
|
|
|
|
end = strtok(buf, ":");
|
|
|
|
start = strtok(NULL, ":");
|
|
|
|
subleaf_end = strtoul(end, NULL, 0);
|
|
|
|
|
|
|
|
/* A subleaf range is given? */
|
|
|
|
if (start) {
|
|
|
|
subleaf_start = strtoul(start, NULL, 0);
|
|
|
|
subleaf_end = min(subleaf_end, (u32)(func->nr - 1));
|
|
|
|
if (subleaf_start > subleaf_end)
|
2025-03-24 15:20:29 +01:00
|
|
|
return;
|
tools/x86/kcpuid: Parse subleaf ranges if provided
It's a common pattern in cpuid leaves to have the same bitfields format
repeated across a number of subleaves. Typically, this is used for
enumerating hierarchial structures like cache and TLB levels, CPU
topology levels, etc.
Modify kcpuid.c to handle subleaf ranges in the CSV file subleaves
column. For example, make it able to parse lines in the form:
# LEAF, SUBLEAVES, reg, bits, short_name , ...
0xb, 1:0, eax, 4:0, x2apic_id_shift , ...
0xb, 1:0, ebx, 15:0, domain_lcpus_count , ...
0xb, 1:0, ecx, 7:0, domain_nr , ...
This way, full output can be printed to the user.
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20240718134755.378115-8-darwi@linutronix.de
2024-07-18 15:47:47 +02:00
|
|
|
} else {
|
|
|
|
subleaf_start = subleaf_end;
|
|
|
|
if (subleaf_start > (u32)(func->nr - 1))
|
2025-03-24 15:20:29 +01:00
|
|
|
return;
|
tools/x86/kcpuid: Parse subleaf ranges if provided
It's a common pattern in cpuid leaves to have the same bitfields format
repeated across a number of subleaves. Typically, this is used for
enumerating hierarchial structures like cache and TLB levels, CPU
topology levels, etc.
Modify kcpuid.c to handle subleaf ranges in the CSV file subleaves
column. For example, make it able to parse lines in the form:
# LEAF, SUBLEAVES, reg, bits, short_name , ...
0xb, 1:0, eax, 4:0, x2apic_id_shift , ...
0xb, 1:0, ebx, 15:0, domain_lcpus_count , ...
0xb, 1:0, ecx, 7:0, domain_nr , ...
This way, full output can be printed to the user.
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20240718134755.378115-8-darwi@linutronix.de
2024-07-18 15:47:47 +02:00
|
|
|
}
|
2021-03-05 15:21:18 +08:00
|
|
|
|
tools/x86/kcpuid: Parse subleaf ranges if provided
It's a common pattern in cpuid leaves to have the same bitfields format
repeated across a number of subleaves. Typically, this is used for
enumerating hierarchial structures like cache and TLB levels, CPU
topology levels, etc.
Modify kcpuid.c to handle subleaf ranges in the CSV file subleaves
column. For example, make it able to parse lines in the form:
# LEAF, SUBLEAVES, reg, bits, short_name , ...
0xb, 1:0, eax, 4:0, x2apic_id_shift , ...
0xb, 1:0, ebx, 15:0, domain_lcpus_count , ...
0xb, 1:0, ecx, 7:0, domain_nr , ...
This way, full output can be printed to the user.
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20240718134755.378115-8-darwi@linutronix.de
2024-07-18 15:47:47 +02:00
|
|
|
/* register */
|
2021-03-05 15:21:18 +08:00
|
|
|
buf = tokens[2];
|
|
|
|
if (strcasestr(buf, "EAX"))
|
|
|
|
reg_index = R_EAX;
|
|
|
|
else if (strcasestr(buf, "EBX"))
|
|
|
|
reg_index = R_EBX;
|
|
|
|
else if (strcasestr(buf, "ECX"))
|
|
|
|
reg_index = R_ECX;
|
|
|
|
else if (strcasestr(buf, "EDX"))
|
|
|
|
reg_index = R_EDX;
|
|
|
|
else
|
|
|
|
goto err_exit;
|
|
|
|
|
|
|
|
/* bit flag or bits field */
|
|
|
|
buf = tokens[3];
|
|
|
|
end = strtok(buf, ":");
|
|
|
|
start = strtok(NULL, ":");
|
tools/x86/kcpuid: Parse subleaf ranges if provided
It's a common pattern in cpuid leaves to have the same bitfields format
repeated across a number of subleaves. Typically, this is used for
enumerating hierarchial structures like cache and TLB levels, CPU
topology levels, etc.
Modify kcpuid.c to handle subleaf ranges in the CSV file subleaves
column. For example, make it able to parse lines in the form:
# LEAF, SUBLEAVES, reg, bits, short_name , ...
0xb, 1:0, eax, 4:0, x2apic_id_shift , ...
0xb, 1:0, ebx, 15:0, domain_lcpus_count , ...
0xb, 1:0, ecx, 7:0, domain_nr , ...
This way, full output can be printed to the user.
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20240718134755.378115-8-darwi@linutronix.de
2024-07-18 15:47:47 +02:00
|
|
|
bit_end = strtoul(end, NULL, 0);
|
|
|
|
bit_start = (start) ? strtoul(start, NULL, 0) : bit_end;
|
|
|
|
|
2025-03-24 15:20:30 +01:00
|
|
|
for (u32 sub = subleaf_start; sub <= subleaf_end; sub++) {
|
tools/x86/kcpuid: Parse subleaf ranges if provided
It's a common pattern in cpuid leaves to have the same bitfields format
repeated across a number of subleaves. Typically, this is used for
enumerating hierarchial structures like cache and TLB levels, CPU
topology levels, etc.
Modify kcpuid.c to handle subleaf ranges in the CSV file subleaves
column. For example, make it able to parse lines in the form:
# LEAF, SUBLEAVES, reg, bits, short_name , ...
0xb, 1:0, eax, 4:0, x2apic_id_shift , ...
0xb, 1:0, ebx, 15:0, domain_lcpus_count , ...
0xb, 1:0, ecx, 7:0, domain_nr , ...
This way, full output can be printed to the user.
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20240718134755.378115-8-darwi@linutronix.de
2024-07-18 15:47:47 +02:00
|
|
|
leaf = &func->leafs[sub];
|
|
|
|
reg = &leaf->info[reg_index];
|
|
|
|
bdesc = ®->descs[reg->nr++];
|
|
|
|
|
|
|
|
bdesc->end = bit_end;
|
|
|
|
bdesc->start = bit_start;
|
|
|
|
strcpy(bdesc->simp, strtok(tokens[4], " \t"));
|
|
|
|
strcpy(bdesc->detail, tokens[5]);
|
|
|
|
}
|
2025-03-24 15:20:29 +01:00
|
|
|
return;
|
2021-03-05 15:21:18 +08:00
|
|
|
|
|
|
|
err_exit:
|
2025-03-24 15:20:22 +01:00
|
|
|
warnx("Wrong line format:\n"
|
|
|
|
"\tline[%d]: %s", flines, line);
|
2021-03-05 15:21:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse csv file, and construct the array of all leafs and subleafs */
|
|
|
|
static void parse_text(void)
|
|
|
|
{
|
|
|
|
FILE *file;
|
|
|
|
char *filename, *line = NULL;
|
|
|
|
size_t len = 0;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (show_raw)
|
|
|
|
return;
|
|
|
|
|
|
|
|
filename = user_csv ? user_csv : def_csv;
|
|
|
|
file = fopen(filename, "r");
|
|
|
|
if (!file) {
|
|
|
|
/* Fallback to a csv in the same dir */
|
|
|
|
file = fopen("./cpuid.csv", "r");
|
|
|
|
}
|
|
|
|
|
2025-03-24 15:20:22 +01:00
|
|
|
if (!file)
|
|
|
|
err(EXIT_FAILURE, "%s", filename);
|
2021-03-05 15:21:18 +08:00
|
|
|
|
|
|
|
while (1) {
|
|
|
|
ret = getline(&line, &len, file);
|
|
|
|
flines++;
|
|
|
|
if (ret > 0)
|
|
|
|
parse_line(line);
|
|
|
|
|
|
|
|
if (feof(file))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(file);
|
|
|
|
}
|
|
|
|
|
2025-03-24 15:20:26 +01:00
|
|
|
static void show_reg(const struct reg_desc *rdesc, u32 value)
|
2021-03-05 15:21:18 +08:00
|
|
|
{
|
2025-03-24 15:20:26 +01:00
|
|
|
const struct bits_desc *bdesc;
|
2025-03-24 15:20:30 +01:00
|
|
|
int start, end;
|
2021-03-05 15:21:18 +08:00
|
|
|
u32 mask;
|
|
|
|
|
2025-03-24 15:20:30 +01:00
|
|
|
for (int i = 0; i < rdesc->nr; i++) {
|
2021-03-05 15:21:18 +08:00
|
|
|
bdesc = &rdesc->descs[i];
|
|
|
|
|
|
|
|
start = bdesc->start;
|
|
|
|
end = bdesc->end;
|
|
|
|
if (start == end) {
|
|
|
|
/* single bit flag */
|
|
|
|
if (value & (1 << start))
|
2024-07-18 15:47:42 +02:00
|
|
|
printf("\t%-20s %s%s%s\n",
|
2021-03-05 15:21:18 +08:00
|
|
|
bdesc->simp,
|
2024-07-18 15:47:42 +02:00
|
|
|
show_flags_only ? "" : "\t\t\t",
|
2021-03-05 15:21:18 +08:00
|
|
|
show_details ? "-" : "",
|
|
|
|
show_details ? bdesc->detail : ""
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
/* bit fields */
|
|
|
|
if (show_flags_only)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
mask = ((u64)1 << (end - start + 1)) - 1;
|
|
|
|
printf("\t%-20s\t: 0x%-8x\t%s%s\n",
|
|
|
|
bdesc->simp,
|
|
|
|
(value >> start) & mask,
|
|
|
|
show_details ? "-" : "",
|
|
|
|
show_details ? bdesc->detail : ""
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-24 15:20:26 +01:00
|
|
|
static void show_reg_header(bool has_entries, u32 leaf, u32 subleaf, const char *reg_name)
|
|
|
|
{
|
|
|
|
if (show_details && has_entries)
|
|
|
|
printf("CPUID_0x%x_%s[0x%x]:\n", leaf, reg_name, subleaf);
|
|
|
|
}
|
|
|
|
|
2021-03-05 15:21:18 +08:00
|
|
|
static void show_leaf(struct subleaf *leaf)
|
|
|
|
{
|
2025-03-24 15:20:26 +01:00
|
|
|
if (show_raw)
|
2021-03-05 15:21:18 +08:00
|
|
|
leaf_print_raw(leaf);
|
2023-02-06 08:18:32 -06:00
|
|
|
|
2025-03-24 15:20:26 +01:00
|
|
|
for (int i = R_EAX; i < NR_REGS; i++) {
|
|
|
|
show_reg_header((leaf->info[i].nr > 0), leaf->index, leaf->sub, reg_names[i]);
|
|
|
|
show_reg(&leaf->info[i], leaf->output[i]);
|
|
|
|
}
|
2021-03-05 15:21:18 +08:00
|
|
|
|
2023-02-06 08:18:32 -06:00
|
|
|
if (!show_raw && show_details)
|
|
|
|
printf("\n");
|
2021-03-05 15:21:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void show_func(struct cpuid_func *func)
|
|
|
|
{
|
2025-03-24 15:20:30 +01:00
|
|
|
for (int i = 0; i < func->nr; i++)
|
2021-03-05 15:21:18 +08:00
|
|
|
show_leaf(&func->leafs[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void show_range(struct cpuid_range *range)
|
|
|
|
{
|
2025-03-24 15:20:30 +01:00
|
|
|
for (int i = 0; i < range->nr; i++)
|
2021-03-05 15:21:18 +08:00
|
|
|
show_func(&range->funcs[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline struct cpuid_func *index_to_func(u32 index)
|
|
|
|
{
|
2025-03-24 15:20:34 +01:00
|
|
|
u32 func_idx = index & CPUID_FUNCTION_MASK;
|
2021-03-05 15:21:18 +08:00
|
|
|
struct cpuid_range *range;
|
|
|
|
|
2025-03-24 15:20:32 +01:00
|
|
|
range = index_to_cpuid_range(index);
|
|
|
|
if (!range)
|
|
|
|
return NULL;
|
2021-03-05 15:21:18 +08:00
|
|
|
|
2023-04-26 11:41:07 +02:00
|
|
|
return &range->funcs[func_idx];
|
2021-03-05 15:21:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void show_info(void)
|
|
|
|
{
|
2025-03-24 15:20:32 +01:00
|
|
|
struct cpuid_range *range;
|
2021-03-05 15:21:18 +08:00
|
|
|
struct cpuid_func *func;
|
|
|
|
|
|
|
|
if (show_raw) {
|
|
|
|
/* Show all of the raw output of 'cpuid' instr */
|
tools/x86/kcpuid: Filter valid CPUID ranges
Next commits will introduce vendor-specific CPUID ranges like Transmeta's
0x8086000 range and Centaur's 0xc0000000.
Initially explicit vendor detection was implemented, but it turned out to
be not strictly necessary. As Dave Hansen noted, even established tools
like cpuid(1) just tries all ranges indices, and see if the CPU responds
back with something sensible.
Do something similar at setup_cpuid_range(). Query the range's index,
and check the maximum range function value returned. If it's within an
expected interval of [range_index, range_index + MAX_RANGE_INDEX_OFFSET],
accept the range as valid and further query its leaves.
Set MAX_RANGE_INDEX_OFFSET to a heuristic of 0xff. That should be
sensible enough since all the ranges covered by x86-cpuid-db XML database
are:
0x00000000 0x00000023
0x40000000 0x40000000
0x80000000 0x80000026
0x80860000 0x80860007
0xc0000000 0xc0000001
At setup_cpuid_range(), if the range's returned maximum function was not
sane, mark it as invalid by setting its number of leaves, range->nr, to
zero.
Introduce the for_each_valid_cpuid_range() iterator instead of sprinkling
"range->nr != 0" checks throughout the code.
Suggested-by: Dave Hansen <dave.hansen@intel.com>
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lore.kernel.org/r/20250324142042.29010-15-darwi@linutronix.de
2025-03-24 15:20:35 +01:00
|
|
|
for_each_valid_cpuid_range(range)
|
2025-03-24 15:20:32 +01:00
|
|
|
raw_dump_range(range);
|
2021-03-05 15:21:18 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (user_index != 0xFFFFFFFF) {
|
|
|
|
/* Only show specific leaf/subleaf info */
|
|
|
|
func = index_to_func(user_index);
|
|
|
|
if (!func)
|
tools/x86/kcpuid: Exit the program on invalid parameters
If the user passed an invalid CPUID index value through --leaf=index,
kcpuid prints a warning, does nothing, then exits successfully.
Transform the warning to an error, and exit the program with a proper
error code.
Similarly, if the user passed an invalid subleaf, kcpuid prints a
warning, dumps the whole leaf, then exits successfully. Print a clear
error message regarding the invalid subleaf and exit the program with the
proper error code.
Note, moving the "Invalid input index" message from index_to_func() to
show_info() localizes error message handling to the latter, where it
should be. It also allows index_to_func() to be refactored at further
commits.
Note, since after this commit and its parent kcpuid does not just "move
on" on failures, remove the NULL parameter check plus silent exit at
show_func() and show_leaf().
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lore.kernel.org/r/20250324142042.29010-3-darwi@linutronix.de
2025-03-24 15:20:23 +01:00
|
|
|
errx(EXIT_FAILURE, "Invalid input leaf (0x%x)", user_index);
|
2021-03-05 15:21:18 +08:00
|
|
|
|
|
|
|
/* Dump the raw data also */
|
|
|
|
show_raw = true;
|
|
|
|
|
|
|
|
if (user_sub != 0xFFFFFFFF) {
|
tools/x86/kcpuid: Exit the program on invalid parameters
If the user passed an invalid CPUID index value through --leaf=index,
kcpuid prints a warning, does nothing, then exits successfully.
Transform the warning to an error, and exit the program with a proper
error code.
Similarly, if the user passed an invalid subleaf, kcpuid prints a
warning, dumps the whole leaf, then exits successfully. Print a clear
error message regarding the invalid subleaf and exit the program with the
proper error code.
Note, moving the "Invalid input index" message from index_to_func() to
show_info() localizes error message handling to the latter, where it
should be. It also allows index_to_func() to be refactored at further
commits.
Note, since after this commit and its parent kcpuid does not just "move
on" on failures, remove the NULL parameter check plus silent exit at
show_func() and show_leaf().
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lore.kernel.org/r/20250324142042.29010-3-darwi@linutronix.de
2025-03-24 15:20:23 +01:00
|
|
|
if (user_sub + 1 > (u32)func->nr) {
|
|
|
|
errx(EXIT_FAILURE, "Leaf 0x%x has no valid subleaf = 0x%x",
|
|
|
|
user_index, user_sub);
|
2021-03-05 15:21:18 +08:00
|
|
|
}
|
|
|
|
|
tools/x86/kcpuid: Exit the program on invalid parameters
If the user passed an invalid CPUID index value through --leaf=index,
kcpuid prints a warning, does nothing, then exits successfully.
Transform the warning to an error, and exit the program with a proper
error code.
Similarly, if the user passed an invalid subleaf, kcpuid prints a
warning, dumps the whole leaf, then exits successfully. Print a clear
error message regarding the invalid subleaf and exit the program with the
proper error code.
Note, moving the "Invalid input index" message from index_to_func() to
show_info() localizes error message handling to the latter, where it
should be. It also allows index_to_func() to be refactored at further
commits.
Note, since after this commit and its parent kcpuid does not just "move
on" on failures, remove the NULL parameter check plus silent exit at
show_func() and show_leaf().
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lore.kernel.org/r/20250324142042.29010-3-darwi@linutronix.de
2025-03-24 15:20:23 +01:00
|
|
|
show_leaf(&func->leafs[user_sub]);
|
|
|
|
return;
|
2021-03-05 15:21:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
show_func(func);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("CPU features:\n=============\n\n");
|
tools/x86/kcpuid: Filter valid CPUID ranges
Next commits will introduce vendor-specific CPUID ranges like Transmeta's
0x8086000 range and Centaur's 0xc0000000.
Initially explicit vendor detection was implemented, but it turned out to
be not strictly necessary. As Dave Hansen noted, even established tools
like cpuid(1) just tries all ranges indices, and see if the CPU responds
back with something sensible.
Do something similar at setup_cpuid_range(). Query the range's index,
and check the maximum range function value returned. If it's within an
expected interval of [range_index, range_index + MAX_RANGE_INDEX_OFFSET],
accept the range as valid and further query its leaves.
Set MAX_RANGE_INDEX_OFFSET to a heuristic of 0xff. That should be
sensible enough since all the ranges covered by x86-cpuid-db XML database
are:
0x00000000 0x00000023
0x40000000 0x40000000
0x80000000 0x80000026
0x80860000 0x80860007
0xc0000000 0xc0000001
At setup_cpuid_range(), if the range's returned maximum function was not
sane, mark it as invalid by setting its number of leaves, range->nr, to
zero.
Introduce the for_each_valid_cpuid_range() iterator instead of sprinkling
"range->nr != 0" checks throughout the code.
Suggested-by: Dave Hansen <dave.hansen@intel.com>
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lore.kernel.org/r/20250324142042.29010-15-darwi@linutronix.de
2025-03-24 15:20:35 +01:00
|
|
|
for_each_valid_cpuid_range(range)
|
2025-03-24 15:20:32 +01:00
|
|
|
show_range(range);
|
2021-03-05 15:21:18 +08:00
|
|
|
}
|
|
|
|
|
2025-03-24 15:20:24 +01:00
|
|
|
static void __noreturn usage(int exit_code)
|
2021-03-05 15:21:18 +08:00
|
|
|
{
|
2025-03-24 15:20:24 +01:00
|
|
|
errx(exit_code, "kcpuid [-abdfhr] [-l leaf] [-s subleaf]\n"
|
|
|
|
"\t-a|--all Show both bit flags and complex bit fields info\n"
|
|
|
|
"\t-b|--bitflags Show boolean flags only\n"
|
|
|
|
"\t-d|--detail Show details of the flag/fields (default)\n"
|
|
|
|
"\t-f|--flags Specify the CPUID CSV file\n"
|
|
|
|
"\t-h|--help Show usage info\n"
|
|
|
|
"\t-l|--leaf=index Specify the leaf you want to check\n"
|
|
|
|
"\t-r|--raw Show raw CPUID data\n"
|
|
|
|
"\t-s|--subleaf=sub Specify the subleaf you want to check"
|
2021-03-05 15:21:18 +08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct option opts[] = {
|
|
|
|
{ "all", no_argument, NULL, 'a' }, /* show both bit flags and fields */
|
|
|
|
{ "bitflags", no_argument, NULL, 'b' }, /* only show bit flags, default on */
|
|
|
|
{ "detail", no_argument, NULL, 'd' }, /* show detail descriptions */
|
|
|
|
{ "file", required_argument, NULL, 'f' }, /* use user's cpuid file */
|
|
|
|
{ "help", no_argument, NULL, 'h'}, /* show usage */
|
|
|
|
{ "leaf", required_argument, NULL, 'l'}, /* only check a specific leaf */
|
|
|
|
{ "raw", no_argument, NULL, 'r'}, /* show raw CPUID leaf data */
|
|
|
|
{ "subleaf", required_argument, NULL, 's'}, /* check a specific subleaf */
|
|
|
|
{ NULL, 0, NULL, 0 }
|
|
|
|
};
|
|
|
|
|
2025-03-24 15:20:24 +01:00
|
|
|
static void parse_options(int argc, char *argv[])
|
2021-03-05 15:21:18 +08:00
|
|
|
{
|
|
|
|
int c;
|
|
|
|
|
|
|
|
while ((c = getopt_long(argc, argv, "abdf:hl:rs:",
|
|
|
|
opts, NULL)) != -1)
|
|
|
|
switch (c) {
|
|
|
|
case 'a':
|
|
|
|
show_flags_only = false;
|
|
|
|
break;
|
|
|
|
case 'b':
|
|
|
|
show_flags_only = true;
|
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
show_details = true;
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
user_csv = optarg;
|
|
|
|
break;
|
|
|
|
case 'h':
|
2025-03-24 15:20:24 +01:00
|
|
|
usage(EXIT_SUCCESS);
|
2021-03-05 15:21:18 +08:00
|
|
|
case 'l':
|
|
|
|
/* main leaf */
|
|
|
|
user_index = strtoul(optarg, NULL, 0);
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
show_raw = true;
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
/* subleaf */
|
|
|
|
user_sub = strtoul(optarg, NULL, 0);
|
|
|
|
break;
|
|
|
|
default:
|
2025-03-24 15:20:24 +01:00
|
|
|
usage(EXIT_FAILURE);
|
|
|
|
}
|
2021-03-05 15:21:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do 4 things in turn:
|
|
|
|
* 1. Parse user options
|
|
|
|
* 2. Parse and store all the CPUID leaf data supported on this platform
|
|
|
|
* 2. Parse the csv file, while skipping leafs which are not available
|
|
|
|
* on this platform
|
|
|
|
* 3. Print leafs info based on user options
|
|
|
|
*/
|
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
2025-03-24 15:20:32 +01:00
|
|
|
struct cpuid_range *range;
|
|
|
|
|
2025-03-24 15:20:24 +01:00
|
|
|
parse_options(argc, argv);
|
2021-03-05 15:21:18 +08:00
|
|
|
|
|
|
|
/* Setup the cpuid leafs of current platform */
|
2025-03-24 15:20:32 +01:00
|
|
|
for_each_cpuid_range(range)
|
|
|
|
setup_cpuid_range(range);
|
2021-03-05 15:21:18 +08:00
|
|
|
|
|
|
|
/* Read and parse the 'cpuid.csv' */
|
|
|
|
parse_text();
|
|
|
|
|
|
|
|
show_info();
|
|
|
|
return 0;
|
|
|
|
}
|