Re: [RFC PATCH v2] tools/x86: add kcpuid tool to show raw CPU features

From: Borislav Petkov
Date: Tue Sep 22 2020 - 16:10:35 EST


+ AMD folks.

On Tue, Sep 22, 2020 at 01:27:50PM +0800, Feng Tang wrote:
> End users frequently want to know what features their processor
> supports, independent of what the kernel supports.
>
> /proc/cpuinfo is great. It is omnipresent and since it is provided by
> the kernel it is always as up to date as the kernel. But, it could be
> ambiguous about processor features which can be disabled by the kernel
> at boot-time or compile-time.
>
> There are some user space tools showing more raw features, but they are
> not bound with kernel, and go with distros. Many end users are still
> using old distros with new kernels (upgraded by themselves), and may
> not upgrade the distros only to get a newer tool.
>
> So here arise the need for a new tool, which
> * Shows raw cpu features got from running cpuid
> * Be easier to obtain updates for compared to existing userspace
> tooling (perhaps distributed like perf)
> * Inherits "modern" kernel development process, in contrast to some
> of the existing userspace cpuid tools which are still being developed
> without git and distributed in tarballs from non-https sites.
> * Can produce output consistent with /proc/cpuinfo to make comparison
> easier.
> * Be in-kernel, could leverage kernel enabling, and even
> theoretically consume arch/x86/boot/cpustr.h so it could pick up
> new features directly from one-line X86_FEATURE_* definitions.
>
> This RFC is an early prototype, and would get community's opinion on
> whether it's the right thing to do, and what functions it should also
> support.
>
> It contains one .c core file and one text file which shows the bits
> definition of all CPUID output data, while in v1, a specific data
> structure is defined for each eax/ebx/ecx/edx output of each leaf
> and subleaf, which is less expandable [1].
>
> The supported options are:
>
> Usage: kcpuid [-adfhr] [-l leaf] [-s subleaf]
> -a|--all Show info of all CPUID leafs and subleafs(default on)
> -d|--detail Show details of the flag/fields
> -f|--flags Show boolean flags only
> -h|--help Show usage info
> -l|--leaf=index Specify the leaf
> -r|--raw Show raw cpuid data
> -s|--subleaf=sub Specify the subleaf
>
> Current RFC version only shows limited number of cpu features, and will
> be completed
>
> This is based on the prototype code from Borislav Petkov [2].
>
> output of the tool (output cut version)
> ---------------------------------------
>
> #kcpuid -r
>
> Basic Leafs:
> 0x00000000: EAX=0x0000000d, EBX=0x756e6547, ECX=0x6c65746e, EDX=0x49656e69
> 0x00000001: EAX=0x000206d7, EBX=0x0a200800, ECX=0x1fbee3ff, EDX=0xbfebfbff
> 0x00000004: subleafs:
> 0: EAX=0x3c004121, EBX=0x01c0003f, ECX=0x0000003f, EDX=0x00000000
> 1: EAX=0x3c004122, EBX=0x01c0003f, ECX=0x0000003f, EDX=0x00000000
> 2: EAX=0x3c004143, EBX=0x01c0003f, ECX=0x000001ff, EDX=0x00000000
>
> Extended Leafs :
> 0x80000000: EAX=0x80000008, EBX=0x00000000, ECX=0x00000000, EDX=0x00000000
> 0x80000001: EAX=0x00000000, EBX=0x00000000, ECX=0x00000001, EDX=0x2c100800
> 0x80000002: EAX=0x20202020, EBX=0x49202020, ECX=0x6c65746e, EDX=0x20295228
> ...
>
> #kcpuid -d
>
> max_basic_leafs : 0xd - Max input value for supported subleafs
> stepping : 0x7 - Stepping ID
> model : 0xd - Model
> family : 0x6 - Family ID
> processor : 0x0 - Processor Type
> sse3 - Streaming SIMD Extensions 3(SSE3)
> pclmulqdq - Support PCLMULQDQ instruction
> dtes64 - DS area uses 64-bit layout
> mwait - MONITOR/MWAIT supported
> ds_cpl - CPL Qualified Debug Store, which allows for branch message storage qualified by CPL
> vmx - Virtual Machine Extensions supported
> smx - Safer Mode Extension supported
> ...
>
> #kcpuid -f
>
> sse3
> pclmulqdq
> dtes64
> mwait
> ds_cpl
> vmx
> smx
> eist
> tm2
> ...
>
> #kcpuid -l 0x1
>
> stepping : 0x7
> model : 0xd
> family : 0x6
> processor : 0x0
> clflush_size : 0x8
> max_cpu_id : 0x20
> apic_id : 0xf
> sse3
> pclmulqdq
> dtes64
> mwait
> ds_cpl
> ...
>
> [1]. https://lore.kernel.org/lkml/1598514543-90152-1-git-send-email-feng.tang@xxxxxxxxx/
> [2]. http://sr71.net/~dave/intel/stupid-cpuid.c
>
> Originally-by: Borislav Petkov <bp@xxxxxxxxx>
> Suggested-by: Dave Hansen <dave.hansen@xxxxxxxxx>
> Suggested-by: Borislav Petkov <bp@xxxxxxxxx>
> Signed-off-by: Feng Tang <feng.tang@xxxxxxxxx>
> ---
> Changelog:
>
> v2:
> * use a new text file to store all the bits definition of each
> CPUID leaf/subleafs, which is easier for future expansion, as
> the core .c file will be kept untouched, suggested by Borislav/Dave
> * some code cleanup
>
> tools/arch/x86/kcpuid/Makefile | 21 ++
> tools/arch/x86/kcpuid/cpuid.txt | 59 ++++
> tools/arch/x86/kcpuid/kcpuid.c | 598 ++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 678 insertions(+)
> create mode 100644 tools/arch/x86/kcpuid/Makefile
> create mode 100644 tools/arch/x86/kcpuid/cpuid.txt
> create mode 100644 tools/arch/x86/kcpuid/kcpuid.c
>
> diff --git a/tools/arch/x86/kcpuid/Makefile b/tools/arch/x86/kcpuid/Makefile
> new file mode 100644
> index 0000000..21453e5
> --- /dev/null
> +++ b/tools/arch/x86/kcpuid/Makefile
> @@ -0,0 +1,21 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# Makefile for x86/kcpuid tool
> +
> +kcpuid : kcpuid.c
> +
> +CFLAGS = -Wextra
> +
> +BINDIR ?= /usr/sbin
> +
> +override CFLAGS += -O2 -Wall -I../../../include
> +
> +%: %.c
> + $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
> +
> +.PHONY : clean
> +clean :
> + @rm -f kcpuid
> +
> +install : kcpuid
> + install -d $(DESTDIR)$(BINDIR)
> + install -m 755 -p kcpuid $(DESTDIR)$(BINDIR)/kcpuid
> diff --git a/tools/arch/x86/kcpuid/cpuid.txt b/tools/arch/x86/kcpuid/cpuid.txt
> new file mode 100644
> index 0000000..8c2c5ec
> --- /dev/null
> +++ b/tools/arch/x86/kcpuid/cpuid.txt
> @@ -0,0 +1,58 @@
> +# Leaf 00H
> +
> +LEAF[00000000],SUBLEAF[00],EAX[ 31:0],max_basic_leafs, Max input value for supported subleafs
> +
> +
> +# Leaf 01H
> +
> +LEAF[00000001],SUBLEAF[00],EAX[ 3:0],stepping, Stepping ID
> +LEAF[00000001],SUBLEAF[00],EAX[ 7:4],model, Model
> +LEAF[00000001],SUBLEAF[00],EAX[ 11:8],family, Family ID
> +LEAF[00000001],SUBLEAF[00],EAX[13:12],processor, Processor Type
> +LEAF[00000001],SUBLEAF[00],EAX[19:16],model_ext, Extended Model ID
> +LEAF[00000001],SUBLEAF[00],EAX[27:20],family_ext, Extended Family ID
> +
> +LEAF[00000001],SUBLEAF[00],EBX[ 7:0],brand, Brand Index
> +LEAF[00000001],SUBLEAF[00],EBX[ 15:8],clflush_size, CLFLUSH line size (value * 8) in bytes
> +LEAF[00000001],SUBLEAF[00],EBX[23:16],max_cpu_id, Maxim number of addressable logic cpu ID in this package
> +LEAF[00000001],SUBLEAF[00],EBX[31:24],apic_id, Initial APIC ID
> +
> +LEAF[00000001],SUBLEAF[00],ECX[ 0],sse3, Streaming SIMD Extensions 3(SSE3)
> +LEAF[00000001],SUBLEAF[00],ECX[ 1],pclmulqdq, Support PCLMULQDQ instruction
> +LEAF[00000001],SUBLEAF[00],ECX[ 2],dtes64, DS area uses 64-bit layout
> +LEAF[00000001],SUBLEAF[00],ECX[ 3],mwait, MONITOR/MWAIT supported
> +LEAF[00000001],SUBLEAF[00],ECX[ 4],ds_cpl, CPL Qualified Debug Store, which allows for branch message storage qualified by CPL
> +LEAF[00000001],SUBLEAF[00],ECX[ 5],vmx, Virtual Machine Extensions supported
> +LEAF[00000001],SUBLEAF[00],ECX[ 6],smx, Safer Mode Extension supported
> +LEAF[00000001],SUBLEAF[00],ECX[ 7],eist, Enhanced Intel SpeedStep Technology
> +LEAF[00000001],SUBLEAF[00],ECX[ 8],tm2, Thermal Monitor 2
> +LEAF[00000001],SUBLEAF[00],ECX[ 9],ssse3, Supplemental Streaming SIMD Extensions 3 (SSSE3)
> +LEAF[00000001],SUBLEAF[00],ECX[ 10],l1_ctx_id, L1 data cache could be set to either adaptive mode or shared mode (check IA32_MISC_ENABLE bit 24 definition)
> +LEAF[00000001],SUBLEAF[00],ECX[ 11],sdbg, IA32_DEBUG_INTERFACE MSR for silicon debug supported
> +LEAF[00000001],SUBLEAF[00],ECX[ 12],fma, FMA extensions using YMM state supported
> +LEAF[00000001],SUBLEAF[00],ECX[ 13],cmpxchg16b, 'CMPXCHG16B - Compare and Exchange Bytes' supported
> +LEAF[00000001],SUBLEAF[00],ECX[ 14],xtpr_update, xTPR Update Control supported
> +LEAF[00000001],SUBLEAF[00],ECX[ 15],pdcm, Perfmon and Debug Capability supported
> +LEAF[00000001],SUBLEAF[00],ECX[ 17],pcid, Process-Context Identifiers supported
> +LEAF[00000001],SUBLEAF[00],ECX[ 18],dca, Prefetching data from a memory mapped device supported
> +LEAF[00000001],SUBLEAF[00],ECX[ 19],sss4_1, SSE4.1 feature present
> +LEAF[00000001],SUBLEAF[00],ECX[ 20],sse4_2, SSE4.2 feature present
> +LEAF[00000001],SUBLEAF[00],ECX[ 21],x2apic, x2APIC supported
> +LEAF[00000001],SUBLEAF[00],ECX[ 22],movbe, MOVBE instruction supported
> +LEAF[00000001],SUBLEAF[00],ECX[ 23],popcnt, POPCNT instruction supported
> +LEAF[00000001],SUBLEAF[00],ECX[ 24],tsc_deadline_timer, LAPIC supports not-shot operation usinga a TSC deadline value
> +LEAF[00000001],SUBLEAF[00],ECX[ 25],aesni, AESNI instruction supported
> +LEAF[00000001],SUBLEAF[00],ECX[ 26],xsave, XSAVE/XRSTOR processor extended states, XSETBV/XGETBV, XCR0 supported
> +LEAF[00000001],SUBLEAF[00],ECX[ 27],osxsave, OS has set CR4.OSXSAVE bit to enable XSETBV/XGETBV, XCR0
> +LEAF[00000001],SUBLEAF[00],ECX[ 28],avx, AVX instruction supported
> +LEAF[00000001],SUBLEAF[00],ECX[ 29],f16c, 16-bit floating-point conversion instruction supported
> +LEAF[00000001],SUBLEAF[00],ECX[ 30],rdrand, RDRAND instruction supported
> +
> +#
> +# !!! Test data for testing different options, will be removed in formal version
> +#
> +LEAF[00000004],SUBLEAF[00],ECX[ 1],aaa, AAA
> +LEAF[00000004],SUBLEAF[01],ECX[ 1],bbb, BBB
> +LEAF[00000004],SUBLEAF[02],ECX[ 1],ccc, CCC
> +LEAF[00000004],SUBLEAF[03],ECX[ 1],ddd, DDD
> +LEAF[80000000],SUBLEAF[00],EAX[ 3],eee, EEE
> diff --git a/tools/arch/x86/kcpuid/kcpuid.c b/tools/arch/x86/kcpuid/kcpuid.c
> new file mode 100644
> index 0000000..ab2ab32
> --- /dev/null
> +++ b/tools/arch/x86/kcpuid/kcpuid.c
> @@ -0,0 +1,598 @@
> +#include <stdio.h>
> +#include <stdbool.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <getopt.h>
> +
> +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
> +
> +typedef unsigned int u32;
> +typedef unsigned long long u64;
> +
> +struct bits_desc {
> + int start, end; /* start and end bits */
> + int value; /* 0 or 1 for 1-bit flag */
> + char simp[32];
> + char detail[256]; /* 256B should be big enough? */
> +};
> +
> +/* descriptor info for eax/ebx/ecx/edx */
> +struct reg_desc {
> + int nr; /* number of valid entries */
> + struct bits_desc descs[32];
> +};
> +
> +enum {
> + R_EAX = 0,
> + R_EBX,
> + R_ECX,
> + R_EDX,
> + NR_REGS
> +};
> +
> +struct subleaf {
> + u32 index;
> + u32 sub;
> + u32 eax, ebx, ecx, edx;
> + struct reg_desc info[NR_REGS]; /* eax, ebx, ecx, edx */
> +};
> +
> +/* cpuid_func represents 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;
> +};
> +
> +struct cpuid_range {
> + /* Array of leafs in this range */
> + struct cpuid_func *funcs;
> + /* Number of valid leafs */
> + int nr;
> +
> + bool is_ext;
> +};
> +
> +
> +/*
> + * 'basic' means basic functions started from 0
> + * 'ext' means extended functions started from 0x80000000
> + */
> +struct cpuid_range *leafs_basic, *leafs_ext;
> +
> +static int num_leafs;
> +static bool is_amd;
> +static bool show_details;
> +static bool show_all = true;
> +static bool show_raw;
> +static bool show_flags_only;
> +
> +static u32 user_index = 0xFFFFFFFF;
> +static u32 user_sub = 0xFFFFFFFF;
> +
> +static inline void cpuid(u32 *eax, u32 *ebx, u32 *ecx, u32 *edx)
> +{
> + /* ecx is often an input as well as an output. */
> + asm volatile("cpuid"
> + : "=a" (*eax),
> + "=b" (*ebx),
> + "=c" (*ecx),
> + "=d" (*edx)
> + : "0" (*eax), "2" (*ecx));
> +}
> +
> +static inline bool has_subleafs(u32 f)
> +{
> + if (f == 0x7 || f == 0xd)
> + return true;
> +
> + if (is_amd) {
> + if (f == 0x8000001d)
> + return true;
> + return false;
> + }
> +
> + if (f == 0x4 || f == 0xf || f == 0x10 || f == 0x14)
> + return true;
> +
> + return false;
> +}
> +
> +static void leaf_print_raw(struct subleaf *leaf)
> +{
> + if (has_subleafs(leaf->index)) {
> + if (leaf->sub == 0)
> + printf("0x%08x: subleafs:\n", leaf->index);
> +
> + printf(" %2d: EAX=0x%08x, EBX=0x%08x, ECX=0x%08x, EDX=0x%08x\n",
> + leaf->sub, leaf->eax, leaf->ebx, leaf->ecx, leaf->edx);
> + } else {
> + printf("0x%08x: EAX=0x%08x, EBX=0x%08x, ECX=0x%08x, EDX=0x%08x\n",
> + leaf->index, leaf->eax, leaf->ebx, leaf->ecx, leaf->edx);
> + }
> +}
> +
> +static void 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;
> +
> + /*
> + * Cut off vendor-prefix from CPUID function as we're using it as an
> + * index into ->funcs.
> + */
> + func = &range->funcs[f & 0xffff];
> + if (!func->leafs) {
> + func->leafs = malloc(sizeof(struct subleaf));
> + if (!func->leafs)
> + perror("malloc func leaf");
> +
> + func->nr = 1;
> + } else {
> + s = func->nr;
> + func->leafs = realloc(func->leafs, (s + 1) * sizeof(struct subleaf));
> + if (!func->leafs)
> + perror("realloc f->leafs");
> +
> + func->nr++;
> + }
> +
> + leaf = &func->leafs[s];
> +
> + leaf->index = f;
> + leaf->sub = subleaf;
> + leaf->eax = a;
> + leaf->ebx = b;
> + leaf->ecx = c;
> + leaf->edx = d;
> +}
> +
> +static void raw_dump_range(struct cpuid_range *range)
> +{
> + u32 f;
> + int i;
> +
> + printf("\n%s Leafs :\n", range->is_ext ? "Extended" : "Basic");
> +
> + for (f = 0; (int)f < range->nr; f++) {
> + struct cpuid_func *func = &range->funcs[f];
> + u32 index = f;
> +
> + if (range->is_ext)
> + index += 0x80000000;
> +
> + if (!func->nr) {
> + printf("0x%08x: ...\n", f);
> + } else {
> + for (i = 0; i < func->nr; i++)
> + leaf_print_raw(&func->leafs[i]);
> + }
> + }
> +}
> +
> +struct cpuid_range *setup_cpuid_range(u32 input_eax)
> +{
> + u32 max_func, idx_func;
> + int subleaf;
> + struct cpuid_range *range;
> + u32 eax, ebx, ecx, edx;
> + u32 f = input_eax;
> +
> + eax = input_eax;
> + ebx = ecx = edx = 0;
> +
> + cpuid(&eax, &ebx, &ecx, &edx);
> + max_func = eax;
> + idx_func = (max_func & 0xffff) + 1;
> +
> + range = malloc(sizeof(struct cpuid_range));
> + if (!range)
> + perror("malloc range");
> +
> + if (input_eax & 0x80000000)
> + range->is_ext = true;
> + else
> + range->is_ext = false;
> +
> + range->funcs = malloc(sizeof(struct cpuid_func) * idx_func);
> + if (!range->funcs)
> + perror("malloc range->funcs");
> +
> + range->nr = idx_func;
> + memset(range->funcs, 0, sizeof(struct cpuid_func) * idx_func);
> +
> + for (; f <= max_func; f++) {
> + eax = f;
> + subleaf = ecx = 0;
> +
> + cpuid(&eax, &ebx, &ecx, &edx);
> + cpuid_store(range, f, subleaf, eax, ebx, ecx, edx);
> + num_leafs++;
> +
> + if (!has_subleafs(f))
> + continue;
> +
> + for (subleaf = 1; subleaf < 64; subleaf++) {
> + eax = f;
> + ecx = subleaf;
> +
> + cpuid(&eax, &ebx, &ecx, &edx);
> +
> + /* is subleaf valid? */
> + if (eax == 0 && ebx == 0 && ecx == 0 && edx == 0)
> + continue;
> +
> + cpuid_store(range, f, subleaf, eax, ebx, ecx, edx);
> + num_leafs++;
> + }
> + }
> +
> + return range;
> +}
> +
> +/*
> + * The max nubmer returned by CPUID is honored and we
> + * created leafs for all of them, even some has no valid info
> + */
> +
> +/*
> + * Currently the input text is assumed to be correct, without bits overlapping
> + * and wrong format. More error check could be added later on demand, sample
> + * like below:
> + *
> + * LEAF[00000000],SUBLEAF[00],EAX[31:00],aaa, AAAAAAAAAAAA
> + * LEAF[80000001],SUBLEAF[00],EAX[ 2],bbb, BBBBBBBBBBBB
> + */
> +static int parse_line(char *line)
> +{
> + char *str, *buf;
> + struct cpuid_range *range;
> + struct cpuid_func *func;
> + struct subleaf *leaf;
> + u32 index, sub;
> + char buffer[512];
> + char *tokens[5];
> + struct reg_desc *reg;
> + struct bits_desc *bdesc;
> + char *start, *end;
> + int i;
> +
> + /* Skip comments parts in cpuid.txt */
> + if (line[0] == '#' || line[0] == '\n' || line[0] == ' ')
> + return 0;
> +
> + /*
> + * Tokens:
> + * 1. leaf
> + * 2. subleaf
> + * 3. bits
> + * 4. simiple text
> + * 5. detail string
> + */
> + str = line;
> + for (i = 0; i < 4; i++) {
> + tokens[i] = strtok(str, ",");
> + if (!tokens[i])
> + goto err_exit;
> + str = NULL;
> + }
> + tokens[4] = strtok(str, "\n");
> +
> + /* index */
> + buf = tokens[0];
> + if (strncmp(buf, "LEAF[", 5) || buf[13] != ']')
> + goto err_exit;
> +
> + buffer[0] = '0';
> + buffer[1] = 'x';
> + strncpy(buffer + 2, buf + 5, 8);
> + index = strtoul(buffer, NULL, 0);
> +
> + if (index & 0x80000000)
> + range = leafs_ext;
> + else
> + range = leafs_basic;
> +
> + index &= 0x7FFFFFFF;
> + if ((int)index > range->nr) {
> + printf("ERR: invalid index[0x%x] nr:%d\n",
> + index,
> + range->nr);
> + return -1;
> + }
> + func = &range->funcs[index];
> +
> + /* subleaf */
> + buf = tokens[1];
> + if (strncmp(buf, "SUBLEAF[", 8) || buf[10] != ']')
> + goto err_exit;
> +
> + strncpy(buffer + 2, buf + 8, 2);
> + buffer[4] = 0;
> + sub = strtoul(buffer, NULL, 0);
> + if (sub > (u32)func->nr) {
> + printf("ERR: invalid subleaf[%d]\n", sub);
> + return -1;
> + }
> +
> + /* token[2]: register and bits field */
> + leaf = &func->leafs[sub];
> + buf = tokens[2];
> + if (buf[0] != 'E' || buf[2] != 'X' || buf[1] < 'A' || buf[1] > 'D')
> + goto err_exit;
> +
> + reg = &leaf->info[buf[1] - 'A'];
> + bdesc = &reg->descs[reg->nr++];
> +
> + strcpy(buffer, buf + 4);
> + if (strstr(buffer, ":")) {
> + end = strtok(buffer, ":");
> + start = strtok(NULL, "]");
> +
> + bdesc->end = strtoul(end, NULL, 0);
> + bdesc->start = strtoul(start, NULL, 0);
> + } else {
> + start = strtok(buffer, "]");
> + bdesc->start = bdesc->end = strtoul(start, NULL, 0);
> + }
> +
> + strcpy(bdesc->simp, tokens[3]);
> + strcpy(bdesc->detail, tokens[4]);
> + return 0;
> +
> +err_exit:
> + printf("ERR: wrong line formt!\n\n");
> + return -1;
> +}
> +
> +/*
> + * Parse text file, and construct the array of all CPUID leafs and subleafs
> + */
> +static void parse_text(void)
> +{
> + FILE *file;
> + char *line = NULL;
> + size_t len = 0;
> + int ret;
> +
> + file = fopen("cpuid.txt", "r");
> + if (!file) {
> + printf("Error in opening 'cpuid.txt'\n");
> + return;
> + }
> +
> + while (1) {
> + ret = getline(&line, &len, file);
> + if (ret > 0)
> + parse_line(line);
> +
> + if (feof(file))
> + break;
> + }
> + fclose(file);
> +}
> +
> +/* Parse every eax/ebx/ecx/edx */
> +static void decode_bits(u32 value, struct reg_desc *rdesc)
> +{
> + struct bits_desc *bdesc;
> + int start, end, i;
> + u32 mask;
> +
> + for (i = 0; i < rdesc->nr; i++) {
> + bdesc = &rdesc->descs[i];
> + start = bdesc->start;
> + end = bdesc->end;
> +
> + if (start == end) {
> + /* single bit flag */
> + if (value & (1 << start)) {
> + printf("\t%-20s %s%s\n",
> + bdesc->simp,
> + 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 : ""
> + );
> + }
> + }
> +}
> +
> +static void show_leaf(struct subleaf *leaf)
> +{
> + if (!leaf)
> + return;
> +
> + decode_bits(leaf->eax, &leaf->info[R_EAX]);
> + decode_bits(leaf->ebx, &leaf->info[R_EBX]);
> + decode_bits(leaf->ecx, &leaf->info[R_ECX]);
> + decode_bits(leaf->edx, &leaf->info[R_EDX]);
> +}
> +
> +static void show_func(struct cpuid_func *func)
> +{
> + int i;
> +
> + if (!func)
> + return;
> +
> + for (i = 0; i < func->nr; i++)
> + show_leaf(&func->leafs[i]);
> +}
> +
> +static void show_range(struct cpuid_range *range)
> +{
> + int i;
> +
> + for (i = 0; i < range->nr; i++)
> + show_func(&range->funcs[i]);
> +}
> +
> +static inline struct cpuid_func *index_to_func(u32 index)
> +{
> + struct cpuid_range *range;
> +
> + range = (index & 0x80000000) ? leafs_ext : leafs_basic;
> + index &= 0x7FFFFFFF;
> +
> + if (((index & 0xFFFF) + 1) > (u32)range->nr) {
> + printf("ERR: invalid input index (0x%x)\n", index);
> + return NULL;
> + }
> + return &range->funcs[index];
> +}
> +
> +static void show_info(void)
> +{
> + struct cpuid_func *func;
> +
> + if (show_raw) {
> + /* Show all of the raw output data of running cpuid */
> + raw_dump_range(leafs_basic);
> + raw_dump_range(leafs_ext);
> + return;
> + }
> +
> + if (show_all) {
> + show_range(leafs_basic);
> + show_range(leafs_ext);
> + return;
> + }
> +
> + /* Show specific leaf/subleaf info */
> + func = index_to_func(user_index);
> + if (!func)
> + return;
> +
> + if (user_sub != 0xFFFFFFFF) {
> + if (user_sub + 1 <= (u32)func->nr) {
> + show_leaf(&func->leafs[user_sub]);
> + return;
> + } else {
> + printf("ERR: invalid input index (0x%x)\n", user_sub);
> + }
> + }
> +
> + show_func(func);
> +}
> +
> +static void setup_platform_cpuid(void)
> +{
> + u32 eax, ebx, ecx, edx;
> +
> + /* check vendor */
> + eax = ebx = ecx = edx = 0;
> + cpuid(&eax, &ebx, &ecx, &edx);
> + /* "htuA" */
> + if (ebx == 0x68747541)
> + is_amd = 1;
> +
> + /* setup leafs by getting the base and extended range */
> + leafs_basic = setup_cpuid_range(0x0);
> + leafs_ext = setup_cpuid_range(0x80000000);
> + printf("This platform has %d CPUID leafs and subleafs.\n\n",
> + num_leafs);
> +}
> +
> +static void usage(void)
> +{
> + printf(" Usage: kcpuid [-adfhr] [-l leaf] [-s subleaf]\n"
> + "\t-a|--all Show info of all CPUID leafs and subleafs(default on)\n"
> + "\t-d|--detail Show details of the flag/fields\n"
> + "\t-f|--flags Show boolean flags only \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\n"
> + "\n"
> + );
> +}
> +
> +struct option opts[] = {
> + { "all", no_argument, NULL, 'a' }, /* show all leafs */
> + { "detail", no_argument, NULL, 'd' }, /* show detail descriptions, default no */
> + { "flags", no_argument, NULL, 'f' }, /* only show flags */
> + { "help", no_argument, NULL, 'h'}, /* show usage */
> + { "leaf", required_argument, NULL, 'l'}, /* give the specific leaf you want to check */
> + { "raw", no_argument, NULL, 'r'}, /* show raw CPUID leaf data */
> + { "subleaf", required_argument, NULL, 's'}, /* give the specific subleaf you want to check */
> + { NULL, 0, NULL, 0 }
> +};
> +
> +static int parse_options(int argc, char *argv[])
> +{
> + int c;
> +
> + while ((c = getopt_long(argc, argv, "adfg:hl:rs:",
> + opts, NULL)) != -1)
> + switch (c) {
> + case 'a':
> + show_all = true;
> + break;
> + case 'd':
> + show_details = true;
> + break;
> + case 'f':
> + show_flags_only = true;
> + break;
> + case 'h':
> + usage();
> + exit(1);
> + break;
> + case 'l':
> + user_index = strtoul(optarg, NULL, 0);
> + show_all = false;
> + break;
> + case 'r':
> + show_raw = true;
> + break;
> + case 's':
> + user_sub = strtoul(optarg, NULL, 0);
> + break;
> + default:
> + printf("%s: Invalid option '%c'\n", argv[0], optopt);
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * Do 4 things in turn:
> + * 1. Parse user input options
> + * 2. Parse and store all the CPUID leaf data supported on this platform
> + * 2. Parse the text file according, skip leafs which is not available
> + * on this platform
> + * 3. Print leafs info based on uers options
> + */
> +int main(int argc, char *argv[])
> +{
> + if (parse_options(argc, argv))
> + return -1;
> +
> + /* setup the cpuid leafs of current platform */
> + setup_platform_cpuid();
> +
> + /* read and parse the 'cpuid.txt' */
> + parse_text();
> +
> + show_info();
> + return 0;
> +}
> --
> 2.7.4
>

--
Regards/Gruss,
Boris.

https://people.kernel.org/tglx/notes-about-netiquette