[PATCH -v2] x86: Add an archinfo dumper module

From: Borislav Petkov
Date: Thu Feb 04 2016 - 10:22:50 EST


Here's v2 with the stuff we talked about, implemented. I've added
'control_regs' file too so that you can do:

$ cat /sys/kernel/debug/x86/archinfo/control_regs
CR4: [-|-|SMEP|OSXSAVE|-|-|-|-|OSXMMEXCPT|OSFXSR|-|PGE|MCE|PAE|PSE|-|-|-|-]: 0x1406f0

for example. Yeah, only CR4 right now.

Off the top of my head, we would need "msrs" which dumps EFER and a
bunch of other interesting MSRs along with the names of the set bits.

---
From: Borislav Petkov <bp@xxxxxxx>
Date: Mon, 30 Nov 2015 13:53:36 +0100
Subject: [PATCH -v2] x86: Add an archinfo dumper module

Dump interesting/valuable information related to the x86 architecture of
the current CPU and platform.

Signed-off-by: Borislav Petkov <bp@xxxxxxx>
---
arch/x86/Kconfig.debug | 5 ++
arch/x86/kernel/Makefile | 1 +
arch/x86/kernel/archinfo.c | 215 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 221 insertions(+)
create mode 100644 arch/x86/kernel/archinfo.c

diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug
index 9b18ed97a8a2..c757d1ee9a7a 100644
--- a/arch/x86/Kconfig.debug
+++ b/arch/x86/Kconfig.debug
@@ -383,4 +383,9 @@ config PUNIT_ATOM_DEBUG
The current power state can be read from
/sys/kernel/debug/punit_atom/dev_power_state

+config ARCHINFO
+ tristate "x86 archinfo dumper module"
+ ---help---
+ Dump interesting x86 arch stuff.
+
endmenu
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index b1b78ffe01d0..89972c1b9de6 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -109,6 +109,7 @@ obj-$(CONFIG_EFI) += sysfb_efi.o

obj-$(CONFIG_PERF_EVENTS) += perf_regs.o
obj-$(CONFIG_TRACING) += tracepoint.o
+obj-$(CONFIG_ARCHINFO) += archinfo.o

