All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v2] tools/x86: add kcpuid tool to show raw CPU features
@ 2020-09-22  5:27 Feng Tang
  2020-09-22 19:38 ` Dave Hansen
                   ` (2 more replies)
  0 siblings, 3 replies; 17+ messages in thread
From: Feng Tang @ 2020-09-22  5:27 UTC (permalink / raw)
  To: Thomas Gleixner, Ingo Molnar, H . Peter Anvin, Borislav Petkov,
	Peter Zijlstra, Dave Hansen, x86, linux-kernel
  Cc: Feng Tang

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

Originally-by: Borislav Petkov <bp@alien8.de>
Suggested-by: Dave Hansen <dave.hansen@intel.com>
Suggested-by: Borislav Petkov <bp@alien8.de>
Signed-off-by: Feng Tang <feng.tang@intel.com>
---
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


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v2] tools/x86: add kcpuid tool to show raw CPU features
  2020-09-22  5:27 [RFC PATCH v2] tools/x86: add kcpuid tool to show raw CPU features Feng Tang
@ 2020-09-22 19:38 ` Dave Hansen
  2020-09-25  7:22   ` Feng Tang
  2020-09-25 17:27   ` Borislav Petkov
  2020-09-22 20:10 ` Borislav Petkov
  2020-09-25 17:24 ` Borislav Petkov
  2 siblings, 2 replies; 17+ messages in thread
From: Dave Hansen @ 2020-09-22 19:38 UTC (permalink / raw)
  To: Feng Tang, Thomas Gleixner, Ingo Molnar, H . Peter Anvin,
	Borislav Petkov, Peter Zijlstra, x86, linux-kernel

On 9/21/20 10:27 PM, Feng Tang wrote:
> +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;
> +	}

This mostly looks fine to me.  A few things about cpuid.txt, though...
It needs to be read out of *some* location which is not the current
directory.  Maybe:

	/usr/share/hwdata/cpu.ids

or something.  It also needs a "-f" argument to override this default
location.  I don't know if there's a better per-kernel place to put this
file, though.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v2] tools/x86: add kcpuid tool to show raw CPU features
  2020-09-22  5:27 [RFC PATCH v2] tools/x86: add kcpuid tool to show raw CPU features Feng Tang
  2020-09-22 19:38 ` Dave Hansen
@ 2020-09-22 20:10 ` Borislav Petkov
  2020-09-22 22:15   ` Arvind Sankar
  2020-09-25 17:24 ` Borislav Petkov
  2 siblings, 1 reply; 17+ messages in thread
From: Borislav Petkov @ 2020-09-22 20:10 UTC (permalink / raw)
  To: Feng Tang, Tom Lendacky, Yazen Ghannam, Wei Huang
  Cc: Thomas Gleixner, Ingo Molnar, H . Peter Anvin, Peter Zijlstra,
	Dave Hansen, x86, linux-kernel

+ 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@intel.com/
> [2]. http://sr71.net/~dave/intel/stupid-cpuid.c
> 
> Originally-by: Borislav Petkov <bp@alien8.de>
> Suggested-by: Dave Hansen <dave.hansen@intel.com>
> Suggested-by: Borislav Petkov <bp@alien8.de>
> Signed-off-by: Feng Tang <feng.tang@intel.com>
> ---
> 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

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v2] tools/x86: add kcpuid tool to show raw CPU features
  2020-09-22 20:10 ` Borislav Petkov
@ 2020-09-22 22:15   ` Arvind Sankar
  2020-09-23  2:45     ` Feng Tang
  0 siblings, 1 reply; 17+ messages in thread
From: Arvind Sankar @ 2020-09-22 22:15 UTC (permalink / raw)
  To: Borislav Petkov
  Cc: Feng Tang, Tom Lendacky, Yazen Ghannam, Wei Huang,
	Thomas Gleixner, Ingo Molnar, H . Peter Anvin, Peter Zijlstra,
	Dave Hansen, x86, linux-kernel

On Tue, Sep 22, 2020 at 10:10:24PM +0200, Borislav Petkov wrote:
> + 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.

Rather than a tool, would additional file(s) in, say,
/sys/devices/system/cpu/cpu<n> be nicer? They could show the raw CPUID
features, one file per leaf or sub-leaf, maybe even along with whether
they were disabled at boot-time.

> >   * 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.

