diff options
| -rwxr-xr-x | arch/x86/include/asm/cpu_debug.h | 16 | ||||
| -rwxr-xr-x | arch/x86/kernel/cpu/cpu_debug.c | 118 |
2 files changed, 97 insertions, 37 deletions
diff --git a/arch/x86/include/asm/cpu_debug.h b/arch/x86/include/asm/cpu_debug.h index d24d64fcee04..56f1635e4617 100755 --- a/arch/x86/include/asm/cpu_debug.h +++ b/arch/x86/include/asm/cpu_debug.h | |||
| @@ -171,16 +171,22 @@ struct cpu_private { | |||
| 171 | struct cpu_debug_base { | 171 | struct cpu_debug_base { |
| 172 | char *name; /* Register name */ | 172 | char *name; /* Register name */ |
| 173 | unsigned flag; /* Register flag */ | 173 | unsigned flag; /* Register flag */ |
| 174 | unsigned write; /* Register write flag */ | ||
| 174 | }; | 175 | }; |
| 175 | 176 | ||
| 176 | struct cpu_cpuX_base { | 177 | /* |
| 177 | struct dentry *dentry; /* Register dentry */ | 178 | * Currently it looks similar to cpu_debug_base but once we add more files |
| 178 | int init; /* Register index file */ | 179 | * cpu_file_base will go in different direction |
| 179 | }; | 180 | */ |
| 180 | |||
| 181 | struct cpu_file_base { | 181 | struct cpu_file_base { |
| 182 | char *name; /* Register file name */ | 182 | char *name; /* Register file name */ |
| 183 | unsigned flag; /* Register file flag */ | 183 | unsigned flag; /* Register file flag */ |
| 184 | unsigned write; /* Register write flag */ | ||
| 185 | }; | ||
| 186 | |||
| 187 | struct cpu_cpuX_base { | ||
| 188 | struct dentry *dentry; /* Register dentry */ | ||
| 189 | int init; /* Register index file */ | ||
| 184 | }; | 190 | }; |
| 185 | 191 | ||
| 186 | struct cpu_debug_range { | 192 | struct cpu_debug_range { |
diff --git a/arch/x86/kernel/cpu/cpu_debug.c b/arch/x86/kernel/cpu/cpu_debug.c index 9abbcbd933cc..21c0cf8ced18 100755 --- a/arch/x86/kernel/cpu/cpu_debug.c +++ b/arch/x86/kernel/cpu/cpu_debug.c | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include <linux/seq_file.h> | 11 | #include <linux/seq_file.h> |
| 12 | #include <linux/debugfs.h> | 12 | #include <linux/debugfs.h> |
| 13 | #include <linux/kprobes.h> | 13 | #include <linux/kprobes.h> |
| 14 | #include <linux/uaccess.h> | ||
| 14 | #include <linux/kernel.h> | 15 | #include <linux/kernel.h> |
| 15 | #include <linux/module.h> | 16 | #include <linux/module.h> |
| 16 | #include <linux/percpu.h> | 17 | #include <linux/percpu.h> |
| @@ -40,41 +41,41 @@ static DEFINE_MUTEX(cpu_debug_lock); | |||
| 40 | static struct dentry *cpu_debugfs_dir; | 41 | static struct dentry *cpu_debugfs_dir; |
| 41 | 42 | ||
| 42 | static struct cpu_debug_base cpu_base[] = { | 43 | static struct cpu_debug_base cpu_base[] = { |
| 43 | { "mc", CPU_MC }, /* Machine Check */ | 44 | { "mc", CPU_MC, 0 }, |
| 44 | { "monitor", CPU_MONITOR }, /* Monitor */ | 45 | { "monitor", CPU_MONITOR, 0 }, |
| 45 | { "time", CPU_TIME }, /* Time */ | 46 | { "time", CPU_TIME, 0 }, |
| 46 | { "pmc", CPU_PMC }, /* Performance Monitor */ | 47 | { "pmc", CPU_PMC, 1 }, |
| 47 | { "platform", CPU_PLATFORM }, /* Platform */ | 48 | { "platform", CPU_PLATFORM, 0 }, |
| 48 | { "apic", CPU_APIC }, /* APIC */ | 49 | { "apic", CPU_APIC, 0 }, |
| 49 | { "poweron", CPU_POWERON }, /* Power-on */ | 50 | { "poweron", CPU_POWERON, 0 }, |
| 50 | { "control", CPU_CONTROL }, /* Control */ | 51 | { "control", CPU_CONTROL, 0 }, |
| 51 | { "features", CPU_FEATURES }, /* Features control */ | 52 | { "features", CPU_FEATURES, 0 }, |
| 52 | { "lastbranch", CPU_LBRANCH }, /* Last Branch */ | 53 | { "lastbranch", CPU_LBRANCH, 0 }, |
| 53 | { "bios", CPU_BIOS }, /* BIOS */ | 54 | { "bios", CPU_BIOS, 0 }, |
| 54 | { "freq", CPU_FREQ }, /* Frequency */ | 55 | { "freq", CPU_FREQ, 0 }, |
| 55 | { "mtrr", CPU_MTRR }, /* MTRR */ | 56 | { "mtrr", CPU_MTRR, 0 }, |
| 56 | { "perf", CPU_PERF }, /* Performance */ | 57 | { "perf", CPU_PERF, 0 }, |
| 57 | { "cache", CPU_CACHE }, /* Cache */ | 58 | { "cache", CPU_CACHE, 0 }, |
| 58 | { "sysenter", CPU_SYSENTER }, /* Sysenter */ | 59 | { "sysenter", CPU_SYSENTER, 0 }, |
| 59 | { "therm", CPU_THERM }, /* Thermal */ | 60 | { "therm", CPU_THERM, 0 }, |
| 60 | { "misc", CPU_MISC }, /* Miscellaneous */ | 61 | { "misc", CPU_MISC, 0 }, |
| 61 | { "debug", CPU_DEBUG }, /* Debug */ | 62 | { "debug", CPU_DEBUG, 0 }, |
| 62 | { "pat", CPU_PAT }, /* PAT */ | 63 | { "pat", CPU_PAT, 0 }, |
| 63 | { "vmx", CPU_VMX }, /* VMX */ | 64 | { "vmx", CPU_VMX, 0 }, |
| 64 | { "call", CPU_CALL }, /* System Call */ | 65 | { "call", CPU_CALL, 0 }, |
| 65 | { "base", CPU_BASE }, /* BASE Address */ | 66 | { "base", CPU_BASE, 0 }, |
| 66 | { "smm", CPU_SMM }, /* System mgmt mode */ | 67 | { "smm", CPU_SMM, 0 }, |
| 67 | { "svm", CPU_SVM }, /*Secure Virtial Machine*/ | 68 | { "svm", CPU_SVM, 0 }, |
| 68 | { "osvm", CPU_OSVM }, /* OS-Visible Workaround*/ | 69 | { "osvm", CPU_OSVM, 0 }, |
| 69 | { "tss", CPU_TSS }, /* Task Stack Segment */ | 70 | { "tss", CPU_TSS, 0 }, |
| 70 | { "cr", CPU_CR }, /* Control Registers */ | 71 | { "cr", CPU_CR, 0 }, |
| 71 | { "dt", CPU_DT }, /* Descriptor Table */ | 72 | { "dt", CPU_DT, 0 }, |
| 72 | { "registers", CPU_REG_ALL }, /* Select all Registers */ | 73 | { "registers", CPU_REG_ALL, 0 }, |
| 73 | }; | 74 | }; |
| 74 | 75 | ||
| 75 | static struct cpu_file_base cpu_file[] = { | 76 | static struct cpu_file_base cpu_file[] = { |
| 76 | { "index", CPU_REG_ALL }, /* index */ | 77 | { "index", CPU_REG_ALL, 0 }, |
| 77 | { "value", CPU_REG_ALL }, /* value */ | 78 | { "value", CPU_REG_ALL, 1 }, |
| 78 | }; | 79 | }; |
| 79 | 80 | ||
| 80 | /* Intel Registers Range */ | 81 | /* Intel Registers Range */ |
| @@ -608,9 +609,62 @@ static int cpu_seq_open(struct inode *inode, struct file *file) | |||
| 608 | return err; | 609 | return err; |
| 609 | } | 610 | } |
| 610 | 611 | ||
| 612 | static int write_msr(struct cpu_private *priv, u64 val) | ||
| 613 | { | ||
| 614 | u32 low, high; | ||
| 615 | |||
| 616 | high = (val >> 32) & 0xffffffff; | ||
| 617 | low = val & 0xffffffff; | ||
| 618 | |||
| 619 | if (!wrmsr_safe_on_cpu(priv->cpu, priv->reg, low, high)) | ||
| 620 | return 0; | ||
| 621 | |||
| 622 | return -EPERM; | ||
| 623 | } | ||
| 624 | |||
| 625 | static int write_cpu_register(struct cpu_private *priv, const char *buf) | ||
| 626 | { | ||
| 627 | int ret = -EPERM; | ||
| 628 | u64 val; | ||
| 629 | |||
| 630 | ret = strict_strtoull(buf, 0, &val); | ||
| 631 | if (ret < 0) | ||
| 632 | return ret; | ||
| 633 | |||
| 634 | /* Supporting only MSRs */ | ||
| 635 | if (priv->type < CPU_TSS_BIT) | ||
| 636 | return write_msr(priv, val); | ||
| 637 | |||
| 638 | return ret; | ||
| 639 | } | ||
| 640 | |||
| 641 | static ssize_t cpu_write(struct file *file, const char __user *ubuf, | ||
| 642 | size_t count, loff_t *off) | ||
| 643 | { | ||
| 644 | struct seq_file *seq = file->private_data; | ||
| 645 | struct cpu_private *priv = seq->private; | ||
| 646 | char buf[19]; | ||
| 647 | |||
| 648 | if ((priv == NULL) || (count >= sizeof(buf))) | ||
| 649 | return -EINVAL; | ||
| 650 | |||
| 651 | if (copy_from_user(&buf, ubuf, count)) | ||
| 652 | return -EFAULT; | ||
| 653 | |||
| 654 | buf[count] = 0; | ||
| 655 | |||
| 656 | if ((cpu_base[priv->type].write) && (cpu_file[priv->file].write)) | ||
| 657 | if (!write_cpu_register(priv, buf)) | ||
| 658 | return count; | ||
| 659 | |||
| 660 | return -EACCES; | ||
| 661 | } | ||
| 662 | |||
| 611 | static const struct file_operations cpu_fops = { | 663 | static const struct file_operations cpu_fops = { |
| 664 | .owner = THIS_MODULE, | ||
| 612 | .open = cpu_seq_open, | 665 | .open = cpu_seq_open, |
| 613 | .read = seq_read, | 666 | .read = seq_read, |
| 667 | .write = cpu_write, | ||
| 614 | .llseek = seq_lseek, | 668 | .llseek = seq_lseek, |
| 615 | .release = seq_release, | 669 | .release = seq_release, |
| 616 | }; | 670 | }; |
