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 <b...@suse.de> 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 <b...@suse.de> --- 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 <b...@alien8.de>"); +MODULE_DESCRIPTION("x86 arch info dumper"); -- 2.3.5 -- Regards/Gruss, Boris. ECO tip #101: Trim your mails when you reply.