###
# 64 bit specific files
diff --git a/arch/x86/kernel/archinfo.c b/arch/x86/kernel/archinfo.c
new file mode 100644
index 000000000000..c04e98625565
--- /dev/null
+++ b/arch/x86/kernel/archinfo.c
@@ -0,0 +1,215 @@
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/cpu.h>
+
+#include <asm/desc.h>
+
+static const char * const system_desc_types[] = {
+ [0] = "Reserved (illegal)",
+ [1] = "Available 16-bit TSS",
+ [2] = "LDT",
+ [3] = "Busy 16-bit TSS",
+ [4] = "16-bit Call Gate",
+ [5] = "Task Gate",
+ [6] = "16-bit Interrupt Gate",
+ [7] = "16-bit Trap Gate",
+ [8] = "Reserved (illegal)",
+ [9] = "Available 32-bit TSS",
+ [10] = "Reserved (illegal)",
+ [11] = "Busy 32-bit TSS",
+ [12] = "32-bit Call Gate",
+ [13] = "Reserved (illegal)",
+ [14] = "32-bit Interrupt Gate",
+ [15] = "32-bit Trap Gate",
+};
+
+static const char * const user_desc_types[] = {
+ [0] = "Read-Only",
+ [1] = "Read-only - Accessed",
+ [2] = "Read/Write",
+ [3] = "Read/Write - Accessed",
+ [4] = "Expand-down, Read-Only",
+ [5] = "Expand-down, Read-Only - Accessed",
+ [6] = "Expand-down, Read-Write",
+ [7] = "Expand-down, Read-Write - Accessed",
+ [8] = "Execute-Only",
+ [9] = "Execute-Only - Accessed",
+ [10] = "Execute/Readable",
+ [11] = "Execute/Readable - Accessed",
+ [12] = "Conforming, Execute-Only",
+ [13] = "Conforming, Execute-Only - Accessed",
+ [14] = "Conforming, Execute/Readable",
+ [15] = "Conforming, Execute/Readable - Accessed",
+};
+
+static void print_seg_desc(struct seq_file *m, struct desc_struct *d, int num)
+{
+ seq_printf(m, "%02d:\n", num);
+ seq_printf(m, "[ base[31:24]:%02x G:%x D:%x L:%x AVL:%x lim[19:16]:%x |",
+ d->base2, d->g, d->d, d->l, d->avl, d->limit);
+ seq_printf(m, " P:%x DPL:%x S:%x C:%x base[23:16]:%02x ]\n",
+ d->p, d->dpl, d->s, !!(d->type & BIT(2)), d->base1);
+ seq_printf(m, "[ base[15:00]:%04x | lim[15:00]:%04x ]: ",
+ d->base0, d->limit0);
+
+ if (d->s)
+ seq_printf(m, "User: (0x%x) %s, %s\n",
+ d->type,
+ (d->type > 7 ? "Code" : "Data"),
+ (user_desc_types[d->type]));
+ else
+ seq_printf(m, "System: (0x%x) %s\n", d->type, system_desc_types[d->type]);
+
+ seq_printf(m, "\n");
+}
+
+static void dump_gdt(void *info)
+{
+ struct gdt_page *g = this_cpu_ptr(&gdt_page);
+ struct seq_file *m = (struct seq_file *)info;
+ int i;
+
+ seq_printf(m, "CPU%d, GDT %p:\n", smp_processor_id(), &g->gdt);
+
+ for (i = 0; i < GDT_ENTRIES; i++)
+ print_seg_desc(m, &g->gdt[i], i);
+
+ seq_printf(m, "----\n");
+
+}
+
+static int gdt_show(struct seq_file *m, void *v)
+{
+ int c;
+
+ /*
+ * Using on_each_cpu() here fudges the output and we want it nicely
+ * sorted by CPU.
+ */
+ get_online_cpus();
+ for_each_online_cpu(c)
+ smp_call_function_single(c, dump_gdt, m, 1);
+ put_online_cpus();
+
+ seq_printf(m,
+ "\nInfo:\n"
+ "base,limit,A,G,R: ignored in 64-bit mode.\n"
+ "G: granularity bit (23):\n"
+ "\t- 0b: segment limit is not scaled.\n"
+ "\t- 1b: segment limit scaled by 4K.\n"
+ "D/B: CS default operand size bit (22):\n"
+ "\t- 0b: 16-bit.\n"
+ "\t- 1b: 32-bit.\n"
+ "\tD=0b is the only allowed setting in long mode (L=1b).\n"
+ "\tCalled B in stack segments.\n"
+ "L: long mode bit (21):\n"
+ "\t- 0b: CPU in compat mode. Enables segmentation.\n"
+ "\t- 1b: CPU in long mode.\n"
+ "AVL: bit available to software (20).\n"
+ "P: present bit (15):\n"
+ "\t- 0b: seg. not present in mem => #NP.\n"
+ "\t- 1b: seg is present in memory.\n"
+ "DPL: Descriptor Privilege Level [14:13]:\n"
+ "\t- 0b: highest privilege level.\n"
+ " ...\n"
+ "\t- 3b: lowest privilege level.\n"
+ "S+Type: decriptor types [12,11:8]:\n"
+ "\t Specify descriptor type and access characteristics.\n"
+ " S:\n"
+ "\t- 0b: System descriptor.\n"
+ "\t- 1b: User descriptor.\n"
+ " R: readable bit (9):\n"
+ "\t- 0b: code seg is executable, reads -> #GP\n"
+ "\t- 1b: code seg is both read/exec\n"
+ " A: accessed bit (8): set by CPU when desc copied into %%cs; cleared only by sw.\n"
+ );
+
+ return 0;
+}
+
+static int gdt_open(struct inode *inode, struct file *filp)
+{
+ return single_open(filp, gdt_show, NULL);
+}
+
+static int cr_show(struct seq_file *m, void *v)
+{
+ unsigned long cr4 = __read_cr4();
+
+ seq_printf(m, "CR4: [%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s]: 0x%lx\n",
+ (cr4 & BIT(22) ? "PKE" : "-"),
+ (cr4 & BIT(21) ? "SMAP" : "-"),
+ (cr4 & BIT(20) ? "SMEP" : "-"),
+ (cr4 & BIT(18) ? "OSXSAVE" : "-"),
+ (cr4 & BIT(17) ? "PCIDE" : "-"),
+ (cr4 & BIT(16) ? "FSGSBASE" : "-"),
+ (cr4 & BIT(14) ? "SMXE" : "-"),
+ (cr4 & BIT(13) ? "VMXE" : "-"),
+ (cr4 & BIT(10) ? "OSXMMEXCPT" : "-"),
+ (cr4 & BIT(9) ? "OSFXSR" : "-"),
+ (cr4 & BIT(8) ? "PCE" : "-"),
+ (cr4 & BIT(7) ? "PGE" : "-"),
+ (cr4 & BIT(6) ? "MCE" : "-"),
+ (cr4 & BIT(5) ? "PAE" : "-"),
+ (cr4 & BIT(4) ? "PSE" : "-"),
+ (cr4 & BIT(3) ? "DE" : "-"),
+ (cr4 & BIT(2) ? "TSD" : "-"),
+ (cr4 & BIT(1) ? "PVI" : "-"),
+ (cr4 & BIT(0) ? "VME" : "-"),
+ cr4);
+
+ return 0;
+}
+
+static int cr_open(struct inode *inode, struct file *filp)
+{
+ return single_open(filp, cr_show, NULL);
+}
+
+static const struct file_operations cr_fops = {
+ .owner = THIS_MODULE,
+ .open = cr_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations gdt_fops = {
+ .owner = THIS_MODULE,
+ .open = gdt_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct dentry *dfs_entry;
+
+static int __init archinfo_init(void)
+{
+ dfs_entry = debugfs_create_dir("archinfo", arch_debugfs_dir);
+ if (IS_ERR_OR_NULL(dfs_entry))
+ return -EINVAL;
+
+ if (!debugfs_create_file("gdt", S_IRUSR, dfs_entry, NULL, &gdt_fops)) {
+ debugfs_remove_recursive(dfs_entry);
+ return -EINVAL;
+ }
+
+ if (!debugfs_create_file("control_regs", S_IRUSR, dfs_entry, NULL, &cr_fops)) {
+ debugfs_remove_recursive(dfs_entry);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void __exit archinfo_exit(void)
+{
+ debugfs_remove_recursive(dfs_entry);
+}
+
+module_init(archinfo_init);
+module_exit(archinfo_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Borislav Petkov <bp@xxxxxxxxx>");
+MODULE_DESCRIPTION("x86 arch info dumper");
--
2.3.5


--
Regards/Gruss,
Boris.

ECO tip #101: Trim your mails when you reply.