That's arch/x86/include/asm/cpufeatures.h right -- cpustr.h is generated
from that. The table there already has comments which could be extracted
as the one-line description.


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v2] tools/x86: add kcpuid tool to show raw CPU features
  2020-09-22 22:15   ` Arvind Sankar
@ 2020-09-23  2:45     ` Feng Tang
  2020-09-25 17:16       ` Borislav Petkov
  0 siblings, 1 reply; 17+ messages in thread
From: Feng Tang @ 2020-09-23  2:45 UTC (permalink / raw)
  To: Arvind Sankar
  Cc: Borislav Petkov, Tom Lendacky, Yazen Ghannam, Wei Huang,
	Thomas Gleixner, Ingo Molnar, H . Peter Anvin, Peter Zijlstra,
	Dave Hansen, x86, linux-kernel

Hi Arvind,

On Tue, Sep 22, 2020 at 06:15:23PM -0400, Arvind Sankar wrote:
> On Tue, Sep 22, 2020 at 10:10:24PM +0200, Borislav Petkov wrote:
> > + 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.
> 
> Rather than a tool, would additional file(s) in, say,
> /sys/devices/system/cpu/cpu<n> be nicer? They could show the raw CPUID
> features, one file per leaf or sub-leaf, maybe even along with whether
> they were disabled at boot-time.

My thought is we already have in-kernel powerful /proc/cpuinfo, while 
a user space tool could be more flexible for text parsing/layout, and
show different info on user's demand/options.

> > >   * 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.
> 
> That's arch/x86/include/asm/cpufeatures.h right -- cpustr.h is generated
> from that. The table there already has comments which could be extracted
> as the one-line description.

Thanks for the hint! I found the comments in cpufeatures.h is much better
than what I extraced from SDM :), which I should use instead.

One other thing as Boris has mentioned, cpu feature is mixture of raw
silicon features and kernel software ones. Also, cpufeatures.h only
contains shows ont-bit boolean flag, while cpuid has multiple-bits field
containing numbers.

Thanks,
Feng



^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v2] tools/x86: add kcpuid tool to show raw CPU features
  2020-09-22 19:38 ` Dave Hansen
@ 2020-09-25  7:22   ` Feng Tang
  2020-09-25 17:27   ` Borislav Petkov
  1 sibling, 0 replies; 17+ messages in thread
From: Feng Tang @ 2020-09-25  7:22 UTC (permalink / raw)
  To: Dave Hansen
  Cc: Thomas Gleixner, Ingo Molnar, H . Peter Anvin, Borislav Petkov,
	Peter Zijlstra, x86, linux-kernel

On Tue, Sep 22, 2020 at 12:38:30PM -0700, Dave Hansen wrote:
> On 9/21/20 10:27 PM, Feng Tang wrote:
> > +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;
> > +	}
> 
> This mostly looks fine to me.  A few things about cpuid.txt, though...
> It needs to be read out of *some* location which is not the current
> directory.  Maybe:
> 
> 	/usr/share/hwdata/cpu.ids

Good point! User won't run it inside a kernel source tree folder. And yes,
there are already similar pci.ids and usb.ids in the same folder, we coud 
do this in the 'install' part of Makefile

> or something.  It also needs a "-f" argument to override this default
> location. 

Ok, will add.

> I don't know if there's a better per-kernel place to put this
> file, though.

Device tree's dtb file may be similar, but it's also outside of bzImage.

Thanks,
Feng

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v2] tools/x86: add kcpuid tool to show raw CPU features
  2020-09-23  2:45     ` Feng Tang
@ 2020-09-25 17:16       ` Borislav Petkov
  2020-09-25 19:48         ` Arvind Sankar
  0 siblings, 1 reply; 17+ messages in thread
From: Borislav Petkov @ 2020-09-25 17:16 UTC (permalink / raw)
  To: Feng Tang
  Cc: Arvind Sankar, Tom Lendacky, Yazen Ghannam, Wei Huang,
	Thomas Gleixner, Ingo Molnar, H . Peter Anvin, Peter Zijlstra,
	Dave Hansen, x86, linux-kernel

On Wed, Sep 23, 2020 at 10:45:29AM +0800, Feng Tang wrote:
> > Rather than a tool, would additional file(s) in, say,
> > /sys/devices/system/cpu/cpu<n> be nicer? They could show the raw CPUID
> > features, one file per leaf or sub-leaf, maybe even along with whether
> > they were disabled at boot-time.
> 
> My thought is we already have in-kernel powerful /proc/cpuinfo, while 
> a user space tool could be more flexible for text parsing/layout, and
> show different info on user's demand/options.

The important thing here is that a separate tool would be decoupled from
the kernel and thus will be independent from it, i.e., you can get the
tool and run it without having to install a new kernel.

And with the CPUID leafs specified in a text file, you won't even need
to update the tool even - just cpuid.txt. It can't get any better than
that.

-- 
Regards/Gruss,
    Boris.

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

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v2] tools/x86: add kcpuid tool to show raw CPU features
  2020-09-22  5:27 [RFC PATCH v2] tools/x86: add kcpuid tool to show raw CPU features Feng Tang
  2020-09-22 19:38 ` Dave Hansen
  2020-09-22 20:10 ` Borislav Petkov
@ 2020-09-25 17:24 ` Borislav Petkov
  2020-09-25 17:26   ` Dave Hansen
  2020-09-28  7:07   ` Feng Tang
  2 siblings, 2 replies; 17+ messages in thread
