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