Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  1 /*
  2  * Machine check injection support.
  3  * Copyright 2008 Intel Corporation.
  4  *
  5  * This program is free software; you can redistribute it and/or
  6  * modify it under the terms of the GNU General Public License
  7  * as published by the Free Software Foundation; version 2
  8  * of the License.
  9  *
 10  * Authors:
 11  * Andi Kleen
 12  * Ying Huang
 13  */
 14 #include <linux/uaccess.h>
 15 #include <linux/module.h>
 16 #include <linux/timer.h>
 17 #include <linux/kernel.h>
 18 #include <linux/string.h>
 19 #include <linux/fs.h>
 20 #include <linux/smp.h>
 21 #include <asm/mce.h>
 22 
 23 /* Update fake mce registers on current CPU. */
 24 static void inject_mce(struct mce *m)
 25 {
 26         struct mce *i = &per_cpu(injectm, m->extcpu);
 27 
 28         /* Make sure noone reads partially written injectm */
 29         i->finished = 0;
 30         mb();
 31         m->finished = 0;
 32         /* First set the fields after finished */
 33         i->extcpu = m->extcpu;
 34         mb();
 35         /* Now write record in order, finished last (except above) */
 36         memcpy(i, m, sizeof(struct mce));
 37         /* Finally activate it */
 38         mb();
 39         i->finished = 1;
 40 }
 41 
 42 struct delayed_mce {
 43         struct timer_list timer;
 44         struct mce m;
 45 };
 46 
 47 /* Inject mce on current CPU */
 48 static void raise_mce(unsigned long data)
 49 {
 50         struct delayed_mce *dm = (struct delayed_mce *)data;
 51         struct mce *m = &dm->m;
 52         int cpu = m->extcpu;
 53 
 54         inject_mce(m);
 55         if (m->status & MCI_STATUS_UC) {
 56                 struct pt_regs regs;
 57                 memset(&regs, 0, sizeof(struct pt_regs));
 58                 regs.ip = m->ip;
 59                 regs.cs = m->cs;
 60                 printk(KERN_INFO "Triggering MCE exception on CPU %d\n", cpu);
 61                 do_machine_check(&regs, 0);
 62                 printk(KERN_INFO "MCE exception done on CPU %d\n", cpu);
 63         } else {
 64                 mce_banks_t b;
 65                 memset(&b, 0xff, sizeof(mce_banks_t));
 66                 printk(KERN_INFO "Starting machine check poll CPU %d\n", cpu);
 67                 machine_check_poll(0, &b);
 68                 mce_notify_irq();
 69                 printk(KERN_INFO "Finished machine check poll on CPU %d\n",
 70                        cpu);
 71         }
 72         kfree(dm);
 73 }
 74 
 75 /* Error injection interface */
 76 static ssize_t mce_write(struct file *filp, const char __user *ubuf,
 77                          size_t usize, loff_t *off)
 78 {
 79         struct delayed_mce *dm;
 80         struct mce m;
 81 
 82         if (!capable(CAP_SYS_ADMIN))
 83                 return -EPERM;
 84         /*
 85          * There are some cases where real MSR reads could slip
 86          * through.
 87          */
 88         if (!boot_cpu_has(X86_FEATURE_MCE) || !boot_cpu_has(X86_FEATURE_MCA))
 89                 return -EIO;
 90 
 91         if ((unsigned long)usize > sizeof(struct mce))
 92                 usize = sizeof(struct mce);
 93         if (copy_from_user(&m, ubuf, usize))
 94                 return -EFAULT;
 95 
 96         if (m.extcpu >= num_possible_cpus() || !cpu_online(m.extcpu))
 97                 return -EINVAL;
 98 
 99         dm = kmalloc(sizeof(struct delayed_mce), GFP_KERNEL);
100         if (!dm)
101                 return -ENOMEM;
102 
103         /*
104          * Need to give user space some time to set everything up,
105          * so do it a jiffie or two later everywhere.
106          * Should we use a hrtimer here for better synchronization?
107          */
108         memcpy(&dm->m, &m, sizeof(struct mce));
109         setup_timer(&dm->timer, raise_mce, (unsigned long)dm);
110         dm->timer.expires = jiffies + 2;
111         add_timer_on(&dm->timer, m.extcpu);
112         return usize;
113 }
114 
115 static int inject_init(void)
116 {
117         printk(KERN_INFO "Machine check injector initialized\n");
118         mce_chrdev_ops.write = mce_write;
119         return 0;
120 }
121 
122 module_init(inject_init);
123 /*
124  * Cannot tolerate unloading currently because we cannot
125  * guarantee all openers of mce_chrdev will get a reference to us.
126  */
127 MODULE_LICENSE("GPL");
128 
  This page was automatically generated by the LXR engine.