From: Borislav Petkov @ 2020-09-25 17:24 UTC (permalink / raw)
  To: Feng Tang
  Cc: Thomas Gleixner, Ingo Molnar, H . Peter Anvin, Peter Zijlstra,
	Dave Hansen, x86, linux-kernel

On Tue, Sep 22, 2020 at 01:27:50PM +0800, Feng Tang wrote:
> +++ 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

Yeah, this looks good but how about we simplify and ease the parsing
even more? I.e., make it a real csv:

# Table row names:
# LEAF,SUBLEAF,register_name,bits,short name, long name

0x1,0,EBX,7:0,brand,Brand Index
0x1,0,EBX,15:8,clflush_size,CLFLUSH line size (value * 8) in bytes
0x1,0,EBX,23:16,max_cpu_id,Maxim number of addressable logic cpu ID in this package
...

Yeah, this way it might not be as readable but the format is pretty simple and
everybody knows .csv so....

We can even do vertical alignment for better readability:

#     LEAF,SUBLEAF,register,  bits,short name, long name
         1,	 0,	EBX,   7:0,brand,Brand Index
         1,	 0,	EBX,  15:8,clflush_size,CLFLUSH line size (value * 8) in bytes
     	 1,	 0,	EBX, 23:16,max_cpu_id,Maxim number of addressable logic cpu ID in this package
0x80000000,	12,	EAX,     3,eee, EEE

and can keep it in tabellary form this way. The parsing code should
asimply split on ',' and ignore whitespace.

Thoughts?

-- 
Regards/Gruss,
    Boris.

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

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v2] tools/x86: add kcpuid tool to show raw CPU features
  2020-09-25 17:24 ` Borislav Petkov
@ 2020-09-25 17:26   ` Dave Hansen
  2020-09-28  7:07   ` Feng Tang
  1 sibling, 0 replies; 17+ messages in thread
From: Dave Hansen @ 2020-09-25 17:26 UTC (permalink / raw)
  To: Borislav Petkov, Feng Tang
  Cc: Thomas Gleixner, Ingo Molnar, H . Peter Anvin, Peter Zijlstra,
	x86, linux-kernel

On 9/25/20 10:24 AM, Borislav Petkov wrote:
> #     LEAF,SUBLEAF,register,  bits,short name, long name
>          1,	 0,	EBX,   7:0,brand,Brand Index
>          1,	 0,	EBX,  15:8,clflush_size,CLFLUSH line size (value * 8) in bytes
>      	 1,	 0,	EBX, 23:16,max_cpu_id,Maxim number of addressable logic cpu ID in this package
> 0x80000000,	12,	EAX,     3,eee, EEE
> 
> and can keep it in tabellary form this way. The parsing code should
> asimply split on ',' and ignore whitespace.
> 
> Thoughts?

Looks fine to me.  It's plenty readable that way too.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v2] tools/x86: add kcpuid tool to show raw CPU features
  2020-09-22 19:38 ` Dave Hansen
  2020-09-25  7:22   ` Feng Tang
@ 2020-09-25 17:27   ` Borislav Petkov
  1 sibling, 0 replies; 17+ messages in thread
From: Borislav Petkov @ 2020-09-25 17:27 UTC (permalink / raw)
  To: Dave Hansen
  Cc: Feng Tang, Thomas Gleixner, Ingo Molnar, H . Peter Anvin,
	Peter Zijlstra, x86, linux-kernel

