18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Machine check injection support. 48c2ecf20Sopenharmony_ci * Copyright 2008 Intel Corporation. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Authors: 78c2ecf20Sopenharmony_ci * Andi Kleen 88c2ecf20Sopenharmony_ci * Ying Huang 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * The AMD part (from mce_amd_inj.c): a simple MCE injection facility 118c2ecf20Sopenharmony_ci * for testing different aspects of the RAS code. This driver should be 128c2ecf20Sopenharmony_ci * built as module so that it can be loaded on production kernels for 138c2ecf20Sopenharmony_ci * testing purposes. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Copyright (c) 2010-17: Borislav Petkov <bp@alien8.de> 168c2ecf20Sopenharmony_ci * Advanced Micro Devices Inc. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/cpu.h> 208c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 218c2ecf20Sopenharmony_ci#include <linux/kernel.h> 228c2ecf20Sopenharmony_ci#include <linux/module.h> 238c2ecf20Sopenharmony_ci#include <linux/notifier.h> 248c2ecf20Sopenharmony_ci#include <linux/pci.h> 258c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <asm/amd_nb.h> 288c2ecf20Sopenharmony_ci#include <asm/apic.h> 298c2ecf20Sopenharmony_ci#include <asm/irq_vectors.h> 308c2ecf20Sopenharmony_ci#include <asm/mce.h> 318c2ecf20Sopenharmony_ci#include <asm/nmi.h> 328c2ecf20Sopenharmony_ci#include <asm/smp.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include "internal.h" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * Collect all the MCi_XXX settings 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_cistatic struct mce i_mce; 408c2ecf20Sopenharmony_cistatic struct dentry *dfs_inj; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define MAX_FLAG_OPT_SIZE 4 438c2ecf20Sopenharmony_ci#define NBCFG 0x44 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cienum injection_type { 468c2ecf20Sopenharmony_ci SW_INJ = 0, /* SW injection, simply decode the error */ 478c2ecf20Sopenharmony_ci HW_INJ, /* Trigger a #MC */ 488c2ecf20Sopenharmony_ci DFR_INT_INJ, /* Trigger Deferred error interrupt */ 498c2ecf20Sopenharmony_ci THR_INT_INJ, /* Trigger threshold interrupt */ 508c2ecf20Sopenharmony_ci N_INJ_TYPES, 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic const char * const flags_options[] = { 548c2ecf20Sopenharmony_ci [SW_INJ] = "sw", 558c2ecf20Sopenharmony_ci [HW_INJ] = "hw", 568c2ecf20Sopenharmony_ci [DFR_INT_INJ] = "df", 578c2ecf20Sopenharmony_ci [THR_INT_INJ] = "th", 588c2ecf20Sopenharmony_ci NULL 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* Set default injection to SW_INJ */ 628c2ecf20Sopenharmony_cistatic enum injection_type inj_type = SW_INJ; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define MCE_INJECT_SET(reg) \ 658c2ecf20Sopenharmony_cistatic int inj_##reg##_set(void *data, u64 val) \ 668c2ecf20Sopenharmony_ci{ \ 678c2ecf20Sopenharmony_ci struct mce *m = (struct mce *)data; \ 688c2ecf20Sopenharmony_ci \ 698c2ecf20Sopenharmony_ci m->reg = val; \ 708c2ecf20Sopenharmony_ci return 0; \ 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ciMCE_INJECT_SET(status); 748c2ecf20Sopenharmony_ciMCE_INJECT_SET(misc); 758c2ecf20Sopenharmony_ciMCE_INJECT_SET(addr); 768c2ecf20Sopenharmony_ciMCE_INJECT_SET(synd); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci#define MCE_INJECT_GET(reg) \ 798c2ecf20Sopenharmony_cistatic int inj_##reg##_get(void *data, u64 *val) \ 808c2ecf20Sopenharmony_ci{ \ 818c2ecf20Sopenharmony_ci struct mce *m = (struct mce *)data; \ 828c2ecf20Sopenharmony_ci \ 838c2ecf20Sopenharmony_ci *val = m->reg; \ 848c2ecf20Sopenharmony_ci return 0; \ 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ciMCE_INJECT_GET(status); 888c2ecf20Sopenharmony_ciMCE_INJECT_GET(misc); 898c2ecf20Sopenharmony_ciMCE_INJECT_GET(addr); 908c2ecf20Sopenharmony_ciMCE_INJECT_GET(synd); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(status_fops, inj_status_get, inj_status_set, "%llx\n"); 938c2ecf20Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(misc_fops, inj_misc_get, inj_misc_set, "%llx\n"); 948c2ecf20Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(addr_fops, inj_addr_get, inj_addr_set, "%llx\n"); 958c2ecf20Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(synd_fops, inj_synd_get, inj_synd_set, "%llx\n"); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic void setup_inj_struct(struct mce *m) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci memset(m, 0, sizeof(struct mce)); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci m->cpuvendor = boot_cpu_data.x86_vendor; 1028c2ecf20Sopenharmony_ci m->time = ktime_get_real_seconds(); 1038c2ecf20Sopenharmony_ci m->cpuid = cpuid_eax(1); 1048c2ecf20Sopenharmony_ci m->microcode = boot_cpu_data.microcode; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci/* Update fake mce registers on current CPU. */ 1088c2ecf20Sopenharmony_cistatic void inject_mce(struct mce *m) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct mce *i = &per_cpu(injectm, m->extcpu); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* Make sure no one reads partially written injectm */ 1138c2ecf20Sopenharmony_ci i->finished = 0; 1148c2ecf20Sopenharmony_ci mb(); 1158c2ecf20Sopenharmony_ci m->finished = 0; 1168c2ecf20Sopenharmony_ci /* First set the fields after finished */ 1178c2ecf20Sopenharmony_ci i->extcpu = m->extcpu; 1188c2ecf20Sopenharmony_ci mb(); 1198c2ecf20Sopenharmony_ci /* Now write record in order, finished last (except above) */ 1208c2ecf20Sopenharmony_ci memcpy(i, m, sizeof(struct mce)); 1218c2ecf20Sopenharmony_ci /* Finally activate it */ 1228c2ecf20Sopenharmony_ci mb(); 1238c2ecf20Sopenharmony_ci i->finished = 1; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic void raise_poll(struct mce *m) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci unsigned long flags; 1298c2ecf20Sopenharmony_ci mce_banks_t b; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci memset(&b, 0xff, sizeof(mce_banks_t)); 1328c2ecf20Sopenharmony_ci local_irq_save(flags); 1338c2ecf20Sopenharmony_ci machine_check_poll(0, &b); 1348c2ecf20Sopenharmony_ci local_irq_restore(flags); 1358c2ecf20Sopenharmony_ci m->finished = 0; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic void raise_exception(struct mce *m, struct pt_regs *pregs) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct pt_regs regs; 1418c2ecf20Sopenharmony_ci unsigned long flags; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (!pregs) { 1448c2ecf20Sopenharmony_ci memset(®s, 0, sizeof(struct pt_regs)); 1458c2ecf20Sopenharmony_ci regs.ip = m->ip; 1468c2ecf20Sopenharmony_ci regs.cs = m->cs; 1478c2ecf20Sopenharmony_ci pregs = ®s; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci /* do_machine_check() expects interrupts disabled -- at least */ 1508c2ecf20Sopenharmony_ci local_irq_save(flags); 1518c2ecf20Sopenharmony_ci do_machine_check(pregs); 1528c2ecf20Sopenharmony_ci local_irq_restore(flags); 1538c2ecf20Sopenharmony_ci m->finished = 0; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic cpumask_var_t mce_inject_cpumask; 1578c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(mce_inject_mutex); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic int mce_raise_notify(unsigned int cmd, struct pt_regs *regs) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci int cpu = smp_processor_id(); 1628c2ecf20Sopenharmony_ci struct mce *m = this_cpu_ptr(&injectm); 1638c2ecf20Sopenharmony_ci if (!cpumask_test_cpu(cpu, mce_inject_cpumask)) 1648c2ecf20Sopenharmony_ci return NMI_DONE; 1658c2ecf20Sopenharmony_ci cpumask_clear_cpu(cpu, mce_inject_cpumask); 1668c2ecf20Sopenharmony_ci if (m->inject_flags & MCJ_EXCEPTION) 1678c2ecf20Sopenharmony_ci raise_exception(m, regs); 1688c2ecf20Sopenharmony_ci else if (m->status) 1698c2ecf20Sopenharmony_ci raise_poll(m); 1708c2ecf20Sopenharmony_ci return NMI_HANDLED; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic void mce_irq_ipi(void *info) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci int cpu = smp_processor_id(); 1768c2ecf20Sopenharmony_ci struct mce *m = this_cpu_ptr(&injectm); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (cpumask_test_cpu(cpu, mce_inject_cpumask) && 1798c2ecf20Sopenharmony_ci m->inject_flags & MCJ_EXCEPTION) { 1808c2ecf20Sopenharmony_ci cpumask_clear_cpu(cpu, mce_inject_cpumask); 1818c2ecf20Sopenharmony_ci raise_exception(m, NULL); 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci/* Inject mce on current CPU */ 1868c2ecf20Sopenharmony_cistatic int raise_local(void) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct mce *m = this_cpu_ptr(&injectm); 1898c2ecf20Sopenharmony_ci int context = MCJ_CTX(m->inject_flags); 1908c2ecf20Sopenharmony_ci int ret = 0; 1918c2ecf20Sopenharmony_ci int cpu = m->extcpu; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (m->inject_flags & MCJ_EXCEPTION) { 1948c2ecf20Sopenharmony_ci pr_info("Triggering MCE exception on CPU %d\n", cpu); 1958c2ecf20Sopenharmony_ci switch (context) { 1968c2ecf20Sopenharmony_ci case MCJ_CTX_IRQ: 1978c2ecf20Sopenharmony_ci /* 1988c2ecf20Sopenharmony_ci * Could do more to fake interrupts like 1998c2ecf20Sopenharmony_ci * calling irq_enter, but the necessary 2008c2ecf20Sopenharmony_ci * machinery isn't exported currently. 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_ci fallthrough; 2038c2ecf20Sopenharmony_ci case MCJ_CTX_PROCESS: 2048c2ecf20Sopenharmony_ci raise_exception(m, NULL); 2058c2ecf20Sopenharmony_ci break; 2068c2ecf20Sopenharmony_ci default: 2078c2ecf20Sopenharmony_ci pr_info("Invalid MCE context\n"); 2088c2ecf20Sopenharmony_ci ret = -EINVAL; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci pr_info("MCE exception done on CPU %d\n", cpu); 2118c2ecf20Sopenharmony_ci } else if (m->status) { 2128c2ecf20Sopenharmony_ci pr_info("Starting machine check poll CPU %d\n", cpu); 2138c2ecf20Sopenharmony_ci raise_poll(m); 2148c2ecf20Sopenharmony_ci mce_notify_irq(); 2158c2ecf20Sopenharmony_ci pr_info("Machine check poll done on CPU %d\n", cpu); 2168c2ecf20Sopenharmony_ci } else 2178c2ecf20Sopenharmony_ci m->finished = 0; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return ret; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic void __maybe_unused raise_mce(struct mce *m) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci int context = MCJ_CTX(m->inject_flags); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci inject_mce(m); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (context == MCJ_CTX_RANDOM) 2298c2ecf20Sopenharmony_ci return; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (m->inject_flags & (MCJ_IRQ_BROADCAST | MCJ_NMI_BROADCAST)) { 2328c2ecf20Sopenharmony_ci unsigned long start; 2338c2ecf20Sopenharmony_ci int cpu; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci get_online_cpus(); 2368c2ecf20Sopenharmony_ci cpumask_copy(mce_inject_cpumask, cpu_online_mask); 2378c2ecf20Sopenharmony_ci cpumask_clear_cpu(get_cpu(), mce_inject_cpumask); 2388c2ecf20Sopenharmony_ci for_each_online_cpu(cpu) { 2398c2ecf20Sopenharmony_ci struct mce *mcpu = &per_cpu(injectm, cpu); 2408c2ecf20Sopenharmony_ci if (!mcpu->finished || 2418c2ecf20Sopenharmony_ci MCJ_CTX(mcpu->inject_flags) != MCJ_CTX_RANDOM) 2428c2ecf20Sopenharmony_ci cpumask_clear_cpu(cpu, mce_inject_cpumask); 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci if (!cpumask_empty(mce_inject_cpumask)) { 2458c2ecf20Sopenharmony_ci if (m->inject_flags & MCJ_IRQ_BROADCAST) { 2468c2ecf20Sopenharmony_ci /* 2478c2ecf20Sopenharmony_ci * don't wait because mce_irq_ipi is necessary 2488c2ecf20Sopenharmony_ci * to be sync with following raise_local 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_ci preempt_disable(); 2518c2ecf20Sopenharmony_ci smp_call_function_many(mce_inject_cpumask, 2528c2ecf20Sopenharmony_ci mce_irq_ipi, NULL, 0); 2538c2ecf20Sopenharmony_ci preempt_enable(); 2548c2ecf20Sopenharmony_ci } else if (m->inject_flags & MCJ_NMI_BROADCAST) 2558c2ecf20Sopenharmony_ci apic->send_IPI_mask(mce_inject_cpumask, 2568c2ecf20Sopenharmony_ci NMI_VECTOR); 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci start = jiffies; 2598c2ecf20Sopenharmony_ci while (!cpumask_empty(mce_inject_cpumask)) { 2608c2ecf20Sopenharmony_ci if (!time_before(jiffies, start + 2*HZ)) { 2618c2ecf20Sopenharmony_ci pr_err("Timeout waiting for mce inject %lx\n", 2628c2ecf20Sopenharmony_ci *cpumask_bits(mce_inject_cpumask)); 2638c2ecf20Sopenharmony_ci break; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci cpu_relax(); 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci raise_local(); 2688c2ecf20Sopenharmony_ci put_cpu(); 2698c2ecf20Sopenharmony_ci put_online_cpus(); 2708c2ecf20Sopenharmony_ci } else { 2718c2ecf20Sopenharmony_ci preempt_disable(); 2728c2ecf20Sopenharmony_ci raise_local(); 2738c2ecf20Sopenharmony_ci preempt_enable(); 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic int mce_inject_raise(struct notifier_block *nb, unsigned long val, 2788c2ecf20Sopenharmony_ci void *data) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct mce *m = (struct mce *)data; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (!m) 2838c2ecf20Sopenharmony_ci return NOTIFY_DONE; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci mutex_lock(&mce_inject_mutex); 2868c2ecf20Sopenharmony_ci raise_mce(m); 2878c2ecf20Sopenharmony_ci mutex_unlock(&mce_inject_mutex); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci return NOTIFY_DONE; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic struct notifier_block inject_nb = { 2938c2ecf20Sopenharmony_ci .notifier_call = mce_inject_raise, 2948c2ecf20Sopenharmony_ci}; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci/* 2978c2ecf20Sopenharmony_ci * Caller needs to be make sure this cpu doesn't disappear 2988c2ecf20Sopenharmony_ci * from under us, i.e.: get_cpu/put_cpu. 2998c2ecf20Sopenharmony_ci */ 3008c2ecf20Sopenharmony_cistatic int toggle_hw_mce_inject(unsigned int cpu, bool enable) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci u32 l, h; 3038c2ecf20Sopenharmony_ci int err; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci err = rdmsr_on_cpu(cpu, MSR_K7_HWCR, &l, &h); 3068c2ecf20Sopenharmony_ci if (err) { 3078c2ecf20Sopenharmony_ci pr_err("%s: error reading HWCR\n", __func__); 3088c2ecf20Sopenharmony_ci return err; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci enable ? (l |= BIT(18)) : (l &= ~BIT(18)); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci err = wrmsr_on_cpu(cpu, MSR_K7_HWCR, l, h); 3148c2ecf20Sopenharmony_ci if (err) 3158c2ecf20Sopenharmony_ci pr_err("%s: error writing HWCR\n", __func__); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return err; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic int __set_inj(const char *buf) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci int i; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci for (i = 0; i < N_INJ_TYPES; i++) { 3258c2ecf20Sopenharmony_ci if (!strncmp(flags_options[i], buf, strlen(flags_options[i]))) { 3268c2ecf20Sopenharmony_ci inj_type = i; 3278c2ecf20Sopenharmony_ci return 0; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci return -EINVAL; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic ssize_t flags_read(struct file *filp, char __user *ubuf, 3348c2ecf20Sopenharmony_ci size_t cnt, loff_t *ppos) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci char buf[MAX_FLAG_OPT_SIZE]; 3378c2ecf20Sopenharmony_ci int n; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci n = sprintf(buf, "%s\n", flags_options[inj_type]); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci return simple_read_from_buffer(ubuf, cnt, ppos, buf, n); 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic ssize_t flags_write(struct file *filp, const char __user *ubuf, 3458c2ecf20Sopenharmony_ci size_t cnt, loff_t *ppos) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci char buf[MAX_FLAG_OPT_SIZE], *__buf; 3488c2ecf20Sopenharmony_ci int err; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (!cnt || cnt > MAX_FLAG_OPT_SIZE) 3518c2ecf20Sopenharmony_ci return -EINVAL; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci if (copy_from_user(&buf, ubuf, cnt)) 3548c2ecf20Sopenharmony_ci return -EFAULT; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci buf[cnt - 1] = 0; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* strip whitespace */ 3598c2ecf20Sopenharmony_ci __buf = strstrip(buf); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci err = __set_inj(__buf); 3628c2ecf20Sopenharmony_ci if (err) { 3638c2ecf20Sopenharmony_ci pr_err("%s: Invalid flags value: %s\n", __func__, __buf); 3648c2ecf20Sopenharmony_ci return err; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci *ppos += cnt; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci return cnt; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic const struct file_operations flags_fops = { 3738c2ecf20Sopenharmony_ci .read = flags_read, 3748c2ecf20Sopenharmony_ci .write = flags_write, 3758c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 3768c2ecf20Sopenharmony_ci}; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci/* 3798c2ecf20Sopenharmony_ci * On which CPU to inject? 3808c2ecf20Sopenharmony_ci */ 3818c2ecf20Sopenharmony_ciMCE_INJECT_GET(extcpu); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic int inj_extcpu_set(void *data, u64 val) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci struct mce *m = (struct mce *)data; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (val >= nr_cpu_ids || !cpu_online(val)) { 3888c2ecf20Sopenharmony_ci pr_err("%s: Invalid CPU: %llu\n", __func__, val); 3898c2ecf20Sopenharmony_ci return -EINVAL; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci m->extcpu = val; 3928c2ecf20Sopenharmony_ci return 0; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(extcpu_fops, inj_extcpu_get, inj_extcpu_set, "%llu\n"); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic void trigger_mce(void *info) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci asm volatile("int $18"); 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic void trigger_dfr_int(void *info) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci asm volatile("int %0" :: "i" (DEFERRED_ERROR_VECTOR)); 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic void trigger_thr_int(void *info) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci asm volatile("int %0" :: "i" (THRESHOLD_APIC_VECTOR)); 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic u32 get_nbc_for_node(int node_id) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct cpuinfo_x86 *c = &boot_cpu_data; 4158c2ecf20Sopenharmony_ci u32 cores_per_node; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci cores_per_node = (c->x86_max_cores * smp_num_siblings) / amd_get_nodes_per_socket(); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci return cores_per_node * node_id; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic void toggle_nb_mca_mst_cpu(u16 nid) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci struct amd_northbridge *nb; 4258c2ecf20Sopenharmony_ci struct pci_dev *F3; 4268c2ecf20Sopenharmony_ci u32 val; 4278c2ecf20Sopenharmony_ci int err; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci nb = node_to_amd_nb(nid); 4308c2ecf20Sopenharmony_ci if (!nb) 4318c2ecf20Sopenharmony_ci return; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci F3 = nb->misc; 4348c2ecf20Sopenharmony_ci if (!F3) 4358c2ecf20Sopenharmony_ci return; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci err = pci_read_config_dword(F3, NBCFG, &val); 4388c2ecf20Sopenharmony_ci if (err) { 4398c2ecf20Sopenharmony_ci pr_err("%s: Error reading F%dx%03x.\n", 4408c2ecf20Sopenharmony_ci __func__, PCI_FUNC(F3->devfn), NBCFG); 4418c2ecf20Sopenharmony_ci return; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (val & BIT(27)) 4458c2ecf20Sopenharmony_ci return; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci pr_err("%s: Set D18F3x44[NbMcaToMstCpuEn] which BIOS hasn't done.\n", 4488c2ecf20Sopenharmony_ci __func__); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci val |= BIT(27); 4518c2ecf20Sopenharmony_ci err = pci_write_config_dword(F3, NBCFG, val); 4528c2ecf20Sopenharmony_ci if (err) 4538c2ecf20Sopenharmony_ci pr_err("%s: Error writing F%dx%03x.\n", 4548c2ecf20Sopenharmony_ci __func__, PCI_FUNC(F3->devfn), NBCFG); 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic void prepare_msrs(void *info) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci struct mce m = *(struct mce *)info; 4608c2ecf20Sopenharmony_ci u8 b = m.bank; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci wrmsrl(MSR_IA32_MCG_STATUS, m.mcgstatus); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (boot_cpu_has(X86_FEATURE_SMCA)) { 4658c2ecf20Sopenharmony_ci if (m.inject_flags == DFR_INT_INJ) { 4668c2ecf20Sopenharmony_ci wrmsrl(MSR_AMD64_SMCA_MCx_DESTAT(b), m.status); 4678c2ecf20Sopenharmony_ci wrmsrl(MSR_AMD64_SMCA_MCx_DEADDR(b), m.addr); 4688c2ecf20Sopenharmony_ci } else { 4698c2ecf20Sopenharmony_ci wrmsrl(MSR_AMD64_SMCA_MCx_STATUS(b), m.status); 4708c2ecf20Sopenharmony_ci wrmsrl(MSR_AMD64_SMCA_MCx_ADDR(b), m.addr); 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci wrmsrl(MSR_AMD64_SMCA_MCx_MISC(b), m.misc); 4748c2ecf20Sopenharmony_ci wrmsrl(MSR_AMD64_SMCA_MCx_SYND(b), m.synd); 4758c2ecf20Sopenharmony_ci } else { 4768c2ecf20Sopenharmony_ci wrmsrl(MSR_IA32_MCx_STATUS(b), m.status); 4778c2ecf20Sopenharmony_ci wrmsrl(MSR_IA32_MCx_ADDR(b), m.addr); 4788c2ecf20Sopenharmony_ci wrmsrl(MSR_IA32_MCx_MISC(b), m.misc); 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic void do_inject(void) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci u64 mcg_status = 0; 4858c2ecf20Sopenharmony_ci unsigned int cpu = i_mce.extcpu; 4868c2ecf20Sopenharmony_ci u8 b = i_mce.bank; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci i_mce.tsc = rdtsc_ordered(); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (i_mce.misc) 4918c2ecf20Sopenharmony_ci i_mce.status |= MCI_STATUS_MISCV; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if (i_mce.synd) 4948c2ecf20Sopenharmony_ci i_mce.status |= MCI_STATUS_SYNDV; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (inj_type == SW_INJ) { 4978c2ecf20Sopenharmony_ci mce_log(&i_mce); 4988c2ecf20Sopenharmony_ci return; 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* prep MCE global settings for the injection */ 5028c2ecf20Sopenharmony_ci mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (!(i_mce.status & MCI_STATUS_PCC)) 5058c2ecf20Sopenharmony_ci mcg_status |= MCG_STATUS_RIPV; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci /* 5088c2ecf20Sopenharmony_ci * Ensure necessary status bits for deferred errors: 5098c2ecf20Sopenharmony_ci * - MCx_STATUS[Deferred]: make sure it is a deferred error 5108c2ecf20Sopenharmony_ci * - MCx_STATUS[UC] cleared: deferred errors are _not_ UC 5118c2ecf20Sopenharmony_ci */ 5128c2ecf20Sopenharmony_ci if (inj_type == DFR_INT_INJ) { 5138c2ecf20Sopenharmony_ci i_mce.status |= MCI_STATUS_DEFERRED; 5148c2ecf20Sopenharmony_ci i_mce.status &= ~MCI_STATUS_UC; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci /* 5188c2ecf20Sopenharmony_ci * For multi node CPUs, logging and reporting of bank 4 errors happens 5198c2ecf20Sopenharmony_ci * only on the node base core. Refer to D18F3x44[NbMcaToMstCpuEn] for 5208c2ecf20Sopenharmony_ci * Fam10h and later BKDGs. 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_ci if (boot_cpu_has(X86_FEATURE_AMD_DCM) && 5238c2ecf20Sopenharmony_ci b == 4 && 5248c2ecf20Sopenharmony_ci boot_cpu_data.x86 < 0x17) { 5258c2ecf20Sopenharmony_ci toggle_nb_mca_mst_cpu(amd_get_nb_id(cpu)); 5268c2ecf20Sopenharmony_ci cpu = get_nbc_for_node(amd_get_nb_id(cpu)); 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci get_online_cpus(); 5308c2ecf20Sopenharmony_ci if (!cpu_online(cpu)) 5318c2ecf20Sopenharmony_ci goto err; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci toggle_hw_mce_inject(cpu, true); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci i_mce.mcgstatus = mcg_status; 5368c2ecf20Sopenharmony_ci i_mce.inject_flags = inj_type; 5378c2ecf20Sopenharmony_ci smp_call_function_single(cpu, prepare_msrs, &i_mce, 0); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci toggle_hw_mce_inject(cpu, false); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci switch (inj_type) { 5428c2ecf20Sopenharmony_ci case DFR_INT_INJ: 5438c2ecf20Sopenharmony_ci smp_call_function_single(cpu, trigger_dfr_int, NULL, 0); 5448c2ecf20Sopenharmony_ci break; 5458c2ecf20Sopenharmony_ci case THR_INT_INJ: 5468c2ecf20Sopenharmony_ci smp_call_function_single(cpu, trigger_thr_int, NULL, 0); 5478c2ecf20Sopenharmony_ci break; 5488c2ecf20Sopenharmony_ci default: 5498c2ecf20Sopenharmony_ci smp_call_function_single(cpu, trigger_mce, NULL, 0); 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cierr: 5538c2ecf20Sopenharmony_ci put_online_cpus(); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci/* 5588c2ecf20Sopenharmony_ci * This denotes into which bank we're injecting and triggers 5598c2ecf20Sopenharmony_ci * the injection, at the same time. 5608c2ecf20Sopenharmony_ci */ 5618c2ecf20Sopenharmony_cistatic int inj_bank_set(void *data, u64 val) 5628c2ecf20Sopenharmony_ci{ 5638c2ecf20Sopenharmony_ci struct mce *m = (struct mce *)data; 5648c2ecf20Sopenharmony_ci u8 n_banks; 5658c2ecf20Sopenharmony_ci u64 cap; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci /* Get bank count on target CPU so we can handle non-uniform values. */ 5688c2ecf20Sopenharmony_ci rdmsrl_on_cpu(m->extcpu, MSR_IA32_MCG_CAP, &cap); 5698c2ecf20Sopenharmony_ci n_banks = cap & MCG_BANKCNT_MASK; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci if (val >= n_banks) { 5728c2ecf20Sopenharmony_ci pr_err("MCA bank %llu non-existent on CPU%d\n", val, m->extcpu); 5738c2ecf20Sopenharmony_ci return -EINVAL; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci m->bank = val; 5778c2ecf20Sopenharmony_ci do_inject(); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci /* Reset injection struct */ 5808c2ecf20Sopenharmony_ci setup_inj_struct(&i_mce); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci return 0; 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ciMCE_INJECT_GET(bank); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(bank_fops, inj_bank_get, inj_bank_set, "%llu\n"); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cistatic const char readme_msg[] = 5908c2ecf20Sopenharmony_ci"Description of the files and their usages:\n" 5918c2ecf20Sopenharmony_ci"\n" 5928c2ecf20Sopenharmony_ci"Note1: i refers to the bank number below.\n" 5938c2ecf20Sopenharmony_ci"Note2: See respective BKDGs for the exact bit definitions of the files below\n" 5948c2ecf20Sopenharmony_ci"as they mirror the hardware registers.\n" 5958c2ecf20Sopenharmony_ci"\n" 5968c2ecf20Sopenharmony_ci"status:\t Set MCi_STATUS: the bits in that MSR control the error type and\n" 5978c2ecf20Sopenharmony_ci"\t attributes of the error which caused the MCE.\n" 5988c2ecf20Sopenharmony_ci"\n" 5998c2ecf20Sopenharmony_ci"misc:\t Set MCi_MISC: provide auxiliary info about the error. It is mostly\n" 6008c2ecf20Sopenharmony_ci"\t used for error thresholding purposes and its validity is indicated by\n" 6018c2ecf20Sopenharmony_ci"\t MCi_STATUS[MiscV].\n" 6028c2ecf20Sopenharmony_ci"\n" 6038c2ecf20Sopenharmony_ci"synd:\t Set MCi_SYND: provide syndrome info about the error. Only valid on\n" 6048c2ecf20Sopenharmony_ci"\t Scalable MCA systems, and its validity is indicated by MCi_STATUS[SyndV].\n" 6058c2ecf20Sopenharmony_ci"\n" 6068c2ecf20Sopenharmony_ci"addr:\t Error address value to be written to MCi_ADDR. Log address information\n" 6078c2ecf20Sopenharmony_ci"\t associated with the error.\n" 6088c2ecf20Sopenharmony_ci"\n" 6098c2ecf20Sopenharmony_ci"cpu:\t The CPU to inject the error on.\n" 6108c2ecf20Sopenharmony_ci"\n" 6118c2ecf20Sopenharmony_ci"bank:\t Specify the bank you want to inject the error into: the number of\n" 6128c2ecf20Sopenharmony_ci"\t banks in a processor varies and is family/model-specific, therefore, the\n" 6138c2ecf20Sopenharmony_ci"\t supplied value is sanity-checked. Setting the bank value also triggers the\n" 6148c2ecf20Sopenharmony_ci"\t injection.\n" 6158c2ecf20Sopenharmony_ci"\n" 6168c2ecf20Sopenharmony_ci"flags:\t Injection type to be performed. Writing to this file will trigger a\n" 6178c2ecf20Sopenharmony_ci"\t real machine check, an APIC interrupt or invoke the error decoder routines\n" 6188c2ecf20Sopenharmony_ci"\t for AMD processors.\n" 6198c2ecf20Sopenharmony_ci"\n" 6208c2ecf20Sopenharmony_ci"\t Allowed error injection types:\n" 6218c2ecf20Sopenharmony_ci"\t - \"sw\": Software error injection. Decode error to a human-readable \n" 6228c2ecf20Sopenharmony_ci"\t format only. Safe to use.\n" 6238c2ecf20Sopenharmony_ci"\t - \"hw\": Hardware error injection. Causes the #MC exception handler to \n" 6248c2ecf20Sopenharmony_ci"\t handle the error. Be warned: might cause system panic if MCi_STATUS[PCC] \n" 6258c2ecf20Sopenharmony_ci"\t is set. Therefore, consider setting (debugfs_mountpoint)/mce/fake_panic \n" 6268c2ecf20Sopenharmony_ci"\t before injecting.\n" 6278c2ecf20Sopenharmony_ci"\t - \"df\": Trigger APIC interrupt for Deferred error. Causes deferred \n" 6288c2ecf20Sopenharmony_ci"\t error APIC interrupt handler to handle the error if the feature is \n" 6298c2ecf20Sopenharmony_ci"\t is present in hardware. \n" 6308c2ecf20Sopenharmony_ci"\t - \"th\": Trigger APIC interrupt for Threshold errors. Causes threshold \n" 6318c2ecf20Sopenharmony_ci"\t APIC interrupt handler to handle the error. \n" 6328c2ecf20Sopenharmony_ci"\n"; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_cistatic ssize_t 6358c2ecf20Sopenharmony_ciinj_readme_read(struct file *filp, char __user *ubuf, 6368c2ecf20Sopenharmony_ci size_t cnt, loff_t *ppos) 6378c2ecf20Sopenharmony_ci{ 6388c2ecf20Sopenharmony_ci return simple_read_from_buffer(ubuf, cnt, ppos, 6398c2ecf20Sopenharmony_ci readme_msg, strlen(readme_msg)); 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_cistatic const struct file_operations readme_fops = { 6438c2ecf20Sopenharmony_ci .read = inj_readme_read, 6448c2ecf20Sopenharmony_ci}; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_cistatic struct dfs_node { 6478c2ecf20Sopenharmony_ci char *name; 6488c2ecf20Sopenharmony_ci const struct file_operations *fops; 6498c2ecf20Sopenharmony_ci umode_t perm; 6508c2ecf20Sopenharmony_ci} dfs_fls[] = { 6518c2ecf20Sopenharmony_ci { .name = "status", .fops = &status_fops, .perm = S_IRUSR | S_IWUSR }, 6528c2ecf20Sopenharmony_ci { .name = "misc", .fops = &misc_fops, .perm = S_IRUSR | S_IWUSR }, 6538c2ecf20Sopenharmony_ci { .name = "addr", .fops = &addr_fops, .perm = S_IRUSR | S_IWUSR }, 6548c2ecf20Sopenharmony_ci { .name = "synd", .fops = &synd_fops, .perm = S_IRUSR | S_IWUSR }, 6558c2ecf20Sopenharmony_ci { .name = "bank", .fops = &bank_fops, .perm = S_IRUSR | S_IWUSR }, 6568c2ecf20Sopenharmony_ci { .name = "flags", .fops = &flags_fops, .perm = S_IRUSR | S_IWUSR }, 6578c2ecf20Sopenharmony_ci { .name = "cpu", .fops = &extcpu_fops, .perm = S_IRUSR | S_IWUSR }, 6588c2ecf20Sopenharmony_ci { .name = "README", .fops = &readme_fops, .perm = S_IRUSR | S_IRGRP | S_IROTH }, 6598c2ecf20Sopenharmony_ci}; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_cistatic void __init debugfs_init(void) 6628c2ecf20Sopenharmony_ci{ 6638c2ecf20Sopenharmony_ci unsigned int i; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci dfs_inj = debugfs_create_dir("mce-inject", NULL); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dfs_fls); i++) 6688c2ecf20Sopenharmony_ci debugfs_create_file(dfs_fls[i].name, dfs_fls[i].perm, dfs_inj, 6698c2ecf20Sopenharmony_ci &i_mce, dfs_fls[i].fops); 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic int __init inject_init(void) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci if (!alloc_cpumask_var(&mce_inject_cpumask, GFP_KERNEL)) 6758c2ecf20Sopenharmony_ci return -ENOMEM; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci debugfs_init(); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify"); 6808c2ecf20Sopenharmony_ci mce_register_injector_chain(&inject_nb); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci setup_inj_struct(&i_mce); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci pr_info("Machine check injector initialized\n"); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci return 0; 6878c2ecf20Sopenharmony_ci} 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_cistatic void __exit inject_exit(void) 6908c2ecf20Sopenharmony_ci{ 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci mce_unregister_injector_chain(&inject_nb); 6938c2ecf20Sopenharmony_ci unregister_nmi_handler(NMI_LOCAL, "mce_notify"); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci debugfs_remove_recursive(dfs_inj); 6968c2ecf20Sopenharmony_ci dfs_inj = NULL; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci memset(&dfs_fls, 0, sizeof(dfs_fls)); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci free_cpumask_var(mce_inject_cpumask); 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_cimodule_init(inject_init); 7048c2ecf20Sopenharmony_cimodule_exit(inject_exit); 7058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 706