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 * 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. 24 */ 25 26 #include <linux/cpu.h> 27 #include <linux/debugfs.h> 28 #include <linux/kernel.h> 29 #include <linux/module.h> 30 #include <linux/notifier.h> 31 #include <linux/pci.h> 32 #include <linux/uaccess.h> 33 34 #include <asm/amd_nb.h> 35 #include <asm/apic.h> 36 #include <asm/irq_vectors.h> 37 #include <asm/mce.h> 38 #include <asm/nmi.h> 39 #include <asm/smp.h> 40 41 #include "internal.h" 42 43 /* 44 * Collect all the MCi_XXX settings 45 */ 46 static struct mce i_mce; 47 static struct dentry *dfs_inj; 48 49 #define MAX_FLAG_OPT_SIZE 4 50 #define NBCFG 0x44 51 52 enum 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 60 static 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 */ 69 static enum injection_type inj_type = SW_INJ; 70 71 #define MCE_INJECT_SET(reg) \ 72 static 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 80 MCE_INJECT_SET(status); 81 MCE_INJECT_SET(misc); 82 MCE_INJECT_SET(addr); 83 MCE_INJECT_SET(synd); 84 85 #define MCE_INJECT_GET(reg) \ 86 static 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 94 MCE_INJECT_GET(status); 95 MCE_INJECT_GET(misc); 96 MCE_INJECT_GET(addr); 97 MCE_INJECT_GET(synd); 98 99 DEFINE_SIMPLE_ATTRIBUTE(status_fops, inj_status_get, inj_status_set, "%llx\n"); 100 DEFINE_SIMPLE_ATTRIBUTE(misc_fops, inj_misc_get, inj_misc_set, "%llx\n"); 101 DEFINE_SIMPLE_ATTRIBUTE(addr_fops, inj_addr_get, inj_addr_set, "%llx\n"); 102 DEFINE_SIMPLE_ATTRIBUTE(synd_fops, inj_synd_get, inj_synd_set, "%llx\n"); 103 104 static void setup_inj_struct(struct mce *m) 105 { 106 memset(m, 0, sizeof(struct mce)); 107 108 m->cpuvendor = boot_cpu_data.x86_vendor; 109 m->time = ktime_get_real_seconds(); 110 m->cpuid = cpuid_eax(1); 111 m->microcode = boot_cpu_data.microcode; 112 } 113 114 /* Update fake mce registers on current CPU. */ 115 static void inject_mce(struct mce *m) 116 { 117 struct mce *i = &per_cpu(injectm, m->extcpu); 118 119 /* Make sure no one reads partially written injectm */ 120 i->finished = 0; 121 mb(); 122 m->finished = 0; 123 /* First set the fields after finished */ 124 i->extcpu = m->extcpu; 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 133 static void raise_poll(struct mce *m) 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 145 static void raise_exception(struct mce *m, struct pt_regs *pregs) 146 { 147 struct pt_regs regs; 148 unsigned long flags; 149 150 if (!pregs) { 151 memset(®s, 0, sizeof(struct pt_regs)); 152 regs.ip = m->ip; 153 regs.cs = m->cs; 154 pregs = ®s; 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 163 static cpumask_var_t mce_inject_cpumask; 164 static DEFINE_MUTEX(mce_inject_mutex); 165 166 static int mce_raise_notify(unsigned int cmd, struct pt_regs *regs) 167 { 168 int cpu = smp_processor_id(); 169 struct mce *m = this_cpu_ptr(&injectm); 170 if (!cpumask_test_cpu(cpu, mce_inject_cpumask)) 171 return NMI_DONE; 172 cpumask_clear_cpu(cpu, mce_inject_cpumask); 173 if (m->inject_flags & MCJ_EXCEPTION) 174 raise_exception(m, regs); 175 else if (m->status) 176 raise_poll(m); 177 return NMI_HANDLED; 178 } 179 180 static void mce_irq_ipi(void *info) 181 { 182 int cpu = smp_processor_id(); 183 struct mce *m = this_cpu_ptr(&injectm); 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 192 /* Inject mce on current CPU */ 193 static int raise_local(void) 194 { 195 struct mce *m = this_cpu_ptr(&injectm); 196 int context = MCJ_CTX(m->inject_flags); 197 int ret = 0; 198 int cpu = m->extcpu; 199 200 if (m->inject_flags & MCJ_EXCEPTION) { 201 pr_info("Triggering MCE exception on CPU %d\n", cpu); 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: 211 raise_exception(m, NULL); 212 break; 213 default: 214 pr_info("Invalid MCE context\n"); 215 ret = -EINVAL; 216 } 217 pr_info("MCE exception done on CPU %d\n", cpu); 218 } else if (m->status) { 219 pr_info("Starting machine check poll CPU %d\n", cpu); 220 raise_poll(m); 221 mce_notify_irq(); 222 pr_info("Machine check poll done on CPU %d\n", cpu); 223 } else 224 m->finished = 0; 225 226 return ret; 227 } 228 229 static void __maybe_unused raise_mce(struct mce *m) 230 { 231 int context = MCJ_CTX(m->inject_flags); 232 233 inject_mce(m); 234 235 if (context == MCJ_CTX_RANDOM) 236 return; 237 238 if (m->inject_flags & (MCJ_IRQ_BROADCAST | MCJ_NMI_BROADCAST)) { 239 unsigned long start; 240 int cpu; 241 242 get_online_cpus(); 243 cpumask_copy(mce_inject_cpumask, cpu_online_mask); 244 cpumask_clear_cpu(get_cpu(), mce_inject_cpumask); 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) 249 cpumask_clear_cpu(cpu, mce_inject_cpumask); 250 } 251 if (!cpumask_empty(mce_inject_cpumask)) { 252 if (m->inject_flags & MCJ_IRQ_BROADCAST) { 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 } 265 start = jiffies; 266 while (!cpumask_empty(mce_inject_cpumask)) { 267 if (!time_before(jiffies, start + 2*HZ)) { 268 pr_err("Timeout waiting for mce inject %lx\n", 269 *cpumask_bits(mce_inject_cpumask)); 270 break; 271 } 272 cpu_relax(); 273 } 274 raise_local(); 275 put_cpu(); 276 put_online_cpus(); 277 } else { 278 preempt_disable(); 279 raise_local(); 280 preempt_enable(); 281 } 282 } 283 284 static int mce_inject_raise(struct notifier_block *nb, unsigned long val, 285 void *data) 286 { 287 struct mce *m = (struct mce *)data; 288 289 if (!m) 290 return NOTIFY_DONE; 291 292 mutex_lock(&mce_inject_mutex); 293 raise_mce(m); 294 mutex_unlock(&mce_inject_mutex); 295 296 return NOTIFY_DONE; 297 } 298 299 static struct notifier_block inject_nb = { 300 .notifier_call = mce_inject_raise, 301 }; 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 */ 307 static 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 327 static 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 340 static 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 351 static 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 379 static 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 */ 388 MCE_INJECT_GET(extcpu); 389 390 static 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 402 DEFINE_SIMPLE_ATTRIBUTE(extcpu_fops, inj_extcpu_get, inj_extcpu_set, "%llu\n"); 403 404 static void trigger_mce(void *info) 405 { 406 asm volatile("int $18"); 407 } 408 409 static void trigger_dfr_int(void *info) 410 { 411 asm volatile("int %0" :: "i" (DEFERRED_ERROR_VECTOR)); 412 } 413 414 static void trigger_thr_int(void *info) 415 { 416 asm volatile("int %0" :: "i" (THRESHOLD_APIC_VECTOR)); 417 } 418 419 static 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 429 static 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 } 463 464 static void prepare_msrs(void *info) 465 { 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 489 static 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 i_mce.tsc = rdtsc_ordered(); 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 (boot_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 559 err: 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 */ 568 static int inj_bank_set(void *data, u64 val) 569 { 570 struct mce *m = (struct mce *)data; 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; 577 578 if (val >= n_banks) { 579 pr_err("MCA bank %llu non-existent on CPU%d\n", val, m->extcpu); 580 return -EINVAL; 581 } 582 583 m->bank = val; 584 do_inject(); 585 586 /* Reset injection struct */ 587 setup_inj_struct(&i_mce); 588 589 return 0; 590 } 591 592 MCE_INJECT_GET(bank); 593 594 DEFINE_SIMPLE_ATTRIBUTE(bank_fops, inj_bank_get, inj_bank_set, "%llu\n"); 595 596 static 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 641 static ssize_t 642 inj_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 649 static const struct file_operations readme_fops = { 650 .read = inj_readme_read, 651 }; 652 653 static 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 669 static int __init debugfs_init(void) 670 { 671 unsigned int i; 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 690 err_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 700 static int __init inject_init(void) 701 { 702 int err; 703 704 if (!alloc_cpumask_var(&mce_inject_cpumask, GFP_KERNEL)) 705 return -ENOMEM; 706 707 err = debugfs_init(); 708 if (err) { 709 free_cpumask_var(mce_inject_cpumask); 710 return err; 711 } 712 713 register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify"); 714 mce_register_injector_chain(&inject_nb); 715 716 setup_inj_struct(&i_mce); 717 718 pr_info("Machine check injector initialized\n"); 719 720 return 0; 721 } 722 723 static void __exit inject_exit(void) 724 { 725 726 mce_unregister_injector_chain(&inject_nb); 727 unregister_nmi_handler(NMI_LOCAL, "mce_notify"); 728 729 debugfs_remove_recursive(dfs_inj); 730 dfs_inj = NULL; 731 732 memset(&dfs_fls, 0, sizeof(dfs_fls)); 733 734 free_cpumask_var(mce_inject_cpumask); 735 } 736 737 module_init(inject_init); 738 module_exit(inject_exit); 739 MODULE_LICENSE("GPL"); 740