On Tue, Sep 22, 2020 at 12:38:30PM -0700, Dave Hansen wrote:
> or something.  It also needs a "-f" argument to override this default
> location.

Yah, the -f thing is almost mandatory. I can imagine simply downloading
the latest cpuid.leafs or so and then running kcpuid on it. Can't get
any better than this I'd say.

Which reminds me: we probably should aim at not changing that text
file's format too often and design it to contain the required info
from the get-go so that we don't have uglies with old kcpuid not
understanding the new format or the other way around. That would be a
pain.

-- 
Regards/Gruss,
    Boris.

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

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v2] tools/x86: add kcpuid tool to show raw CPU features
  2020-09-25 17:16       ` Borislav Petkov
@ 2020-09-25 19:48         ` Arvind Sankar
  2020-09-25 20:17           ` Borislav Petkov
  0 siblings, 1 reply; 17+ messages in thread
From: Arvind Sankar @ 2020-09-25 19:48 UTC (permalink / raw)
  To: Borislav Petkov
  Cc: Feng Tang, Arvind Sankar, Tom Lendacky, Yazen Ghannam, Wei Huang,
	Thomas Gleixner, Ingo Molnar, H . Peter Anvin, Peter Zijlstra,
	Dave Hansen, x86, linux-kernel

On Fri, Sep 25, 2020 at 07:16:31PM +0200, Borislav Petkov wrote:
> On Wed, Sep 23, 2020 at 10:45:29AM +0800, Feng Tang wrote:
> > > Rather than a tool, would additional file(s) in, say,
> > > /sys/devices/system/cpu/cpu<n> be nicer? They could show the raw CPUID
> > > features, one file per leaf or sub-leaf, maybe even along with whether
> > > they were disabled at boot-time.
> > 
> > My thought is we already have in-kernel powerful /proc/cpuinfo, while 
> > a user space tool could be more flexible for text parsing/layout, and
> > show different info on user's demand/options.
> 
> The important thing here is that a separate tool would be decoupled from
> the kernel and thus will be independent from it, i.e., you can get the
> tool and run it without having to install a new kernel.
> 
> And with the CPUID leafs specified in a text file, you won't even need
> to update the tool even - just cpuid.txt. It can't get any better than
> that.
> 
> -- 
> Regards/Gruss,
>     Boris.
> 
> https://people.kernel.org/tglx/notes-about-netiquette

Ok. Another suggestion: while designing the format for the cpuid.txt
file, would it be possible to include enough information for
cpufeatures.h to be auto-generated from it?

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v2] tools/x86: add kcpuid tool to show raw CPU features
  2020-09-25 19:48         ` Arvind Sankar
@ 2020-09-25 20:17           ` Borislav Petkov
  2020-09-25 20:40             ` Arvind Sankar
  0 siblings, 1 reply; 17+ messages in thread
From: Borislav Petkov @ 2020-09-25 20:17 UTC (permalink / raw)
  To: Arvind Sankar
  Cc: Feng Tang, Tom Lendacky, Yazen Ghannam, Wei Huang,
	Thomas Gleixner, Ingo Molnar, H . Peter Anvin, Peter Zijlstra,
	Dave Hansen, x86, linux-kernel

On Fri, Sep 25, 2020 at 03:48:04PM -0400, Arvind Sankar wrote:
> Ok. Another suggestion: while designing the format for the cpuid.txt
> file, would it be possible to include enough information for
> cpufeatures.h to be auto-generated from it?

I think you're missing the previous discussion on this topic. In short:
/proc/cpuinfo is not CPUID but what the kernel supports. The kcpuid tool
is supposed to dump CPUID leafs and it should be updatable the easiest
way possible.

Here's some more documentation which should make this more clear:

https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git/commit/?id=ea4e3bef4c94d5b5d8e790f37b48b7641172bcf5

