]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - arch/x86/kernel/cpu/mcheck/mce-inject.c
x86/mce-inject: Reset injection struct after injection
[mirror_ubuntu-bionic-kernel.git] / arch / x86 / kernel / cpu / mcheck / mce-inject.c
CommitLineData
ea149b36
AK
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
bc8e80d5
BP
13 *
14 * The AMD part (from mce_amd_inj.c): a simple MCE injection facility
15 * for testing different aspects of the RAS code. This driver should be
16 * built as module so that it can be loaded on production kernels for
17 * testing purposes.
18 *
19 * This file may be distributed under the terms of the GNU General Public
20 * License version 2.
21 *
22 * Copyright (c) 2010-17: Borislav Petkov <bp@alien8.de>
23 * Advanced Micro Devices Inc.
ea149b36 24 */
bc8e80d5
BP
25
26#include <linux/cpu.h>
27#include <linux/debugfs.h>
bc8e80d5 28#include <linux/kernel.h>
bc8e80d5
BP
29#include <linux/module.h>
30#include <linux/notifier.h>
31#include <linux/pci.h>
bc8e80d5
BP
32#include <linux/uaccess.h>
33
34#include <asm/amd_nb.h>
5b7e88ed 35#include <asm/apic.h>
bc8e80d5
BP
36#include <asm/irq_vectors.h>
37#include <asm/mce.h>
c410b830 38#include <asm/nmi.h>
bc8e80d5
BP
39#include <asm/smp.h>
40
41#include "mce-internal.h"
42
43/*
44 * Collect all the MCi_XXX settings
45 */
46static struct mce i_mce;
47static struct dentry *dfs_inj;
48
7d339271 49#define MAX_FLAG_OPT_SIZE 4
bc8e80d5
BP
50#define NBCFG 0x44
51
52enum injection_type {
53 SW_INJ = 0, /* SW injection, simply decode the error */
54 HW_INJ, /* Trigger a #MC */
55 DFR_INT_INJ, /* Trigger Deferred error interrupt */
56 THR_INT_INJ, /* Trigger threshold interrupt */
57 N_INJ_TYPES,
58};
59
60static const char * const flags_options[] = {
61 [SW_INJ] = "sw",
62 [HW_INJ] = "hw",
63 [DFR_INT_INJ] = "df",
64 [THR_INT_INJ] = "th",
65 NULL
66};
67
68/* Set default injection to SW_INJ */
69static enum injection_type inj_type = SW_INJ;
70
71#define MCE_INJECT_SET(reg) \
72static int inj_##reg##_set(void *data, u64 val) \
73{ \
74 struct mce *m = (struct mce *)data; \
75 \
76 m->reg = val; \
77 return 0; \
78}
79
80MCE_INJECT_SET(status);
81MCE_INJECT_SET(misc);
82MCE_INJECT_SET(addr);
83MCE_INJECT_SET(synd);
84
85#define MCE_INJECT_GET(reg) \
86static int inj_##reg##_get(void *data, u64 *val) \
87{ \
88 struct mce *m = (struct mce *)data; \
89 \
90 *val = m->reg; \
91 return 0; \
92}
93
94MCE_INJECT_GET(status);
95MCE_INJECT_GET(misc);
96MCE_INJECT_GET(addr);
97MCE_INJECT_GET(synd);
98
99DEFINE_SIMPLE_ATTRIBUTE(status_fops, inj_status_get, inj_status_set, "%llx\n");
100DEFINE_SIMPLE_ATTRIBUTE(misc_fops, inj_misc_get, inj_misc_set, "%llx\n");
101DEFINE_SIMPLE_ATTRIBUTE(addr_fops, inj_addr_get, inj_addr_set, "%llx\n");
102DEFINE_SIMPLE_ATTRIBUTE(synd_fops, inj_synd_get, inj_synd_set, "%llx\n");
ea149b36 103
86d2eac5
BP
104static void setup_inj_struct(struct mce *m)
105{
106 memset(m, 0, sizeof(struct mce));
107
108 m->cpuvendor = boot_cpu_data.x86_vendor;
2cc23299
BP
109 m->time = ktime_get_real_seconds();
110 m->cpuid = cpuid_eax(1);
111 m->microcode = boot_cpu_data.microcode;
86d2eac5
BP
112}
113
ea149b36
AK
114/* Update fake mce registers on current CPU. */
115static void inject_mce(struct mce *m)
116{
d620c67f 117 struct mce *i = &per_cpu(injectm, m->extcpu);
ea149b36 118
0d2eb44f 119 /* Make sure no one reads partially written injectm */
ea149b36
AK
120 i->finished = 0;
121 mb();
122 m->finished = 0;
123 /* First set the fields after finished */
d620c67f 124 i->extcpu = m->extcpu;
ea149b36
AK
125 mb();
126 /* Now write record in order, finished last (except above) */
127 memcpy(i, m, sizeof(struct mce));
128 /* Finally activate it */
129 mb();
130 i->finished = 1;
131}
132
0dcc6685 133static void raise_poll(struct mce *m)
5b7e88ed
HY
134{
135 unsigned long flags;
136 mce_banks_t b;
137
138 memset(&b, 0xff, sizeof(mce_banks_t));
139 local_irq_save(flags);
140 machine_check_poll(0, &b);
141 local_irq_restore(flags);
142 m->finished = 0;
143}
144
0dcc6685 145static void raise_exception(struct mce *m, struct pt_regs *pregs)
5b7e88ed
HY
146{
147 struct pt_regs regs;
148 unsigned long flags;
149
150 if (!pregs) {
151 memset(&regs, 0, sizeof(struct pt_regs));
152 regs.ip = m->ip;
153 regs.cs = m->cs;
154 pregs = &regs;
155 }
156 /* in mcheck exeception handler, irq will be disabled */
157 local_irq_save(flags);
158 do_machine_check(pregs, 0);
159 local_irq_restore(flags);
160 m->finished = 0;
161}
162
6ac5c531 163static cpumask_var_t mce_inject_cpumask;
b5975917 164static DEFINE_MUTEX(mce_inject_mutex);
5b7e88ed 165
9c48f1c6 166static int mce_raise_notify(unsigned int cmd, struct pt_regs *regs)
5b7e88ed 167{
5b7e88ed 168 int cpu = smp_processor_id();
89cbc767 169 struct mce *m = this_cpu_ptr(&injectm);
9c48f1c6
DZ
170 if (!cpumask_test_cpu(cpu, mce_inject_cpumask))
171 return NMI_DONE;
6ac5c531 172 cpumask_clear_cpu(cpu, mce_inject_cpumask);
0dcc6685 173 if (m->inject_flags & MCJ_EXCEPTION)
9c48f1c6 174 raise_exception(m, regs);
5b7e88ed 175 else if (m->status)
0dcc6685 176 raise_poll(m);
9c48f1c6 177 return NMI_HANDLED;
5b7e88ed
HY
178}
179
2c29d9dd
CG
180static void mce_irq_ipi(void *info)
181{
182 int cpu = smp_processor_id();
89cbc767 183 struct mce *m = this_cpu_ptr(&injectm);
2c29d9dd
CG
184
185 if (cpumask_test_cpu(cpu, mce_inject_cpumask) &&
186 m->inject_flags & MCJ_EXCEPTION) {
187 cpumask_clear_cpu(cpu, mce_inject_cpumask);
188 raise_exception(m, NULL);
189 }
190}
191
ea149b36 192/* Inject mce on current CPU */
14c0abf1 193static int raise_local(void)
ea149b36 194{
89cbc767 195 struct mce *m = this_cpu_ptr(&injectm);
5b7e88ed
HY
196 int context = MCJ_CTX(m->inject_flags);
197 int ret = 0;
d620c67f 198 int cpu = m->extcpu;
ea149b36 199
0dcc6685 200 if (m->inject_flags & MCJ_EXCEPTION) {
1b74dde7 201 pr_info("Triggering MCE exception on CPU %d\n", cpu);
5b7e88ed
HY
202 switch (context) {
203 case MCJ_CTX_IRQ:
204 /*
205 * Could do more to fake interrupts like
206 * calling irq_enter, but the necessary
207 * machinery isn't exported currently.
208 */
209 /*FALL THROUGH*/
210 case MCJ_CTX_PROCESS:
0dcc6685 211 raise_exception(m, NULL);
5b7e88ed
HY
212 break;
213 default:
1b74dde7 214 pr_info("Invalid MCE context\n");
5b7e88ed
HY
215 ret = -EINVAL;
216 }
1b74dde7 217 pr_info("MCE exception done on CPU %d\n", cpu);
5b7e88ed 218 } else if (m->status) {
1b74dde7 219 pr_info("Starting machine check poll CPU %d\n", cpu);
0dcc6685 220 raise_poll(m);
9ff36ee9 221 mce_notify_irq();
1b74dde7 222 pr_info("Machine check poll done on CPU %d\n", cpu);
5b7e88ed
HY
223 } else
224 m->finished = 0;
225
226 return ret;
227}
228
bc8e80d5 229static void __maybe_unused raise_mce(struct mce *m)
5b7e88ed
HY
230{
231 int context = MCJ_CTX(m->inject_flags);
232
233 inject_mce(m);
234
235 if (context == MCJ_CTX_RANDOM)
236 return;
237
a9093684 238 if (m->inject_flags & (MCJ_IRQ_BROADCAST | MCJ_NMI_BROADCAST)) {
5b7e88ed
HY
239 unsigned long start;
240 int cpu;
2c29d9dd 241
5b7e88ed 242 get_online_cpus();
6ac5c531
RR
243 cpumask_copy(mce_inject_cpumask, cpu_online_mask);
244 cpumask_clear_cpu(get_cpu(), mce_inject_cpumask);
5b7e88ed
HY
245 for_each_online_cpu(cpu) {
246 struct mce *mcpu = &per_cpu(injectm, cpu);
247 if (!mcpu->finished ||
248 MCJ_CTX(mcpu->inject_flags) != MCJ_CTX_RANDOM)
6ac5c531 249 cpumask_clear_cpu(cpu, mce_inject_cpumask);
5b7e88ed 250 }
2c29d9dd 251 if (!cpumask_empty(mce_inject_cpumask)) {
a9093684 252 if (m->inject_flags & MCJ_IRQ_BROADCAST) {
2c29d9dd
CG
253 /*
254 * don't wait because mce_irq_ipi is necessary
255 * to be sync with following raise_local
256 */
257 preempt_disable();
258 smp_call_function_many(mce_inject_cpumask,
259 mce_irq_ipi, NULL, 0);
260 preempt_enable();
261 } else if (m->inject_flags & MCJ_NMI_BROADCAST)
262 apic->send_IPI_mask(mce_inject_cpumask,
263 NMI_VECTOR);
264 }
5b7e88ed 265 start = jiffies;
6ac5c531 266 while (!cpumask_empty(mce_inject_cpumask)) {
5b7e88ed 267 if (!time_before(jiffies, start + 2*HZ)) {
1b74dde7 268 pr_err("Timeout waiting for mce inject %lx\n",
6ac5c531 269 *cpumask_bits(mce_inject_cpumask));
5b7e88ed
HY
270 break;
271 }
272 cpu_relax();
273 }
14c0abf1 274 raise_local();
5b7e88ed
HY
275 put_cpu();
276 put_online_cpus();
d4b2ac63 277 } else {
ea22571c 278 preempt_disable();
14c0abf1 279 raise_local();
ea22571c
TG
280 preempt_enable();
281 }
ea149b36
AK
282}
283
fbe9ff9e
BP
284static int mce_inject_raise(struct notifier_block *nb, unsigned long val,
285 void *data)
ea149b36 286{
fbe9ff9e 287 struct mce *m = (struct mce *)data;
ea149b36 288
fbe9ff9e
BP
289 if (!m)
290 return NOTIFY_DONE;
b5975917
TG
291
292 mutex_lock(&mce_inject_mutex);
fbe9ff9e 293 raise_mce(m);
b5975917 294 mutex_unlock(&mce_inject_mutex);
fbe9ff9e
BP
295
296 return NOTIFY_DONE;
ea149b36 297}
fbe9ff9e
BP
298
299static struct notifier_block inject_nb = {
300 .notifier_call = mce_inject_raise,
301};
bc8e80d5
BP
302
303/*
304 * Caller needs to be make sure this cpu doesn't disappear
305 * from under us, i.e.: get_cpu/put_cpu.
306 */
307static int toggle_hw_mce_inject(unsigned int cpu, bool enable)
308{
309 u32 l, h;
310 int err;
311
312 err = rdmsr_on_cpu(cpu, MSR_K7_HWCR, &l, &h);
313 if (err) {
314 pr_err("%s: error reading HWCR\n", __func__);
315 return err;
316 }
317
318 enable ? (l |= BIT(18)) : (l &= ~BIT(18));
319
320 err = wrmsr_on_cpu(cpu, MSR_K7_HWCR, l, h);
321 if (err)
322 pr_err("%s: error writing HWCR\n", __func__);
323
324 return err;
325}
326
327static int __set_inj(const char *buf)
328{
329 int i;
330
331 for (i = 0; i < N_INJ_TYPES; i++) {
332 if (!strncmp(flags_options[i], buf, strlen(flags_options[i]))) {
333 inj_type = i;
334 return 0;
335 }
336 }
337 return -EINVAL;
338}
339
340static ssize_t flags_read(struct file *filp, char __user *ubuf,
341 size_t cnt, loff_t *ppos)
342{
343 char buf[MAX_FLAG_OPT_SIZE];
344 int n;
345
346 n = sprintf(buf, "%s\n", flags_options[inj_type]);
347
348 return simple_read_from_buffer(ubuf, cnt, ppos, buf, n);
349}
350
351static ssize_t flags_write(struct file *filp, const char __user *ubuf,
352 size_t cnt, loff_t *ppos)
353{
354 char buf[MAX_FLAG_OPT_SIZE], *__buf;
355 int err;
356
357 if (cnt > MAX_FLAG_OPT_SIZE)
358 return -EINVAL;
359
360 if (copy_from_user(&buf, ubuf, cnt))
361 return -EFAULT;
362
363 buf[cnt - 1] = 0;
364
365 /* strip whitespace */
366 __buf = strstrip(buf);
367
368 err = __set_inj(__buf);
369 if (err) {
370 pr_err("%s: Invalid flags value: %s\n", __func__, __buf);
371 return err;
372 }
373
374 *ppos += cnt;
375
376 return cnt;
377}
378
379static const struct file_operations flags_fops = {
380 .read = flags_read,
381 .write = flags_write,
382 .llseek = generic_file_llseek,
383};
384
385/*
386 * On which CPU to inject?
387 */
388MCE_INJECT_GET(extcpu);
389
390static int inj_extcpu_set(void *data, u64 val)
391{
392 struct mce *m = (struct mce *)data;
393
394 if (val >= nr_cpu_ids || !cpu_online(val)) {
395 pr_err("%s: Invalid CPU: %llu\n", __func__, val);
396 return -EINVAL;
397 }
398 m->extcpu = val;
399 return 0;
400}
401
402DEFINE_SIMPLE_ATTRIBUTE(extcpu_fops, inj_extcpu_get, inj_extcpu_set, "%llu\n");
403
404static void trigger_mce(void *info)
405{
406 asm volatile("int $18");
407}
408
409static void trigger_dfr_int(void *info)
410{
411 asm volatile("int %0" :: "i" (DEFERRED_ERROR_VECTOR));
412}
413
414static void trigger_thr_int(void *info)
415{
416 asm volatile("int %0" :: "i" (THRESHOLD_APIC_VECTOR));
417}
418
419static u32 get_nbc_for_node(int node_id)
420{
421 struct cpuinfo_x86 *c = &boot_cpu_data;
422 u32 cores_per_node;
423
424 cores_per_node = (c->x86_max_cores * smp_num_siblings) / amd_get_nodes_per_socket();
425
426 return cores_per_node * node_id;
427}
428
429static void toggle_nb_mca_mst_cpu(u16 nid)
430{
431 struct amd_northbridge *nb;
432 struct pci_dev *F3;
433 u32 val;
434 int err;
435
436 nb = node_to_amd_nb(nid);
437 if (!nb)
438 return;
439
440 F3 = nb->misc;
441 if (!F3)
442 return;
443
444 err = pci_read_config_dword(F3, NBCFG, &val);
445 if (err) {
446 pr_err("%s: Error reading F%dx%03x.\n",
447 __func__, PCI_FUNC(F3->devfn), NBCFG);
448 return;
449 }
450
451 if (val & BIT(27))
452 return;
453
454 pr_err("%s: Set D18F3x44[NbMcaToMstCpuEn] which BIOS hasn't done.\n",
455 __func__);
456
457 val |= BIT(27);
458 err = pci_write_config_dword(F3, NBCFG, val);
459 if (err)
460 pr_err("%s: Error writing F%dx%03x.\n",
461 __func__, PCI_FUNC(F3->devfn), NBCFG);
462}
ea149b36 463
bc8e80d5 464static void prepare_msrs(void *info)
ea149b36 465{
bc8e80d5
BP
466 struct mce m = *(struct mce *)info;
467 u8 b = m.bank;
468
469 wrmsrl(MSR_IA32_MCG_STATUS, m.mcgstatus);
470
471 if (boot_cpu_has(X86_FEATURE_SMCA)) {
472 if (m.inject_flags == DFR_INT_INJ) {
473 wrmsrl(MSR_AMD64_SMCA_MCx_DESTAT(b), m.status);
474 wrmsrl(MSR_AMD64_SMCA_MCx_DEADDR(b), m.addr);
475 } else {
476 wrmsrl(MSR_AMD64_SMCA_MCx_STATUS(b), m.status);
477 wrmsrl(MSR_AMD64_SMCA_MCx_ADDR(b), m.addr);
478 }
479
480 wrmsrl(MSR_AMD64_SMCA_MCx_MISC(b), m.misc);
481 wrmsrl(MSR_AMD64_SMCA_MCx_SYND(b), m.synd);
482 } else {
483 wrmsrl(MSR_IA32_MCx_STATUS(b), m.status);
484 wrmsrl(MSR_IA32_MCx_ADDR(b), m.addr);
485 wrmsrl(MSR_IA32_MCx_MISC(b), m.misc);
486 }
487}
488
489static void do_inject(void)
490{
491 u64 mcg_status = 0;
492 unsigned int cpu = i_mce.extcpu;
493 u8 b = i_mce.bank;
494
495 rdtscll(i_mce.tsc);
496
497 if (i_mce.misc)
498 i_mce.status |= MCI_STATUS_MISCV;
499
500 if (i_mce.synd)
501 i_mce.status |= MCI_STATUS_SYNDV;
502
503 if (inj_type == SW_INJ) {
504 mce_inject_log(&i_mce);
505 return;
506 }
507
508 /* prep MCE global settings for the injection */
509 mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV;
510
511 if (!(i_mce.status & MCI_STATUS_PCC))
512 mcg_status |= MCG_STATUS_RIPV;
513
514 /*
515 * Ensure necessary status bits for deferred errors:
516 * - MCx_STATUS[Deferred]: make sure it is a deferred error
517 * - MCx_STATUS[UC] cleared: deferred errors are _not_ UC
518 */
519 if (inj_type == DFR_INT_INJ) {
520 i_mce.status |= MCI_STATUS_DEFERRED;
521 i_mce.status |= (i_mce.status & ~MCI_STATUS_UC);
522 }
523
524 /*
525 * For multi node CPUs, logging and reporting of bank 4 errors happens
526 * only on the node base core. Refer to D18F3x44[NbMcaToMstCpuEn] for
527 * Fam10h and later BKDGs.
528 */
529 if (static_cpu_has(X86_FEATURE_AMD_DCM) &&
530 b == 4 &&
531 boot_cpu_data.x86 < 0x17) {
532 toggle_nb_mca_mst_cpu(amd_get_nb_id(cpu));
533 cpu = get_nbc_for_node(amd_get_nb_id(cpu));
534 }
535
536 get_online_cpus();
537 if (!cpu_online(cpu))
538 goto err;
539
540 toggle_hw_mce_inject(cpu, true);
541
542 i_mce.mcgstatus = mcg_status;
543 i_mce.inject_flags = inj_type;
544 smp_call_function_single(cpu, prepare_msrs, &i_mce, 0);
545
546 toggle_hw_mce_inject(cpu, false);
547
548 switch (inj_type) {
549 case DFR_INT_INJ:
550 smp_call_function_single(cpu, trigger_dfr_int, NULL, 0);
551 break;
552 case THR_INT_INJ:
553 smp_call_function_single(cpu, trigger_thr_int, NULL, 0);
554 break;
555 default:
556 smp_call_function_single(cpu, trigger_mce, NULL, 0);
557 }
558
559err:
560 put_online_cpus();
561
562}
563
564/*
565 * This denotes into which bank we're injecting and triggers
566 * the injection, at the same time.
567 */
568static int inj_bank_set(void *data, u64 val)
569{
570 struct mce *m = (struct mce *)data;
f54e97ca
YG
571 u8 n_banks;
572 u64 cap;
573
574 /* Get bank count on target CPU so we can handle non-uniform values. */
575 rdmsrl_on_cpu(m->extcpu, MSR_IA32_MCG_CAP, &cap);
576 n_banks = cap & MCG_BANKCNT_MASK;
bc8e80d5
BP
577
578 if (val >= n_banks) {
f54e97ca 579 pr_err("MCA bank %llu non-existent on CPU%d\n", val, m->extcpu);
bc8e80d5
BP
580 return -EINVAL;
581 }
582
583 m->bank = val;
584 do_inject();
585
2cc23299
BP
586 /* Reset injection struct */
587 setup_inj_struct(&i_mce);
588
bc8e80d5
BP
589 return 0;
590}
591
592MCE_INJECT_GET(bank);
593
594DEFINE_SIMPLE_ATTRIBUTE(bank_fops, inj_bank_get, inj_bank_set, "%llu\n");
595
596static const char readme_msg[] =
597"Description of the files and their usages:\n"
598"\n"
599"Note1: i refers to the bank number below.\n"
600"Note2: See respective BKDGs for the exact bit definitions of the files below\n"
601"as they mirror the hardware registers.\n"
602"\n"
603"status:\t Set MCi_STATUS: the bits in that MSR control the error type and\n"
604"\t attributes of the error which caused the MCE.\n"
605"\n"
606"misc:\t Set MCi_MISC: provide auxiliary info about the error. It is mostly\n"
607"\t used for error thresholding purposes and its validity is indicated by\n"
608"\t MCi_STATUS[MiscV].\n"
609"\n"
610"synd:\t Set MCi_SYND: provide syndrome info about the error. Only valid on\n"
611"\t Scalable MCA systems, and its validity is indicated by MCi_STATUS[SyndV].\n"
612"\n"
613"addr:\t Error address value to be written to MCi_ADDR. Log address information\n"
614"\t associated with the error.\n"
615"\n"
616"cpu:\t The CPU to inject the error on.\n"
617"\n"
618"bank:\t Specify the bank you want to inject the error into: the number of\n"
619"\t banks in a processor varies and is family/model-specific, therefore, the\n"
620"\t supplied value is sanity-checked. Setting the bank value also triggers the\n"
621"\t injection.\n"
622"\n"
623"flags:\t Injection type to be performed. Writing to this file will trigger a\n"
624"\t real machine check, an APIC interrupt or invoke the error decoder routines\n"
625"\t for AMD processors.\n"
626"\n"
627"\t Allowed error injection types:\n"
628"\t - \"sw\": Software error injection. Decode error to a human-readable \n"
629"\t format only. Safe to use.\n"
630"\t - \"hw\": Hardware error injection. Causes the #MC exception handler to \n"
631"\t handle the error. Be warned: might cause system panic if MCi_STATUS[PCC] \n"
632"\t is set. Therefore, consider setting (debugfs_mountpoint)/mce/fake_panic \n"
633"\t before injecting.\n"
634"\t - \"df\": Trigger APIC interrupt for Deferred error. Causes deferred \n"
635"\t error APIC interrupt handler to handle the error if the feature is \n"
636"\t is present in hardware. \n"
637"\t - \"th\": Trigger APIC interrupt for Threshold errors. Causes threshold \n"
638"\t APIC interrupt handler to handle the error. \n"
639"\n";
640
641static ssize_t
642inj_readme_read(struct file *filp, char __user *ubuf,
643 size_t cnt, loff_t *ppos)
644{
645 return simple_read_from_buffer(ubuf, cnt, ppos,
646 readme_msg, strlen(readme_msg));
647}
648
649static const struct file_operations readme_fops = {
650 .read = inj_readme_read,
651};
652
653static struct dfs_node {
654 char *name;
655 struct dentry *d;
656 const struct file_operations *fops;
657 umode_t perm;
658} dfs_fls[] = {
659 { .name = "status", .fops = &status_fops, .perm = S_IRUSR | S_IWUSR },
660 { .name = "misc", .fops = &misc_fops, .perm = S_IRUSR | S_IWUSR },
661 { .name = "addr", .fops = &addr_fops, .perm = S_IRUSR | S_IWUSR },
662 { .name = "synd", .fops = &synd_fops, .perm = S_IRUSR | S_IWUSR },
663 { .name = "bank", .fops = &bank_fops, .perm = S_IRUSR | S_IWUSR },
664 { .name = "flags", .fops = &flags_fops, .perm = S_IRUSR | S_IWUSR },
665 { .name = "cpu", .fops = &extcpu_fops, .perm = S_IRUSR | S_IWUSR },
666 { .name = "README", .fops = &readme_fops, .perm = S_IRUSR | S_IRGRP | S_IROTH },
667};
668
669static int __init debugfs_init(void)
670{
671 unsigned int i;
bc8e80d5
BP
672
673 dfs_inj = debugfs_create_dir("mce-inject", NULL);
674 if (!dfs_inj)
675 return -EINVAL;
676
677 for (i = 0; i < ARRAY_SIZE(dfs_fls); i++) {
678 dfs_fls[i].d = debugfs_create_file(dfs_fls[i].name,
679 dfs_fls[i].perm,
680 dfs_inj,
681 &i_mce,
682 dfs_fls[i].fops);
683
684 if (!dfs_fls[i].d)
685 goto err_dfs_add;
686 }
687
688 return 0;
689
690err_dfs_add:
691 while (i-- > 0)
692 debugfs_remove(dfs_fls[i].d);
693
694 debugfs_remove(dfs_inj);
695 dfs_inj = NULL;
696
697 return -ENODEV;
698}
699
700static int __init inject_init(void)
701{
702 int err;
703
6ac5c531
RR
704 if (!alloc_cpumask_var(&mce_inject_cpumask, GFP_KERNEL))
705 return -ENOMEM;
bc8e80d5 706
bc8e80d5
BP
707 err = debugfs_init();
708 if (err) {
709 free_cpumask_var(mce_inject_cpumask);
710 return err;
711 }
712
fbe9ff9e
BP
713 register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify");
714 mce_register_injector_chain(&inject_nb);
715
86d2eac5
BP
716 setup_inj_struct(&i_mce);
717
1b74dde7 718 pr_info("Machine check injector initialized\n");
bc8e80d5 719
ea149b36
AK
720 return 0;
721}
722
bc8e80d5
BP
723static void __exit inject_exit(void)
724{
725
fbe9ff9e
BP
726 mce_unregister_injector_chain(&inject_nb);
727 unregister_nmi_handler(NMI_LOCAL, "mce_notify");
728
bc8e80d5
BP
729 debugfs_remove_recursive(dfs_inj);
730 dfs_inj = NULL;
731
732 memset(&dfs_fls, 0, sizeof(dfs_fls));
733
bc8e80d5
BP
734 free_cpumask_var(mce_inject_cpumask);
735}
736
fbe9ff9e 737module_init(inject_init);
bc8e80d5 738module_exit(inject_exit);
ea149b36 739MODULE_LICENSE("GPL");