1
2
3
4
5
6
7
8
9
10
11
12
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 <linux/notifier.h>
22#include <linux/kdebug.h>
23#include <linux/cpu.h>
24#include <linux/sched.h>
25#include <asm/mce.h>
26#include <asm/apic.h>
27
28
29static void inject_mce(struct mce *m)
30{
31 struct mce *i = &per_cpu(injectm, m->extcpu);
32
33
34 i->finished = 0;
35 mb();
36 m->finished = 0;
37
38 i->extcpu = m->extcpu;
39 mb();
40
41 memcpy(i, m, sizeof(struct mce));
42
43 mb();
44 i->finished = 1;
45}
46
47static void raise_poll(struct mce *m)
48{
49 unsigned long flags;
50 mce_banks_t b;
51
52 memset(&b, 0xff, sizeof(mce_banks_t));
53 local_irq_save(flags);
54 machine_check_poll(0, &b);
55 local_irq_restore(flags);
56 m->finished = 0;
57}
58
59static void raise_exception(struct mce *m, struct pt_regs *pregs)
60{
61 struct pt_regs regs;
62 unsigned long flags;
63
64 if (!pregs) {
65 memset(®s, 0, sizeof(struct pt_regs));
66 regs.ip = m->ip;
67 regs.cs = m->cs;
68 pregs = ®s;
69 }
70
71 local_irq_save(flags);
72 do_machine_check(pregs, 0);
73 local_irq_restore(flags);
74 m->finished = 0;
75}
76
77static cpumask_t mce_inject_cpumask;
78
79static int mce_raise_notify(struct notifier_block *self,
80 unsigned long val, void *data)
81{
82 struct die_args *args = (struct die_args *)data;
83 int cpu = smp_processor_id();
84 struct mce *m = &__get_cpu_var(injectm);
85 if (val != DIE_NMI_IPI || !cpu_isset(cpu, mce_inject_cpumask))
86 return NOTIFY_DONE;
87 cpu_clear(cpu, mce_inject_cpumask);
88 if (m->inject_flags & MCJ_EXCEPTION)
89 raise_exception(m, args->regs);
90 else if (m->status)
91 raise_poll(m);
92 return NOTIFY_STOP;
93}
94
95static struct notifier_block mce_raise_nb = {
96 .notifier_call = mce_raise_notify,
97 .priority = 1000,
98};
99
100
101static int raise_local(void)
102{
103 struct mce *m = &__get_cpu_var(injectm);
104 int context = MCJ_CTX(m->inject_flags);
105 int ret = 0;
106 int cpu = m->extcpu;
107
108 if (m->inject_flags & MCJ_EXCEPTION) {
109 printk(KERN_INFO "Triggering MCE exception on CPU %d\n", cpu);
110 switch (context) {
111 case MCJ_CTX_IRQ:
112
113
114
115
116
117
118 case MCJ_CTX_PROCESS:
119 raise_exception(m, NULL);
120 break;
121 default:
122 printk(KERN_INFO "Invalid MCE context\n");
123 ret = -EINVAL;
124 }
125 printk(KERN_INFO "MCE exception done on CPU %d\n", cpu);
126 } else if (m->status) {
127 printk(KERN_INFO "Starting machine check poll CPU %d\n", cpu);
128 raise_poll(m);
129 mce_notify_irq();
130 printk(KERN_INFO "Machine check poll done on CPU %d\n", cpu);
131 } else
132 m->finished = 0;
133
134 return ret;
135}
136
137static void raise_mce(struct mce *m)
138{
139 int context = MCJ_CTX(m->inject_flags);
140
141 inject_mce(m);
142
143 if (context == MCJ_CTX_RANDOM)
144 return;
145
146#ifdef CONFIG_X86_LOCAL_APIC
147 if (m->inject_flags & MCJ_NMI_BROADCAST) {
148 unsigned long start;
149 int cpu;
150 get_online_cpus();
151 mce_inject_cpumask = cpu_online_map;
152 cpu_clear(get_cpu(), mce_inject_cpumask);
153 for_each_online_cpu(cpu) {
154 struct mce *mcpu = &per_cpu(injectm, cpu);
155 if (!mcpu->finished ||
156 MCJ_CTX(mcpu->inject_flags) != MCJ_CTX_RANDOM)
157 cpu_clear(cpu, mce_inject_cpumask);
158 }
159 if (!cpus_empty(mce_inject_cpumask))
160 apic->send_IPI_mask(&mce_inject_cpumask, NMI_VECTOR);
161 start = jiffies;
162 while (!cpus_empty(mce_inject_cpumask)) {
163 if (!time_before(jiffies, start + 2*HZ)) {
164 printk(KERN_ERR
165 "Timeout waiting for mce inject NMI %lx\n",
166 *cpus_addr(mce_inject_cpumask));
167 break;
168 }
169 cpu_relax();
170 }
171 raise_local();
172 put_cpu();
173 put_online_cpus();
174 } else
175#endif
176 raise_local();
177}
178
179
180static ssize_t mce_write(struct file *filp, const char __user *ubuf,
181 size_t usize, loff_t *off)
182{
183 struct mce m;
184
185 if (!capable(CAP_SYS_ADMIN))
186 return -EPERM;
187
188
189
190
191 if (!boot_cpu_has(X86_FEATURE_MCE) || !boot_cpu_has(X86_FEATURE_MCA))
192 return -EIO;
193
194 if ((unsigned long)usize > sizeof(struct mce))
195 usize = sizeof(struct mce);
196 if (copy_from_user(&m, ubuf, usize))
197 return -EFAULT;
198
199 if (m.extcpu >= num_possible_cpus() || !cpu_online(m.extcpu))
200 return -EINVAL;
201
202
203
204
205
206 schedule_timeout(2);
207 raise_mce(&m);
208 return usize;
209}
210
211static int inject_init(void)
212{
213 printk(KERN_INFO "Machine check injector initialized\n");
214 mce_chrdev_ops.write = mce_write;
215 register_die_notifier(&mce_raise_nb);
216 return 0;
217}
218
219module_init(inject_init);
220
221
222
223
224MODULE_LICENSE("GPL");
225