-- 
Regards/Gruss,
    Boris.

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

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v2] tools/x86: add kcpuid tool to show raw CPU features
  2020-09-25 20:17           ` Borislav Petkov
@ 2020-09-25 20:40             ` Arvind Sankar
  2020-09-25 20:57               ` Borislav Petkov
  0 siblings, 1 reply; 17+ messages in thread
From: Arvind Sankar @ 2020-09-25 20:40 UTC (permalink / raw)
  To: Borislav Petkov
  Cc: Arvind Sankar, Feng Tang, Tom Lendacky, Yazen Ghannam, Wei Huang,
	Thomas Gleixner, Ingo Molnar, H . Peter Anvin, Peter Zijlstra,
	Dave Hansen, x86, linux-kernel

On Fri, Sep 25, 2020 at 10:17:07PM +0200, Borislav Petkov wrote:
> On Fri, Sep 25, 2020 at 03:48:04PM -0400, Arvind Sankar wrote:
> > Ok. Another suggestion: while designing the format for the cpuid.txt
> > file, would it be possible to include enough information for
> > cpufeatures.h to be auto-generated from it?
> 
> I think you're missing the previous discussion on this topic. In short:
> /proc/cpuinfo is not CPUID but what the kernel supports. The kcpuid tool
> is supposed to dump CPUID leafs and it should be updatable the easiest
> way possible.
> 
> Here's some more documentation which should make this more clear:
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git/commit/?id=ea4e3bef4c94d5b5d8e790f37b48b7641172bcf5
> 
> -- 
> Regards/Gruss,
>     Boris.
> 
> https://people.kernel.org/tglx/notes-about-netiquette

They're not the same, but aren't there going to be quite a few common
flags between the definitions in cpufeatures.h and the definitions in
cpuid.txt? If they're both living in the kernel repo, it would be nice
for them to not duplicate what's common between them, no?

This shouldn't affect how easy it is to update, I think. The kernel
build will use whatever version is in the source tree, the tool will use
whatever version is installed under /usr/share, so the latter can be
updated without needing a new kernel.

Thanks.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v2] tools/x86: add kcpuid tool to show raw CPU features
  2020-09-25 20:40             ` Arvind Sankar
@ 2020-09-25 20:57               ` Borislav Petkov
  0 siblings, 0 replies; 17+ messages in thread
From: Borislav Petkov @ 2020-09-25 20:57 UTC (permalink / raw)
  To: Arvind Sankar
  Cc: Feng Tang, Tom Lendacky, Yazen Ghannam, Wei Huang,
	Thomas Gleixner, Ingo Molnar, H . Peter Anvin, Peter Zijlstra,
	Dave Hansen, x86, linux-kernel

On Fri, Sep 25, 2020 at 04:40:47PM -0400, Arvind Sankar wrote:
> They're not the same, but aren't there going to be quite a few common
> flags between the definitions in cpufeatures.h and the definitions in
> cpuid.txt? If they're both living in the kernel repo, it would be nice
> for them to not duplicate what's common between them, no?

You will generate cpuid.txt exactly once and shortly after cpufeatures.h
will already be ancient in comparison to it. So there would be no point
to share.

Also, have a look at which leafs are in cpufeatures.h, which of those
leafs are synthetic and how many leafs are in an actual CPUID hw
implementation.

Then, some of the bits in cpufeatures.h are not present while the leafs
in CPUID have them for the above reason.

And so on...

> This shouldn't affect how easy it is to update, I think. The kernel
> build will use whatever version is in the source tree, the tool will use
> whatever version is installed under /usr/share, so the latter can be
> updated without needing a new kernel.

And I believe that keeping those apart because there are differences,
would cause more confusion vs having the two things completely separate.

So I actually think that sharing between the two is not even worth the
effort.

-- 
Regards/Gruss,
    Boris.

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

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v2] tools/x86: add kcpuid tool to show raw CPU features
  2020-09-25 17:24 ` Borislav Petkov
  2020-09-25 17:26   ` Dave Hansen
@ 2020-09-28  7:07   ` Feng Tang
  2020-09-28  8:34     ` Borislav Petkov
  1 sibling, 1 reply; 17+ messages in thread
From: Feng Tang @ 2020-09-28  7:07 UTC (permalink / raw)
  To: Borislav Petkov
  Cc: Thomas Gleixner, Ingo Molnar, H . Peter Anvin, Peter Zijlstra,
	Dave Hansen, x86, linux-kernel

On Fri, Sep 25, 2020 at 07:24:37PM +0200, Borislav Petkov wrote:
> On Tue, Sep 22, 2020 at 01:27:50PM +0800, Feng Tang wrote:
> > +++ 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
> 
> Yeah, this looks good but how about we simplify and ease the parsing
> even more? I.e., make it a real csv:
> 
> # Table row names:
> # LEAF,SUBLEAF,register_name,bits,short name, long name
> 
> 0x1,0,EBX,7:0,brand,Brand Index
> 0x1,0,EBX,15:8,clflush_size,CLFLUSH line size (value * 8) in bytes
> 0x1,0,EBX,23:16,max_cpu_id,Maxim number of addressable logic cpu ID in this package
> ...
> 
> Yeah, this way it might not be as readable but the format is pretty simple and
> everybody knows .csv so....
> 
> We can even do vertical alignment for better readability:
> 
> #     LEAF,SUBLEAF,register,  bits,short name, long name
>          1,	 0,	EBX,   7:0,brand,Brand Index
>          1,	 0,	EBX,  15:8,clflush_size,CLFLUSH line size (value * 8) in bytes
>      	 1,	 0,	EBX, 23:16,max_cpu_id,Maxim number of addressable logic cpu ID in this package
> 0x80000000,	12,	EAX,     3,eee, EEE
> 
> and can keep it in tabellary form this way. The parsing code should
> asimply split on ',' and ignore whitespace.
> 
> Thoughts?
 
Thanks, it looks good to me. One thing is in v2 we supported ',' in the
long name field, but that's minor as we could avoid using it in .csv 

- Feng

> -- 
> Regards/Gruss,
>     Boris.
> 
> https://people.kernel.org/tglx/notes-about-netiquette

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v2] tools/x86: add kcpuid tool to show raw CPU features
  2020-09-28  7:07   ` Feng Tang
