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/[email protected]/
[2]. http://sr71.net/~dave/intel/stupid-cpuid.c

Originally-by: Borislav Petkov <[email protected]>
Suggested-by: Dave Hansen <[email protected]>
Suggested-by: Borislav Petkov <[email protected]>
Signed-off-by: Feng Tang <[email protected]>
---
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

Reply via email to