112237550SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
221afaf18SBorislav Petkov /*
321afaf18SBorislav Petkov * Machine check injection support.
421afaf18SBorislav Petkov * Copyright 2008 Intel Corporation.
521afaf18SBorislav Petkov *
621afaf18SBorislav Petkov * Authors:
721afaf18SBorislav Petkov * Andi Kleen
821afaf18SBorislav Petkov * Ying Huang
921afaf18SBorislav Petkov *
1021afaf18SBorislav Petkov * The AMD part (from mce_amd_inj.c): a simple MCE injection facility
1121afaf18SBorislav Petkov * for testing different aspects of the RAS code. This driver should be
1221afaf18SBorislav Petkov * built as module so that it can be loaded on production kernels for
1321afaf18SBorislav Petkov * testing purposes.
1421afaf18SBorislav Petkov *
1521afaf18SBorislav Petkov * Copyright (c) 2010-17: Borislav Petkov <bp@alien8.de>
1621afaf18SBorislav Petkov * Advanced Micro Devices Inc.
1721afaf18SBorislav Petkov */
1821afaf18SBorislav Petkov
1921afaf18SBorislav Petkov #include <linux/cpu.h>
2021afaf18SBorislav Petkov #include <linux/debugfs.h>
2121afaf18SBorislav Petkov #include <linux/kernel.h>
2221afaf18SBorislav Petkov #include <linux/module.h>
2321afaf18SBorislav Petkov #include <linux/notifier.h>
2421afaf18SBorislav Petkov #include <linux/pci.h>
2521afaf18SBorislav Petkov #include <linux/uaccess.h>
2621afaf18SBorislav Petkov
2721afaf18SBorislav Petkov #include <asm/amd_nb.h>
2821afaf18SBorislav Petkov #include <asm/apic.h>
2921afaf18SBorislav Petkov #include <asm/irq_vectors.h>
3021afaf18SBorislav Petkov #include <asm/mce.h>
3121afaf18SBorislav Petkov #include <asm/nmi.h>
3221afaf18SBorislav Petkov #include <asm/smp.h>
3321afaf18SBorislav Petkov
3421afaf18SBorislav Petkov #include "internal.h"
3521afaf18SBorislav Petkov
36891e465aSSmita Koralahalli static bool hw_injection_possible = true;
37891e465aSSmita Koralahalli
3821afaf18SBorislav Petkov /*
3921afaf18SBorislav Petkov * Collect all the MCi_XXX settings
4021afaf18SBorislav Petkov */
4121afaf18SBorislav Petkov static struct mce i_mce;
4221afaf18SBorislav Petkov static struct dentry *dfs_inj;
4321afaf18SBorislav Petkov
4421afaf18SBorislav Petkov #define MAX_FLAG_OPT_SIZE 4
4521afaf18SBorislav Petkov #define NBCFG 0x44
4621afaf18SBorislav Petkov
4721afaf18SBorislav Petkov enum injection_type {
4821afaf18SBorislav Petkov SW_INJ = 0, /* SW injection, simply decode the error */
4921afaf18SBorislav Petkov HW_INJ, /* Trigger a #MC */
5021afaf18SBorislav Petkov DFR_INT_INJ, /* Trigger Deferred error interrupt */
5121afaf18SBorislav Petkov THR_INT_INJ, /* Trigger threshold interrupt */
5221afaf18SBorislav Petkov N_INJ_TYPES,
5321afaf18SBorislav Petkov };
5421afaf18SBorislav Petkov
5521afaf18SBorislav Petkov static const char * const flags_options[] = {
5621afaf18SBorislav Petkov [SW_INJ] = "sw",
5721afaf18SBorislav Petkov [HW_INJ] = "hw",
5821afaf18SBorislav Petkov [DFR_INT_INJ] = "df",
5921afaf18SBorislav Petkov [THR_INT_INJ] = "th",
6021afaf18SBorislav Petkov NULL
6121afaf18SBorislav Petkov };
6221afaf18SBorislav Petkov
6321afaf18SBorislav Petkov /* Set default injection to SW_INJ */
6421afaf18SBorislav Petkov static enum injection_type inj_type = SW_INJ;
6521afaf18SBorislav Petkov
6621afaf18SBorislav Petkov #define MCE_INJECT_SET(reg) \
6721afaf18SBorislav Petkov static int inj_##reg##_set(void *data, u64 val) \
6821afaf18SBorislav Petkov { \
6921afaf18SBorislav Petkov struct mce *m = (struct mce *)data; \
7021afaf18SBorislav Petkov \
7121afaf18SBorislav Petkov m->reg = val; \
7221afaf18SBorislav Petkov return 0; \
7321afaf18SBorislav Petkov }
7421afaf18SBorislav Petkov
7521afaf18SBorislav Petkov MCE_INJECT_SET(status);
7621afaf18SBorislav Petkov MCE_INJECT_SET(misc);
7721afaf18SBorislav Petkov MCE_INJECT_SET(addr);
7821afaf18SBorislav Petkov MCE_INJECT_SET(synd);
7921afaf18SBorislav Petkov
8021afaf18SBorislav Petkov #define MCE_INJECT_GET(reg) \
8121afaf18SBorislav Petkov static int inj_##reg##_get(void *data, u64 *val) \
8221afaf18SBorislav Petkov { \
8321afaf18SBorislav Petkov struct mce *m = (struct mce *)data; \
8421afaf18SBorislav Petkov \
8521afaf18SBorislav Petkov *val = m->reg; \
8621afaf18SBorislav Petkov return 0; \
8721afaf18SBorislav Petkov }
8821afaf18SBorislav Petkov
8921afaf18SBorislav Petkov MCE_INJECT_GET(status);
9021afaf18SBorislav Petkov MCE_INJECT_GET(misc);
9121afaf18SBorislav Petkov MCE_INJECT_GET(addr);
9221afaf18SBorislav Petkov MCE_INJECT_GET(synd);
932ffdc2c3SBorislav Petkov MCE_INJECT_GET(ipid);
9421afaf18SBorislav Petkov
9521afaf18SBorislav Petkov DEFINE_SIMPLE_ATTRIBUTE(status_fops, inj_status_get, inj_status_set, "%llx\n");
9621afaf18SBorislav Petkov DEFINE_SIMPLE_ATTRIBUTE(misc_fops, inj_misc_get, inj_misc_set, "%llx\n");
9721afaf18SBorislav Petkov DEFINE_SIMPLE_ATTRIBUTE(addr_fops, inj_addr_get, inj_addr_set, "%llx\n");
9821afaf18SBorislav Petkov DEFINE_SIMPLE_ATTRIBUTE(synd_fops, inj_synd_get, inj_synd_set, "%llx\n");
99e48d008bSSmita Koralahalli
100e48d008bSSmita Koralahalli /* Use the user provided IPID value on a sw injection. */
inj_ipid_set(void * data,u64 val)101e48d008bSSmita Koralahalli static int inj_ipid_set(void *data, u64 val)
102e48d008bSSmita Koralahalli {
103e48d008bSSmita Koralahalli struct mce *m = (struct mce *)data;
104e48d008bSSmita Koralahalli
105e48d008bSSmita Koralahalli if (cpu_feature_enabled(X86_FEATURE_SMCA)) {
106e48d008bSSmita Koralahalli if (inj_type == SW_INJ)
107e48d008bSSmita Koralahalli m->ipid = val;
108e48d008bSSmita Koralahalli }
109e48d008bSSmita Koralahalli
110e48d008bSSmita Koralahalli return 0;
111e48d008bSSmita Koralahalli }
112e48d008bSSmita Koralahalli
1132ffdc2c3SBorislav Petkov DEFINE_SIMPLE_ATTRIBUTE(ipid_fops, inj_ipid_get, inj_ipid_set, "%llx\n");
11421afaf18SBorislav Petkov
setup_inj_struct(struct mce * m)11521afaf18SBorislav Petkov static void setup_inj_struct(struct mce *m)
11621afaf18SBorislav Petkov {
11721afaf18SBorislav Petkov memset(m, 0, sizeof(struct mce));
11821afaf18SBorislav Petkov
11921afaf18SBorislav Petkov m->cpuvendor = boot_cpu_data.x86_vendor;
12021afaf18SBorislav Petkov m->time = ktime_get_real_seconds();
12121afaf18SBorislav Petkov m->cpuid = cpuid_eax(1);
12221afaf18SBorislav Petkov m->microcode = boot_cpu_data.microcode;
12321afaf18SBorislav Petkov }
12421afaf18SBorislav Petkov
12521afaf18SBorislav Petkov /* Update fake mce registers on current CPU. */
inject_mce(struct mce * m)12621afaf18SBorislav Petkov static void inject_mce(struct mce *m)
12721afaf18SBorislav Petkov {
12821afaf18SBorislav Petkov struct mce *i = &per_cpu(injectm, m->extcpu);
12921afaf18SBorislav Petkov
13021afaf18SBorislav Petkov /* Make sure no one reads partially written injectm */
13121afaf18SBorislav Petkov i->finished = 0;
13221afaf18SBorislav Petkov mb();
13321afaf18SBorislav Petkov m->finished = 0;
13421afaf18SBorislav Petkov /* First set the fields after finished */
13521afaf18SBorislav Petkov i->extcpu = m->extcpu;
13621afaf18SBorislav Petkov mb();
13721afaf18SBorislav Petkov /* Now write record in order, finished last (except above) */
13821afaf18SBorislav Petkov memcpy(i, m, sizeof(struct mce));
13921afaf18SBorislav Petkov /* Finally activate it */
14021afaf18SBorislav Petkov mb();
14121afaf18SBorislav Petkov i->finished = 1;
14221afaf18SBorislav Petkov }
14321afaf18SBorislav Petkov
raise_poll(struct mce * m)14421afaf18SBorislav Petkov static void raise_poll(struct mce *m)
14521afaf18SBorislav Petkov {
14621afaf18SBorislav Petkov unsigned long flags;
14721afaf18SBorislav Petkov mce_banks_t b;
14821afaf18SBorislav Petkov
14921afaf18SBorislav Petkov memset(&b, 0xff, sizeof(mce_banks_t));
15021afaf18SBorislav Petkov local_irq_save(flags);
15121afaf18SBorislav Petkov machine_check_poll(0, &b);
15221afaf18SBorislav Petkov local_irq_restore(flags);
15321afaf18SBorislav Petkov m->finished = 0;
15421afaf18SBorislav Petkov }
15521afaf18SBorislav Petkov
raise_exception(struct mce * m,struct pt_regs * pregs)15621afaf18SBorislav Petkov static void raise_exception(struct mce *m, struct pt_regs *pregs)
15721afaf18SBorislav Petkov {
15821afaf18SBorislav Petkov struct pt_regs regs;
15921afaf18SBorislav Petkov unsigned long flags;
16021afaf18SBorislav Petkov
16121afaf18SBorislav Petkov if (!pregs) {
16221afaf18SBorislav Petkov memset(®s, 0, sizeof(struct pt_regs));
16321afaf18SBorislav Petkov regs.ip = m->ip;
16421afaf18SBorislav Petkov regs.cs = m->cs;
16521afaf18SBorislav Petkov pregs = ®s;
16621afaf18SBorislav Petkov }
1678cd501c1SThomas Gleixner /* do_machine_check() expects interrupts disabled -- at least */
16821afaf18SBorislav Petkov local_irq_save(flags);
1698cd501c1SThomas Gleixner do_machine_check(pregs);
17021afaf18SBorislav Petkov local_irq_restore(flags);
17121afaf18SBorislav Petkov m->finished = 0;
17221afaf18SBorislav Petkov }
17321afaf18SBorislav Petkov
17421afaf18SBorislav Petkov static cpumask_var_t mce_inject_cpumask;
17521afaf18SBorislav Petkov static DEFINE_MUTEX(mce_inject_mutex);
17621afaf18SBorislav Petkov
mce_raise_notify(unsigned int cmd,struct pt_regs * regs)17721afaf18SBorislav Petkov static int mce_raise_notify(unsigned int cmd, struct pt_regs *regs)
17821afaf18SBorislav Petkov {
17921afaf18SBorislav Petkov int cpu = smp_processor_id();
18021afaf18SBorislav Petkov struct mce *m = this_cpu_ptr(&injectm);
18121afaf18SBorislav Petkov if (!cpumask_test_cpu(cpu, mce_inject_cpumask))
18221afaf18SBorislav Petkov return NMI_DONE;
18321afaf18SBorislav Petkov cpumask_clear_cpu(cpu, mce_inject_cpumask);
18421afaf18SBorislav Petkov if (m->inject_flags & MCJ_EXCEPTION)
18521afaf18SBorislav Petkov raise_exception(m, regs);
18621afaf18SBorislav Petkov else if (m->status)
18721afaf18SBorislav Petkov raise_poll(m);
18821afaf18SBorislav Petkov return NMI_HANDLED;
18921afaf18SBorislav Petkov }
19021afaf18SBorislav Petkov
mce_irq_ipi(void * info)19121afaf18SBorislav Petkov static void mce_irq_ipi(void *info)
19221afaf18SBorislav Petkov {
19321afaf18SBorislav Petkov int cpu = smp_processor_id();
19421afaf18SBorislav Petkov struct mce *m = this_cpu_ptr(&injectm);
19521afaf18SBorislav Petkov
19621afaf18SBorislav Petkov if (cpumask_test_cpu(cpu, mce_inject_cpumask) &&
19721afaf18SBorislav Petkov m->inject_flags & MCJ_EXCEPTION) {
19821afaf18SBorislav Petkov cpumask_clear_cpu(cpu, mce_inject_cpumask);
19921afaf18SBorislav Petkov raise_exception(m, NULL);
20021afaf18SBorislav Petkov }
20121afaf18SBorislav Petkov }
20221afaf18SBorislav Petkov
20321afaf18SBorislav Petkov /* Inject mce on current CPU */
raise_local(void)20421afaf18SBorislav Petkov static int raise_local(void)
20521afaf18SBorislav Petkov {
20621afaf18SBorislav Petkov struct mce *m = this_cpu_ptr(&injectm);
20721afaf18SBorislav Petkov int context = MCJ_CTX(m->inject_flags);
20821afaf18SBorislav Petkov int ret = 0;
20921afaf18SBorislav Petkov int cpu = m->extcpu;
21021afaf18SBorislav Petkov
21121afaf18SBorislav Petkov if (m->inject_flags & MCJ_EXCEPTION) {
21221afaf18SBorislav Petkov pr_info("Triggering MCE exception on CPU %d\n", cpu);
21321afaf18SBorislav Petkov switch (context) {
21421afaf18SBorislav Petkov case MCJ_CTX_IRQ:
21521afaf18SBorislav Petkov /*
21621afaf18SBorislav Petkov * Could do more to fake interrupts like
21721afaf18SBorislav Petkov * calling irq_enter, but the necessary
21821afaf18SBorislav Petkov * machinery isn't exported currently.
21921afaf18SBorislav Petkov */
220df561f66SGustavo A. R. Silva fallthrough;
22121afaf18SBorislav Petkov case MCJ_CTX_PROCESS:
22221afaf18SBorislav Petkov raise_exception(m, NULL);
22321afaf18SBorislav Petkov break;
22421afaf18SBorislav Petkov default:
22521afaf18SBorislav Petkov pr_info("Invalid MCE context\n");
22621afaf18SBorislav Petkov ret = -EINVAL;
22721afaf18SBorislav Petkov }
22821afaf18SBorislav Petkov pr_info("MCE exception done on CPU %d\n", cpu);
22921afaf18SBorislav Petkov } else if (m->status) {
23021afaf18SBorislav Petkov pr_info("Starting machine check poll CPU %d\n", cpu);
23121afaf18SBorislav Petkov raise_poll(m);
23221afaf18SBorislav Petkov mce_notify_irq();
23321afaf18SBorislav Petkov pr_info("Machine check poll done on CPU %d\n", cpu);
23421afaf18SBorislav Petkov } else
23521afaf18SBorislav Petkov m->finished = 0;
23621afaf18SBorislav Petkov
23721afaf18SBorislav Petkov return ret;
23821afaf18SBorislav Petkov }
23921afaf18SBorislav Petkov
raise_mce(struct mce * m)24021afaf18SBorislav Petkov static void __maybe_unused raise_mce(struct mce *m)
24121afaf18SBorislav Petkov {
24221afaf18SBorislav Petkov int context = MCJ_CTX(m->inject_flags);
24321afaf18SBorislav Petkov
24421afaf18SBorislav Petkov inject_mce(m);
24521afaf18SBorislav Petkov
24621afaf18SBorislav Petkov if (context == MCJ_CTX_RANDOM)
24721afaf18SBorislav Petkov return;
24821afaf18SBorislav Petkov
24921afaf18SBorislav Petkov if (m->inject_flags & (MCJ_IRQ_BROADCAST | MCJ_NMI_BROADCAST)) {
25021afaf18SBorislav Petkov unsigned long start;
25121afaf18SBorislav Petkov int cpu;
25221afaf18SBorislav Petkov
2538ae9e3f6SSebastian Andrzej Siewior cpus_read_lock();
25421afaf18SBorislav Petkov cpumask_copy(mce_inject_cpumask, cpu_online_mask);
25521afaf18SBorislav Petkov cpumask_clear_cpu(get_cpu(), mce_inject_cpumask);
25621afaf18SBorislav Petkov for_each_online_cpu(cpu) {
25721afaf18SBorislav Petkov struct mce *mcpu = &per_cpu(injectm, cpu);
25821afaf18SBorislav Petkov if (!mcpu->finished ||
25921afaf18SBorislav Petkov MCJ_CTX(mcpu->inject_flags) != MCJ_CTX_RANDOM)
26021afaf18SBorislav Petkov cpumask_clear_cpu(cpu, mce_inject_cpumask);
26121afaf18SBorislav Petkov }
26221afaf18SBorislav Petkov if (!cpumask_empty(mce_inject_cpumask)) {
26321afaf18SBorislav Petkov if (m->inject_flags & MCJ_IRQ_BROADCAST) {
26421afaf18SBorislav Petkov /*
26521afaf18SBorislav Petkov * don't wait because mce_irq_ipi is necessary
26621afaf18SBorislav Petkov * to be sync with following raise_local
26721afaf18SBorislav Petkov */
26821afaf18SBorislav Petkov preempt_disable();
26921afaf18SBorislav Petkov smp_call_function_many(mce_inject_cpumask,
27021afaf18SBorislav Petkov mce_irq_ipi, NULL, 0);
27121afaf18SBorislav Petkov preempt_enable();
27221afaf18SBorislav Petkov } else if (m->inject_flags & MCJ_NMI_BROADCAST)
27328b82352SDave Hansen __apic_send_IPI_mask(mce_inject_cpumask, NMI_VECTOR);
27421afaf18SBorislav Petkov }
27521afaf18SBorislav Petkov start = jiffies;
27621afaf18SBorislav Petkov while (!cpumask_empty(mce_inject_cpumask)) {
27721afaf18SBorislav Petkov if (!time_before(jiffies, start + 2*HZ)) {
27821afaf18SBorislav Petkov pr_err("Timeout waiting for mce inject %lx\n",
27921afaf18SBorislav Petkov *cpumask_bits(mce_inject_cpumask));
28021afaf18SBorislav Petkov break;
28121afaf18SBorislav Petkov }
28221afaf18SBorislav Petkov cpu_relax();
28321afaf18SBorislav Petkov }
28421afaf18SBorislav Petkov raise_local();
28521afaf18SBorislav Petkov put_cpu();
2868ae9e3f6SSebastian Andrzej Siewior cpus_read_unlock();
28721afaf18SBorislav Petkov } else {
28821afaf18SBorislav Petkov preempt_disable();
28921afaf18SBorislav Petkov raise_local();
29021afaf18SBorislav Petkov preempt_enable();
29121afaf18SBorislav Petkov }
29221afaf18SBorislav Petkov }
29321afaf18SBorislav Petkov
mce_inject_raise(struct notifier_block * nb,unsigned long val,void * data)29421afaf18SBorislav Petkov static int mce_inject_raise(struct notifier_block *nb, unsigned long val,
29521afaf18SBorislav Petkov void *data)
29621afaf18SBorislav Petkov {
29721afaf18SBorislav Petkov struct mce *m = (struct mce *)data;
29821afaf18SBorislav Petkov
29921afaf18SBorislav Petkov if (!m)
30021afaf18SBorislav Petkov return NOTIFY_DONE;
30121afaf18SBorislav Petkov
30221afaf18SBorislav Petkov mutex_lock(&mce_inject_mutex);
30321afaf18SBorislav Petkov raise_mce(m);
30421afaf18SBorislav Petkov mutex_unlock(&mce_inject_mutex);
30521afaf18SBorislav Petkov
30621afaf18SBorislav Petkov return NOTIFY_DONE;
30721afaf18SBorislav Petkov }
30821afaf18SBorislav Petkov
30921afaf18SBorislav Petkov static struct notifier_block inject_nb = {
31021afaf18SBorislav Petkov .notifier_call = mce_inject_raise,
31121afaf18SBorislav Petkov };
31221afaf18SBorislav Petkov
31321afaf18SBorislav Petkov /*
31421afaf18SBorislav Petkov * Caller needs to be make sure this cpu doesn't disappear
31521afaf18SBorislav Petkov * from under us, i.e.: get_cpu/put_cpu.
31621afaf18SBorislav Petkov */
toggle_hw_mce_inject(unsigned int cpu,bool enable)31721afaf18SBorislav Petkov static int toggle_hw_mce_inject(unsigned int cpu, bool enable)
31821afaf18SBorislav Petkov {
31921afaf18SBorislav Petkov u32 l, h;
32021afaf18SBorislav Petkov int err;
32121afaf18SBorislav Petkov
32221afaf18SBorislav Petkov err = rdmsr_on_cpu(cpu, MSR_K7_HWCR, &l, &h);
32321afaf18SBorislav Petkov if (err) {
32421afaf18SBorislav Petkov pr_err("%s: error reading HWCR\n", __func__);
32521afaf18SBorislav Petkov return err;
32621afaf18SBorislav Petkov }
32721afaf18SBorislav Petkov
32821afaf18SBorislav Petkov enable ? (l |= BIT(18)) : (l &= ~BIT(18));
32921afaf18SBorislav Petkov
33021afaf18SBorislav Petkov err = wrmsr_on_cpu(cpu, MSR_K7_HWCR, l, h);
33121afaf18SBorislav Petkov if (err)
33221afaf18SBorislav Petkov pr_err("%s: error writing HWCR\n", __func__);
33321afaf18SBorislav Petkov
33421afaf18SBorislav Petkov return err;
33521afaf18SBorislav Petkov }
33621afaf18SBorislav Petkov
__set_inj(const char * buf)33721afaf18SBorislav Petkov static int __set_inj(const char *buf)
33821afaf18SBorislav Petkov {
33921afaf18SBorislav Petkov int i;
34021afaf18SBorislav Petkov
34121afaf18SBorislav Petkov for (i = 0; i < N_INJ_TYPES; i++) {
34221afaf18SBorislav Petkov if (!strncmp(flags_options[i], buf, strlen(flags_options[i]))) {
343891e465aSSmita Koralahalli if (i > SW_INJ && !hw_injection_possible)
344891e465aSSmita Koralahalli continue;
34521afaf18SBorislav Petkov inj_type = i;
34621afaf18SBorislav Petkov return 0;
34721afaf18SBorislav Petkov }
34821afaf18SBorislav Petkov }
34921afaf18SBorislav Petkov return -EINVAL;
35021afaf18SBorislav Petkov }
35121afaf18SBorislav Petkov
flags_read(struct file * filp,char __user * ubuf,size_t cnt,loff_t * ppos)35221afaf18SBorislav Petkov static ssize_t flags_read(struct file *filp, char __user *ubuf,
35321afaf18SBorislav Petkov size_t cnt, loff_t *ppos)
35421afaf18SBorislav Petkov {
35521afaf18SBorislav Petkov char buf[MAX_FLAG_OPT_SIZE];
35621afaf18SBorislav Petkov int n;
35721afaf18SBorislav Petkov
35821afaf18SBorislav Petkov n = sprintf(buf, "%s\n", flags_options[inj_type]);
35921afaf18SBorislav Petkov
36021afaf18SBorislav Petkov return simple_read_from_buffer(ubuf, cnt, ppos, buf, n);
36121afaf18SBorislav Petkov }
36221afaf18SBorislav Petkov
flags_write(struct file * filp,const char __user * ubuf,size_t cnt,loff_t * ppos)36321afaf18SBorislav Petkov static ssize_t flags_write(struct file *filp, const char __user *ubuf,
36421afaf18SBorislav Petkov size_t cnt, loff_t *ppos)
36521afaf18SBorislav Petkov {
36621afaf18SBorislav Petkov char buf[MAX_FLAG_OPT_SIZE], *__buf;
36721afaf18SBorislav Petkov int err;
36821afaf18SBorislav Petkov
369de768416SZhang Zixun if (!cnt || cnt > MAX_FLAG_OPT_SIZE)
37021afaf18SBorislav Petkov return -EINVAL;
37121afaf18SBorislav Petkov
37221afaf18SBorislav Petkov if (copy_from_user(&buf, ubuf, cnt))
37321afaf18SBorislav Petkov return -EFAULT;
37421afaf18SBorislav Petkov
37521afaf18SBorislav Petkov buf[cnt - 1] = 0;
37621afaf18SBorislav Petkov
37721afaf18SBorislav Petkov /* strip whitespace */
37821afaf18SBorislav Petkov __buf = strstrip(buf);
37921afaf18SBorislav Petkov
38021afaf18SBorislav Petkov err = __set_inj(__buf);
38121afaf18SBorislav Petkov if (err) {
38221afaf18SBorislav Petkov pr_err("%s: Invalid flags value: %s\n", __func__, __buf);
38321afaf18SBorislav Petkov return err;
38421afaf18SBorislav Petkov }
38521afaf18SBorislav Petkov
38621afaf18SBorislav Petkov *ppos += cnt;
38721afaf18SBorislav Petkov
38821afaf18SBorislav Petkov return cnt;
38921afaf18SBorislav Petkov }
39021afaf18SBorislav Petkov
39121afaf18SBorislav Petkov static const struct file_operations flags_fops = {
39221afaf18SBorislav Petkov .read = flags_read,
39321afaf18SBorislav Petkov .write = flags_write,
39421afaf18SBorislav Petkov .llseek = generic_file_llseek,
39521afaf18SBorislav Petkov };
39621afaf18SBorislav Petkov
39721afaf18SBorislav Petkov /*
39821afaf18SBorislav Petkov * On which CPU to inject?
39921afaf18SBorislav Petkov */
40021afaf18SBorislav Petkov MCE_INJECT_GET(extcpu);
40121afaf18SBorislav Petkov
inj_extcpu_set(void * data,u64 val)40221afaf18SBorislav Petkov static int inj_extcpu_set(void *data, u64 val)
40321afaf18SBorislav Petkov {
40421afaf18SBorislav Petkov struct mce *m = (struct mce *)data;
40521afaf18SBorislav Petkov
40621afaf18SBorislav Petkov if (val >= nr_cpu_ids || !cpu_online(val)) {
40721afaf18SBorislav Petkov pr_err("%s: Invalid CPU: %llu\n", __func__, val);
40821afaf18SBorislav Petkov return -EINVAL;
40921afaf18SBorislav Petkov }
41021afaf18SBorislav Petkov m->extcpu = val;
41121afaf18SBorislav Petkov return 0;
41221afaf18SBorislav Petkov }
41321afaf18SBorislav Petkov
41421afaf18SBorislav Petkov DEFINE_SIMPLE_ATTRIBUTE(extcpu_fops, inj_extcpu_get, inj_extcpu_set, "%llu\n");
41521afaf18SBorislav Petkov
trigger_mce(void * info)41621afaf18SBorislav Petkov static void trigger_mce(void *info)
41721afaf18SBorislav Petkov {
41821afaf18SBorislav Petkov asm volatile("int $18");
41921afaf18SBorislav Petkov }
42021afaf18SBorislav Petkov
trigger_dfr_int(void * info)42121afaf18SBorislav Petkov static void trigger_dfr_int(void *info)
42221afaf18SBorislav Petkov {
42321afaf18SBorislav Petkov asm volatile("int %0" :: "i" (DEFERRED_ERROR_VECTOR));
42421afaf18SBorislav Petkov }
42521afaf18SBorislav Petkov
trigger_thr_int(void * info)42621afaf18SBorislav Petkov static void trigger_thr_int(void *info)
42721afaf18SBorislav Petkov {
42821afaf18SBorislav Petkov asm volatile("int %0" :: "i" (THRESHOLD_APIC_VECTOR));
42921afaf18SBorislav Petkov }
43021afaf18SBorislav Petkov
get_nbc_for_node(int node_id)43121afaf18SBorislav Petkov static u32 get_nbc_for_node(int node_id)
43221afaf18SBorislav Petkov {
43321afaf18SBorislav Petkov u32 cores_per_node;
43421afaf18SBorislav Petkov
43589b0f15fSThomas Gleixner cores_per_node = topology_num_threads_per_package() / topology_amd_nodes_per_pkg();
43621afaf18SBorislav Petkov return cores_per_node * node_id;
43721afaf18SBorislav Petkov }
43821afaf18SBorislav Petkov
toggle_nb_mca_mst_cpu(u16 nid)43921afaf18SBorislav Petkov static void toggle_nb_mca_mst_cpu(u16 nid)
44021afaf18SBorislav Petkov {
44121afaf18SBorislav Petkov struct amd_northbridge *nb;
44221afaf18SBorislav Petkov struct pci_dev *F3;
44321afaf18SBorislav Petkov u32 val;
44421afaf18SBorislav Petkov int err;
44521afaf18SBorislav Petkov
44621afaf18SBorislav Petkov nb = node_to_amd_nb(nid);
44721afaf18SBorislav Petkov if (!nb)
44821afaf18SBorislav Petkov return;
44921afaf18SBorislav Petkov
45021afaf18SBorislav Petkov F3 = nb->misc;
45121afaf18SBorislav Petkov if (!F3)
45221afaf18SBorislav Petkov return;
45321afaf18SBorislav Petkov
45421afaf18SBorislav Petkov err = pci_read_config_dword(F3, NBCFG, &val);
45521afaf18SBorislav Petkov if (err) {
45621afaf18SBorislav Petkov pr_err("%s: Error reading F%dx%03x.\n",
45721afaf18SBorislav Petkov __func__, PCI_FUNC(F3->devfn), NBCFG);
45821afaf18SBorislav Petkov return;
45921afaf18SBorislav Petkov }
46021afaf18SBorislav Petkov
46121afaf18SBorislav Petkov if (val & BIT(27))
46221afaf18SBorislav Petkov return;
46321afaf18SBorislav Petkov
46421afaf18SBorislav Petkov pr_err("%s: Set D18F3x44[NbMcaToMstCpuEn] which BIOS hasn't done.\n",
46521afaf18SBorislav Petkov __func__);
46621afaf18SBorislav Petkov
46721afaf18SBorislav Petkov val |= BIT(27);
46821afaf18SBorislav Petkov err = pci_write_config_dword(F3, NBCFG, val);
46921afaf18SBorislav Petkov if (err)
47021afaf18SBorislav Petkov pr_err("%s: Error writing F%dx%03x.\n",
47121afaf18SBorislav Petkov __func__, PCI_FUNC(F3->devfn), NBCFG);
47221afaf18SBorislav Petkov }
47321afaf18SBorislav Petkov
prepare_msrs(void * info)47421afaf18SBorislav Petkov static void prepare_msrs(void *info)
47521afaf18SBorislav Petkov {
47621afaf18SBorislav Petkov struct mce m = *(struct mce *)info;
47721afaf18SBorislav Petkov u8 b = m.bank;
47821afaf18SBorislav Petkov
47921afaf18SBorislav Petkov wrmsrl(MSR_IA32_MCG_STATUS, m.mcgstatus);
48021afaf18SBorislav Petkov
48121afaf18SBorislav Petkov if (boot_cpu_has(X86_FEATURE_SMCA)) {
48221afaf18SBorislav Petkov if (m.inject_flags == DFR_INT_INJ) {
48321afaf18SBorislav Petkov wrmsrl(MSR_AMD64_SMCA_MCx_DESTAT(b), m.status);
48421afaf18SBorislav Petkov wrmsrl(MSR_AMD64_SMCA_MCx_DEADDR(b), m.addr);
48521afaf18SBorislav Petkov } else {
48621afaf18SBorislav Petkov wrmsrl(MSR_AMD64_SMCA_MCx_STATUS(b), m.status);
48721afaf18SBorislav Petkov wrmsrl(MSR_AMD64_SMCA_MCx_ADDR(b), m.addr);
48821afaf18SBorislav Petkov }
48921afaf18SBorislav Petkov
49021afaf18SBorislav Petkov wrmsrl(MSR_AMD64_SMCA_MCx_SYND(b), m.synd);
49121afaf18SBorislav Petkov
49221afaf18SBorislav Petkov if (m.misc)
49321afaf18SBorislav Petkov wrmsrl(MSR_AMD64_SMCA_MCx_MISC(b), m.misc);
49421afaf18SBorislav Petkov } else {
49521afaf18SBorislav Petkov wrmsrl(MSR_IA32_MCx_STATUS(b), m.status);
49621afaf18SBorislav Petkov wrmsrl(MSR_IA32_MCx_ADDR(b), m.addr);
49721afaf18SBorislav Petkov
49821afaf18SBorislav Petkov if (m.misc)
49921afaf18SBorislav Petkov wrmsrl(MSR_IA32_MCx_MISC(b), m.misc);
50021afaf18SBorislav Petkov }
50121afaf18SBorislav Petkov }
50221afaf18SBorislav Petkov
do_inject(void)50321afaf18SBorislav Petkov static void do_inject(void)
50421afaf18SBorislav Petkov {
50521afaf18SBorislav Petkov u64 mcg_status = 0;
50621afaf18SBorislav Petkov unsigned int cpu = i_mce.extcpu;
5071e56279aSSmita Koralahalli u8 b = i_mce.bank;
5081e56279aSSmita Koralahalli
50921afaf18SBorislav Petkov i_mce.tsc = rdtsc_ordered();
51021afaf18SBorislav Petkov
51121afaf18SBorislav Petkov i_mce.status |= MCI_STATUS_VAL;
51221afaf18SBorislav Petkov
51321afaf18SBorislav Petkov if (i_mce.misc)
51421afaf18SBorislav Petkov i_mce.status |= MCI_STATUS_MISCV;
51521afaf18SBorislav Petkov
51681736abdSJan H. Schönherr if (i_mce.synd)
51721afaf18SBorislav Petkov i_mce.status |= MCI_STATUS_SYNDV;
51821afaf18SBorislav Petkov
51921afaf18SBorislav Petkov if (inj_type == SW_INJ) {
52021afaf18SBorislav Petkov mce_log(&i_mce);
52121afaf18SBorislav Petkov return;
52221afaf18SBorislav Petkov }
52321afaf18SBorislav Petkov
52421afaf18SBorislav Petkov /* prep MCE global settings for the injection */
52521afaf18SBorislav Petkov mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV;
52621afaf18SBorislav Petkov
52721afaf18SBorislav Petkov if (!(i_mce.status & MCI_STATUS_PCC))
52821afaf18SBorislav Petkov mcg_status |= MCG_STATUS_RIPV;
52921afaf18SBorislav Petkov
53021afaf18SBorislav Petkov /*
53121afaf18SBorislav Petkov * Ensure necessary status bits for deferred errors:
53221afaf18SBorislav Petkov * - MCx_STATUS[Deferred]: make sure it is a deferred error
5335d7f7d1dSZhenzhong Duan * - MCx_STATUS[UC] cleared: deferred errors are _not_ UC
53421afaf18SBorislav Petkov */
53521afaf18SBorislav Petkov if (inj_type == DFR_INT_INJ) {
53621afaf18SBorislav Petkov i_mce.status |= MCI_STATUS_DEFERRED;
53721afaf18SBorislav Petkov i_mce.status &= ~MCI_STATUS_UC;
53821afaf18SBorislav Petkov }
53921afaf18SBorislav Petkov
54021afaf18SBorislav Petkov /*
54167e87d43SBorislav Petkov * For multi node CPUs, logging and reporting of bank 4 errors happens
54221afaf18SBorislav Petkov * only on the node base core. Refer to D18F3x44[NbMcaToMstCpuEn] for
54321afaf18SBorislav Petkov * Fam10h and later BKDGs.
5447e3ec628SThomas Gleixner */
5457e3ec628SThomas Gleixner if (boot_cpu_has(X86_FEATURE_AMD_DCM) &&
54621afaf18SBorislav Petkov b == 4 &&
54721afaf18SBorislav Petkov boot_cpu_data.x86 < 0x17) {
5488ae9e3f6SSebastian Andrzej Siewior toggle_nb_mca_mst_cpu(topology_amd_node_id(cpu));
54921afaf18SBorislav Petkov cpu = get_nbc_for_node(topology_amd_node_id(cpu));
55021afaf18SBorislav Petkov }
55121afaf18SBorislav Petkov
55221afaf18SBorislav Petkov cpus_read_lock();
55321afaf18SBorislav Petkov if (!cpu_online(cpu))
55421afaf18SBorislav Petkov goto err;
55521afaf18SBorislav Petkov
55621afaf18SBorislav Petkov toggle_hw_mce_inject(cpu, true);
55721afaf18SBorislav Petkov
55821afaf18SBorislav Petkov i_mce.mcgstatus = mcg_status;
55921afaf18SBorislav Petkov i_mce.inject_flags = inj_type;
56021afaf18SBorislav Petkov smp_call_function_single(cpu, prepare_msrs, &i_mce, 0);
56121afaf18SBorislav Petkov
56221afaf18SBorislav Petkov toggle_hw_mce_inject(cpu, false);
56321afaf18SBorislav Petkov
56421afaf18SBorislav Petkov switch (inj_type) {
56521afaf18SBorislav Petkov case DFR_INT_INJ:
56621afaf18SBorislav Petkov smp_call_function_single(cpu, trigger_dfr_int, NULL, 0);
56721afaf18SBorislav Petkov break;
56821afaf18SBorislav Petkov case THR_INT_INJ:
56921afaf18SBorislav Petkov smp_call_function_single(cpu, trigger_thr_int, NULL, 0);
57021afaf18SBorislav Petkov break;
57121afaf18SBorislav Petkov default:
5728ae9e3f6SSebastian Andrzej Siewior smp_call_function_single(cpu, trigger_mce, NULL, 0);
57321afaf18SBorislav Petkov }
57421afaf18SBorislav Petkov
57521afaf18SBorislav Petkov err:
57621afaf18SBorislav Petkov cpus_read_unlock();
57721afaf18SBorislav Petkov
57821afaf18SBorislav Petkov }
57921afaf18SBorislav Petkov
58021afaf18SBorislav Petkov /*
58121afaf18SBorislav Petkov * This denotes into which bank we're injecting and triggers
58221afaf18SBorislav Petkov * the injection, at the same time.
583006c0770SYazen Ghannam */
inj_bank_set(void * data,u64 val)584006c0770SYazen Ghannam static int inj_bank_set(void *data, u64 val)
585006c0770SYazen Ghannam {
586006c0770SYazen Ghannam struct mce *m = (struct mce *)data;
587006c0770SYazen Ghannam u8 n_banks;
588006c0770SYazen Ghannam u64 cap;
58921afaf18SBorislav Petkov
59021afaf18SBorislav Petkov /* Get bank count on target CPU so we can handle non-uniform values. */
591006c0770SYazen Ghannam rdmsrl_on_cpu(m->extcpu, MSR_IA32_MCG_CAP, &cap);
59221afaf18SBorislav Petkov n_banks = cap & MCG_BANKCNT_MASK;
59321afaf18SBorislav Petkov
59421afaf18SBorislav Petkov if (val >= n_banks) {
59521afaf18SBorislav Petkov pr_err("MCA bank %llu non-existent on CPU%d\n", val, m->extcpu);
596e48d008bSSmita Koralahalli return -EINVAL;
597e48d008bSSmita Koralahalli }
598e48d008bSSmita Koralahalli
599e48d008bSSmita Koralahalli m->bank = val;
600e48d008bSSmita Koralahalli
601e48d008bSSmita Koralahalli /*
602e48d008bSSmita Koralahalli * sw-only injection allows to write arbitrary values into the MCA
603e48d008bSSmita Koralahalli * registers because it tests only the decoding paths.
604e48d008bSSmita Koralahalli */
605e48d008bSSmita Koralahalli if (inj_type == SW_INJ)
606e48d008bSSmita Koralahalli goto inject;
607e48d008bSSmita Koralahalli
608e48d008bSSmita Koralahalli /*
609e48d008bSSmita Koralahalli * Read IPID value to determine if a bank is populated on the target
610e48d008bSSmita Koralahalli * CPU.
611e48d008bSSmita Koralahalli */
612e48d008bSSmita Koralahalli if (cpu_feature_enabled(X86_FEATURE_SMCA)) {
613e48d008bSSmita Koralahalli u64 ipid;
614e48d008bSSmita Koralahalli
615e48d008bSSmita Koralahalli if (rdmsrl_on_cpu(m->extcpu, MSR_AMD64_SMCA_MCx_IPID(val), &ipid)) {
616e48d008bSSmita Koralahalli pr_err("Error reading IPID on CPU%d\n", m->extcpu);
617e48d008bSSmita Koralahalli return -EINVAL;
618e48d008bSSmita Koralahalli }
619e48d008bSSmita Koralahalli
620e48d008bSSmita Koralahalli if (!ipid) {
621e48d008bSSmita Koralahalli pr_err("Cannot inject into unpopulated bank %llu\n", val);
622e48d008bSSmita Koralahalli return -ENODEV;
62321afaf18SBorislav Petkov }
62421afaf18SBorislav Petkov }
62521afaf18SBorislav Petkov
62621afaf18SBorislav Petkov inject:
62721afaf18SBorislav Petkov do_inject();
62821afaf18SBorislav Petkov
62921afaf18SBorislav Petkov /* Reset injection struct */
63021afaf18SBorislav Petkov setup_inj_struct(&i_mce);
63121afaf18SBorislav Petkov
63221afaf18SBorislav Petkov return 0;
63321afaf18SBorislav Petkov }
63421afaf18SBorislav Petkov
63521afaf18SBorislav Petkov MCE_INJECT_GET(bank);
63621afaf18SBorislav Petkov
63721afaf18SBorislav Petkov DEFINE_SIMPLE_ATTRIBUTE(bank_fops, inj_bank_get, inj_bank_set, "%llu\n");
63821afaf18SBorislav Petkov
63921afaf18SBorislav Petkov static const char readme_msg[] =
64021afaf18SBorislav Petkov "Description of the files and their usages:\n"
64121afaf18SBorislav Petkov "\n"
64221afaf18SBorislav Petkov "Note1: i refers to the bank number below.\n"
64321afaf18SBorislav Petkov "Note2: See respective BKDGs for the exact bit definitions of the files below\n"
64421afaf18SBorislav Petkov "as they mirror the hardware registers.\n"
64521afaf18SBorislav Petkov "\n"
64621afaf18SBorislav Petkov "status:\t Set MCi_STATUS: the bits in that MSR control the error type and\n"
64721afaf18SBorislav Petkov "\t attributes of the error which caused the MCE.\n"
64821afaf18SBorislav Petkov "\n"
64921afaf18SBorislav Petkov "misc:\t Set MCi_MISC: provide auxiliary info about the error. It is mostly\n"
65021afaf18SBorislav Petkov "\t used for error thresholding purposes and its validity is indicated by\n"
65121afaf18SBorislav Petkov "\t MCi_STATUS[MiscV].\n"
65221afaf18SBorislav Petkov "\n"
65321afaf18SBorislav Petkov "synd:\t Set MCi_SYND: provide syndrome info about the error. Only valid on\n"
65421afaf18SBorislav Petkov "\t Scalable MCA systems, and its validity is indicated by MCi_STATUS[SyndV].\n"
65521afaf18SBorislav Petkov "\n"
65621afaf18SBorislav Petkov "addr:\t Error address value to be written to MCi_ADDR. Log address information\n"
65721afaf18SBorislav Petkov "\t associated with the error.\n"
65821afaf18SBorislav Petkov "\n"
65921afaf18SBorislav Petkov "cpu:\t The CPU to inject the error on.\n"
66021afaf18SBorislav Petkov "\n"
66121afaf18SBorislav Petkov "bank:\t Specify the bank you want to inject the error into: the number of\n"
66221afaf18SBorislav Petkov "\t banks in a processor varies and is family/model-specific, therefore, the\n"
66321afaf18SBorislav Petkov "\t supplied value is sanity-checked. Setting the bank value also triggers the\n"
66421afaf18SBorislav Petkov "\t injection.\n"
66521afaf18SBorislav Petkov "\n"
66621afaf18SBorislav Petkov "flags:\t Injection type to be performed. Writing to this file will trigger a\n"
66721afaf18SBorislav Petkov "\t real machine check, an APIC interrupt or invoke the error decoder routines\n"
66821afaf18SBorislav Petkov "\t for AMD processors.\n"
66921afaf18SBorislav Petkov "\n"
67021afaf18SBorislav Petkov "\t Allowed error injection types:\n"
67121afaf18SBorislav Petkov "\t - \"sw\": Software error injection. Decode error to a human-readable \n"
67221afaf18SBorislav Petkov "\t format only. Safe to use.\n"
67321afaf18SBorislav Petkov "\t - \"hw\": Hardware error injection. Causes the #MC exception handler to \n"
67421afaf18SBorislav Petkov "\t handle the error. Be warned: might cause system panic if MCi_STATUS[PCC] \n"
67521afaf18SBorislav Petkov "\t is set. Therefore, consider setting (debugfs_mountpoint)/mce/fake_panic \n"
67621afaf18SBorislav Petkov "\t before injecting.\n"
67721afaf18SBorislav Petkov "\t - \"df\": Trigger APIC interrupt for Deferred error. Causes deferred \n"
6782ffdc2c3SBorislav Petkov "\t error APIC interrupt handler to handle the error if the feature is \n"
6792ffdc2c3SBorislav Petkov "\t is present in hardware. \n"
68021afaf18SBorislav Petkov "\t - \"th\": Trigger APIC interrupt for Threshold errors. Causes threshold \n"
68121afaf18SBorislav Petkov "\t APIC interrupt handler to handle the error. \n"
68221afaf18SBorislav Petkov "\n"
68321afaf18SBorislav Petkov "ipid:\t IPID (AMD-specific)\n"
68421afaf18SBorislav Petkov "\n";
68521afaf18SBorislav Petkov
68621afaf18SBorislav Petkov static ssize_t
inj_readme_read(struct file * filp,char __user * ubuf,size_t cnt,loff_t * ppos)68721afaf18SBorislav Petkov inj_readme_read(struct file *filp, char __user *ubuf,
68821afaf18SBorislav Petkov size_t cnt, loff_t *ppos)
68921afaf18SBorislav Petkov {
69021afaf18SBorislav Petkov return simple_read_from_buffer(ubuf, cnt, ppos,
69121afaf18SBorislav Petkov readme_msg, strlen(readme_msg));
69221afaf18SBorislav Petkov }
69321afaf18SBorislav Petkov
69421afaf18SBorislav Petkov static const struct file_operations readme_fops = {
69521afaf18SBorislav Petkov .read = inj_readme_read,
69621afaf18SBorislav Petkov };
69721afaf18SBorislav Petkov
69821afaf18SBorislav Petkov static struct dfs_node {
69921afaf18SBorislav Petkov char *name;
70021afaf18SBorislav Petkov const struct file_operations *fops;
70121afaf18SBorislav Petkov umode_t perm;
70221afaf18SBorislav Petkov } dfs_fls[] = {
7032ffdc2c3SBorislav Petkov { .name = "status", .fops = &status_fops, .perm = S_IRUSR | S_IWUSR },
70421afaf18SBorislav Petkov { .name = "misc", .fops = &misc_fops, .perm = S_IRUSR | S_IWUSR },
70521afaf18SBorislav Petkov { .name = "addr", .fops = &addr_fops, .perm = S_IRUSR | S_IWUSR },
70621afaf18SBorislav Petkov { .name = "synd", .fops = &synd_fops, .perm = S_IRUSR | S_IWUSR },
70721afaf18SBorislav Petkov { .name = "ipid", .fops = &ipid_fops, .perm = S_IRUSR | S_IWUSR },
70821afaf18SBorislav Petkov { .name = "bank", .fops = &bank_fops, .perm = S_IRUSR | S_IWUSR },
70921afaf18SBorislav Petkov { .name = "flags", .fops = &flags_fops, .perm = S_IRUSR | S_IWUSR },
7106e4f929eSGreg Kroah-Hartman { .name = "cpu", .fops = &extcpu_fops, .perm = S_IRUSR | S_IWUSR },
71121afaf18SBorislav Petkov { .name = "README", .fops = &readme_fops, .perm = S_IRUSR | S_IRGRP | S_IROTH },
71221afaf18SBorislav Petkov };
71321afaf18SBorislav Petkov
debugfs_init(void)71421afaf18SBorislav Petkov static void __init debugfs_init(void)
71521afaf18SBorislav Petkov {
7166e4f929eSGreg Kroah-Hartman unsigned int i;
7176e4f929eSGreg Kroah-Hartman
7186e4f929eSGreg Kroah-Hartman dfs_inj = debugfs_create_dir("mce-inject", NULL);
71921afaf18SBorislav Petkov
72021afaf18SBorislav Petkov for (i = 0; i < ARRAY_SIZE(dfs_fls); i++)
721891e465aSSmita Koralahalli debugfs_create_file(dfs_fls[i].name, dfs_fls[i].perm, dfs_inj,
722891e465aSSmita Koralahalli &i_mce, dfs_fls[i].fops);
723891e465aSSmita Koralahalli }
724891e465aSSmita Koralahalli
check_hw_inj_possible(void)725891e465aSSmita Koralahalli static void check_hw_inj_possible(void)
726891e465aSSmita Koralahalli {
727891e465aSSmita Koralahalli int cpu;
728891e465aSSmita Koralahalli u8 bank;
729891e465aSSmita Koralahalli
730891e465aSSmita Koralahalli /*
731891e465aSSmita Koralahalli * This behavior exists only on SMCA systems though its not directly
732891e465aSSmita Koralahalli * related to SMCA.
733891e465aSSmita Koralahalli */
734891e465aSSmita Koralahalli if (!cpu_feature_enabled(X86_FEATURE_SMCA))
735891e465aSSmita Koralahalli return;
736891e465aSSmita Koralahalli
737891e465aSSmita Koralahalli cpu = get_cpu();
738891e465aSSmita Koralahalli
739891e465aSSmita Koralahalli for (bank = 0; bank < MAX_NR_BANKS; ++bank) {
740891e465aSSmita Koralahalli u64 status = MCI_STATUS_VAL, ipid;
741891e465aSSmita Koralahalli
742891e465aSSmita Koralahalli /* Check whether bank is populated */
743891e465aSSmita Koralahalli rdmsrl(MSR_AMD64_SMCA_MCx_IPID(bank), ipid);
744891e465aSSmita Koralahalli if (!ipid)
745891e465aSSmita Koralahalli continue;
746891e465aSSmita Koralahalli
7476175b407SYazen Ghannam toggle_hw_mce_inject(cpu, true);
748891e465aSSmita Koralahalli
749891e465aSSmita Koralahalli wrmsrl_safe(mca_msr_reg(bank, MCA_STATUS), status);
750891e465aSSmita Koralahalli rdmsrl_safe(mca_msr_reg(bank, MCA_STATUS), &status);
751891e465aSSmita Koralahalli wrmsrl_safe(mca_msr_reg(bank, MCA_STATUS), 0);
752891e465aSSmita Koralahalli
753891e465aSSmita Koralahalli if (!status) {
754891e465aSSmita Koralahalli hw_injection_possible = false;
755891e465aSSmita Koralahalli pr_warn("Platform does not allow *hardware* error injection."
756891e465aSSmita Koralahalli "Try using APEI EINJ instead.\n");
757891e465aSSmita Koralahalli }
758891e465aSSmita Koralahalli
759891e465aSSmita Koralahalli toggle_hw_mce_inject(cpu, false);
760891e465aSSmita Koralahalli
761891e465aSSmita Koralahalli break;
762891e465aSSmita Koralahalli }
76321afaf18SBorislav Petkov
76421afaf18SBorislav Petkov put_cpu();
76521afaf18SBorislav Petkov }
76621afaf18SBorislav Petkov
inject_init(void)76721afaf18SBorislav Petkov static int __init inject_init(void)
768891e465aSSmita Koralahalli {
769891e465aSSmita Koralahalli if (!alloc_cpumask_var(&mce_inject_cpumask, GFP_KERNEL))
7706e4f929eSGreg Kroah-Hartman return -ENOMEM;
77121afaf18SBorislav Petkov
77221afaf18SBorislav Petkov check_hw_inj_possible();
77321afaf18SBorislav Petkov
77421afaf18SBorislav Petkov debugfs_init();
77521afaf18SBorislav Petkov
77621afaf18SBorislav Petkov register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify");
77721afaf18SBorislav Petkov mce_register_injector_chain(&inject_nb);
77821afaf18SBorislav Petkov
77921afaf18SBorislav Petkov setup_inj_struct(&i_mce);
78021afaf18SBorislav Petkov
78121afaf18SBorislav Petkov pr_info("Machine check injector initialized\n");
78221afaf18SBorislav Petkov
78321afaf18SBorislav Petkov return 0;
78421afaf18SBorislav Petkov }
78521afaf18SBorislav Petkov
inject_exit(void)78621afaf18SBorislav Petkov static void __exit inject_exit(void)
78721afaf18SBorislav Petkov {
78821afaf18SBorislav Petkov
78921afaf18SBorislav Petkov mce_unregister_injector_chain(&inject_nb);
79021afaf18SBorislav Petkov unregister_nmi_handler(NMI_LOCAL, "mce_notify");
79121afaf18SBorislav Petkov
79221afaf18SBorislav Petkov debugfs_remove_recursive(dfs_inj);
79321afaf18SBorislav Petkov dfs_inj = NULL;
79421afaf18SBorislav Petkov
79521afaf18SBorislav Petkov memset(&dfs_fls, 0, sizeof(dfs_fls));
79621afaf18SBorislav Petkov
79721afaf18SBorislav Petkov free_cpumask_var(mce_inject_cpumask);
798*eb9d3c0bSJeff Johnson }
79921afaf18SBorislav Petkov
800 module_init(inject_init);
801 module_exit(inject_exit);
802 MODULE_DESCRIPTION("Machine check injection support");
803 MODULE_LICENSE("GPL");
804