@ 2020-09-28  8:34     ` Borislav Petkov
  2020-09-28  8:56       ` Feng Tang
  0 siblings, 1 reply; 17+ messages in thread
From: Borislav Petkov @ 2020-09-28  8:34 UTC (permalink / raw)
  To: Feng Tang
  Cc: Thomas Gleixner, Ingo Molnar, H . Peter Anvin, Peter Zijlstra,
	Dave Hansen, x86, linux-kernel

On Mon, Sep 28, 2020 at 03:07:23PM +0800, Feng Tang wrote:
> Thanks, it looks good to me. One thing is in v2 we supported ',' in the
> long name field, but that's minor as we could avoid using it in .csv

We can always use ';' or some other symbol as divider if we wanna use
',' in the long name field but I can live with either solutions.

-- 
Regards/Gruss,
    Boris.

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

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v2] tools/x86: add kcpuid tool to show raw CPU features
  2020-09-28  8:34     ` Borislav Petkov
@ 2020-09-28  8:56       ` Feng Tang
  0 siblings, 0 replies; 17+ messages in thread
From: Feng Tang @ 2020-09-28  8:56 UTC (permalink / raw)
  To: Borislav Petkov
  Cc: Thomas Gleixner, Ingo Molnar, H . Peter Anvin, Peter Zijlstra,
	Dave Hansen, x86, linux-kernel

On Mon, Sep 28, 2020 at 10:34:20AM +0200, Borislav Petkov wrote:
> On Mon, Sep 28, 2020 at 03:07:23PM +0800, Feng Tang wrote:
> > Thanks, it looks good to me. One thing is in v2 we supported ',' in the
> > long name field, but that's minor as we could avoid using it in .csv
> 
> We can always use ';' or some other symbol as divider if we wanna use
> ',' in the long name field but I can live with either solutions.
 
Ok, will check both the them in implementation, and sent the v3 RFC

Thanks,
Feng

> -- 
> Regards/Gruss,
>     Boris.
> 
> https://people.kernel.org/tglx/notes-about-netiquette

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2020-09-28  8:56 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-22  5:27 [RFC PATCH v2] tools/x86: add kcpuid tool to show raw CPU features Feng Tang
2020-09-22 19:38 ` Dave Hansen
2020-09-25  7:22   ` Feng Tang
2020-09-25 17:27   ` Borislav Petkov
2020-09-22 20:10 ` Borislav Petkov
2020-09-22 22:15   ` Arvind Sankar
2020-09-23  2:45     ` Feng Tang
2020-09-25 17:16       ` Borislav Petkov
2020-09-25 19:48         ` Arvind Sankar
2020-09-25 20:17           ` Borislav Petkov
2020-09-25 20:40             ` Arvind Sankar
2020-09-25 20:57               ` Borislav Petkov
2020-09-25 17:24 ` Borislav Petkov
2020-09-25 17:26   ` Dave Hansen
2020-09-28  7:07   ` Feng Tang
2020-09-28  8:34     ` Borislav Petkov
2020-09-28  8:56       ` Feng Tang

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.