x86: cpu_debug add write support for MSRs
authorJaswinder Singh Rajput <jaswinderrajput@gmail.com>
Wed, 11 Mar 2009 21:07:00 +0000 (02:37 +0530)
committerIngo Molnar <mingo@elte.hu>
Fri, 13 Mar 2009 02:02:45 +0000 (03:02 +0100)
Supported write flag for registers.
currently write is enabled only for PMC MSR.

[root@ht]# cat /sys/kernel/debug/x86/cpu/cpu1/pmc/0x300/value
0x0

[root@ht]# echo 1234 > /sys/kernel/debug/x86/cpu/cpu1/pmc/0x300/value
[root@ht]# cat /sys/kernel/debug/x86/cpu/cpu1/pmc/0x300/value
0x4d2

[root@ht]# echo 0x1234 > /sys/kernel/debug/x86/cpu/cpu1/pmc/0x300/value
[root@ht]# cat /sys/kernel/debug/x86/cpu/cpu1/pmc/0x300/value
0x1234

Signed-off-by: Jaswinder Singh Rajput <jaswinderrajput@gmail.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
arch/x86/include/asm/cpu_debug.h
arch/x86/kernel/cpu/cpu_debug.c

index d24d64fcee04a7dbd48bfe851bf0dbb64a670c10..56f1635e4617f721d526e10f201e4f4d230490d1 100755 (executable)
@@ -171,16 +171,22 @@ struct cpu_private {
 struct cpu_debug_base {
        char                    *name;          /* Register name        */
        unsigned                flag;           /* Register flag        */
+       unsigned                write;          /* Register write flag  */
 };
 
-struct cpu_cpuX_base {
-       struct dentry           *dentry;        /* Register dentry      */
-       int                     init;           /* Register index file  */
-};
-
+/*
+ * Currently it looks similar to cpu_debug_base but once we add more files
+ * cpu_file_base will go in different direction
+ */
 struct cpu_file_base {
        char                    *name;          /* Register file name   */
        unsigned                flag;           /* Register file flag   */
+       unsigned                write;          /* Register write flag  */
+};
+
+struct cpu_cpuX_base {
+       struct dentry           *dentry;        /* Register dentry      */
+       int                     init;           /* Register index file  */
 };
 
 struct cpu_debug_range {
index 9abbcbd933ccc87c26153b30a0e57ad460eb2eeb..21c0cf8ced18de6189de936e55d16c37be261259 100755 (executable)
@@ -11,6 +11,7 @@
 #include <linux/seq_file.h>
 #include <linux/debugfs.h>
 #include <linux/kprobes.h>
+#include <linux/uaccess.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/percpu.h>
@@ -40,41 +41,41 @@ static DEFINE_MUTEX(cpu_debug_lock);
 static struct dentry *cpu_debugfs_dir;
 
 static struct cpu_debug_base cpu_base[] = {
-       { "mc",         CPU_MC          },      /* Machine Check        */
-       { "monitor",    CPU_MONITOR     },      /* Monitor              */
-       { "time",       CPU_TIME        },      /* Time                 */
-       { "pmc",        CPU_PMC         },      /* Performance Monitor  */
-       { "platform",   CPU_PLATFORM    },      /* Platform             */
-       { "apic",       CPU_APIC        },      /* APIC                 */
-       { "poweron",    CPU_POWERON     },      /* Power-on             */
-       { "control",    CPU_CONTROL     },      /* Control              */
-       { "features",   CPU_FEATURES    },      /* Features control     */
-       { "lastbranch", CPU_LBRANCH     },      /* Last Branch          */
-       { "bios",       CPU_BIOS        },      /* BIOS                 */
-       { "freq",       CPU_FREQ        },      /* Frequency            */
-       { "mtrr",       CPU_MTRR        },      /* MTRR                 */
-       { "perf",       CPU_PERF        },      /* Performance          */
-       { "cache",      CPU_CACHE       },      /* Cache                */
-       { "sysenter",   CPU_SYSENTER    },      /* Sysenter             */
-       { "therm",      CPU_THERM       },      /* Thermal              */
-       { "misc",       CPU_MISC        },      /* Miscellaneous        */
-       { "debug",      CPU_DEBUG       },      /* Debug                */
-       { "pat",        CPU_PAT         },      /* PAT                  */
-       { "vmx",        CPU_VMX         },      /* VMX                  */
-       { "call",       CPU_CALL        },      /* System Call          */
-       { "base",       CPU_BASE        },      /* BASE Address         */
-       { "smm",        CPU_SMM         },      /* System mgmt mode     */
-       { "svm",        CPU_SVM         },      /*Secure Virtial Machine*/
-       { "osvm",       CPU_OSVM        },      /* OS-Visible Workaround*/
-       { "tss",        CPU_TSS         },      /* Task Stack Segment   */
-       { "cr",         CPU_CR          },      /* Control Registers    */
-       { "dt",         CPU_DT          },      /* Descriptor Table     */
-       { "registers",  CPU_REG_ALL     },      /* Select all Registers */
+       { "mc",         CPU_MC,         0       },
+       { "monitor",    CPU_MONITOR,    0       },
+       { "time",       CPU_TIME,       0       },
+       { "pmc",        CPU_PMC,        1       },
+       { "platform",   CPU_PLATFORM,   0       },
+       { "apic",       CPU_APIC,       0       },
+       { "poweron",    CPU_POWERON,    0       },
+       { "control",    CPU_CONTROL,    0       },
+       { "features",   CPU_FEATURES,   0       },
+       { "lastbranch", CPU_LBRANCH,    0       },
+       { "bios",       CPU_BIOS,       0       },
+       { "freq",       CPU_FREQ,       0       },
+       { "mtrr",       CPU_MTRR,       0       },
+       { "perf",       CPU_PERF,       0       },
+       { "cache",      CPU_CACHE,      0       },
+       { "sysenter",   CPU_SYSENTER,   0       },
+       { "therm",      CPU_THERM,      0       },
+       { "misc",       CPU_MISC,       0       },
+       { "debug",      CPU_DEBUG,      0       },
+       { "pat",        CPU_PAT,        0       },
+       { "vmx",        CPU_VMX,        0       },
+       { "call",       CPU_CALL,       0       },
+       { "base",       CPU_BASE,       0       },
+       { "smm",        CPU_SMM,        0       },
+       { "svm",        CPU_SVM,        0       },
+       { "osvm",       CPU_OSVM,       0       },
+       { "tss",        CPU_TSS,        0       },
+       { "cr",         CPU_CR,         0       },
+       { "dt",         CPU_DT,         0       },
+       { "registers",  CPU_REG_ALL,    0       },
 };
 
 static struct cpu_file_base cpu_file[] = {
-       { "index",      CPU_REG_ALL     },      /* index                */
-       { "value",      CPU_REG_ALL     },      /* value                */
+       { "index",      CPU_REG_ALL,    0       },
+       { "value",      CPU_REG_ALL,    1       },
 };
 
 /* Intel Registers Range */
@@ -608,9 +609,62 @@ static int cpu_seq_open(struct inode *inode, struct file *file)
        return err;
 }
 
+static int write_msr(struct cpu_private *priv, u64 val)
+{
+       u32 low, high;
+
+       high = (val >> 32) & 0xffffffff;
+       low = val & 0xffffffff;
+
+       if (!wrmsr_safe_on_cpu(priv->cpu, priv->reg, low, high))
+               return 0;
+
+       return -EPERM;
+}
+
+static int write_cpu_register(struct cpu_private *priv, const char *buf)
+{
+       int ret = -EPERM;
+       u64 val;
+
+       ret = strict_strtoull(buf, 0, &val);
+       if (ret < 0)
+               return ret;
+
+       /* Supporting only MSRs */
+       if (priv->type < CPU_TSS_BIT)
+               return write_msr(priv, val);
+
+       return ret;
+}
+
+static ssize_t cpu_write(struct file *file, const char __user *ubuf,
+                            size_t count, loff_t *off)
+{
+       struct seq_file *seq = file->private_data;
+       struct cpu_private *priv = seq->private;
+       char buf[19];
+
+       if ((priv == NULL) || (count >= sizeof(buf)))
+               return -EINVAL;
+
+       if (copy_from_user(&buf, ubuf, count))
+               return -EFAULT;
+
+       buf[count] = 0;
+
+       if ((cpu_base[priv->type].write) && (cpu_file[priv->file].write))
+               if (!write_cpu_register(priv, buf))
+                       return count;
+
+       return -EACCES;
+}
+
 static const struct file_operations cpu_fops = {
+       .owner          = THIS_MODULE,
        .open           = cpu_seq_open,
        .read           = seq_read,
+       .write          = cpu_write,
        .llseek         = seq_lseek,
        .release        = seq_release,
 };