xref: /linux/arch/x86/kernel/cpu/mce/inject.c (revision 6175b407756b22e7fdc771181b7d832ebdedef5c)
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. */
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 
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. */
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 
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 
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(&regs, 0, sizeof(struct pt_regs));
16321afaf18SBorislav Petkov 		regs.ip = m->ip;
16421afaf18SBorislav Petkov 		regs.cs = m->cs;
16521afaf18SBorislav Petkov 		pregs = &regs;
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 
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 
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 */
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 
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 
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  */
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 
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 
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 
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 
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 
41621afaf18SBorislav Petkov static void trigger_mce(void *info)
41721afaf18SBorislav Petkov {
41821afaf18SBorislav Petkov 	asm volatile("int $18");
41921afaf18SBorislav Petkov }
42021afaf18SBorislav Petkov 
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 
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 
43121afaf18SBorislav Petkov static u32 get_nbc_for_node(int node_id)
43221afaf18SBorislav Petkov {
43321afaf18SBorislav Petkov 	struct cpuinfo_x86 *c = &boot_cpu_data;
43421afaf18SBorislav Petkov 	u32 cores_per_node;
43521afaf18SBorislav Petkov 
43621afaf18SBorislav Petkov 	cores_per_node = (c->x86_max_cores * smp_num_siblings) / amd_get_nodes_per_socket();
43721afaf18SBorislav Petkov 
43821afaf18SBorislav Petkov 	return cores_per_node * node_id;
43921afaf18SBorislav Petkov }
44021afaf18SBorislav Petkov 
44121afaf18SBorislav Petkov static void toggle_nb_mca_mst_cpu(u16 nid)
44221afaf18SBorislav Petkov {
44321afaf18SBorislav Petkov 	struct amd_northbridge *nb;
44421afaf18SBorislav Petkov 	struct pci_dev *F3;
44521afaf18SBorislav Petkov 	u32 val;
44621afaf18SBorislav Petkov 	int err;
44721afaf18SBorislav Petkov 
44821afaf18SBorislav Petkov 	nb = node_to_amd_nb(nid);
44921afaf18SBorislav Petkov 	if (!nb)
45021afaf18SBorislav Petkov 		return;
45121afaf18SBorislav Petkov 
45221afaf18SBorislav Petkov 	F3 = nb->misc;
45321afaf18SBorislav Petkov 	if (!F3)
45421afaf18SBorislav Petkov 		return;
45521afaf18SBorislav Petkov 
45621afaf18SBorislav Petkov 	err = pci_read_config_dword(F3, NBCFG, &val);
45721afaf18SBorislav Petkov 	if (err) {
45821afaf18SBorislav Petkov 		pr_err("%s: Error reading F%dx%03x.\n",
45921afaf18SBorislav Petkov 		       __func__, PCI_FUNC(F3->devfn), NBCFG);
46021afaf18SBorislav Petkov 		return;
46121afaf18SBorislav Petkov 	}
46221afaf18SBorislav Petkov 
46321afaf18SBorislav Petkov 	if (val & BIT(27))
46421afaf18SBorislav Petkov 		return;
46521afaf18SBorislav Petkov 
46621afaf18SBorislav Petkov 	pr_err("%s: Set D18F3x44[NbMcaToMstCpuEn] which BIOS hasn't done.\n",
46721afaf18SBorislav Petkov 	       __func__);
46821afaf18SBorislav Petkov 
46921afaf18SBorislav Petkov 	val |= BIT(27);
47021afaf18SBorislav Petkov 	err = pci_write_config_dword(F3, NBCFG, val);
47121afaf18SBorislav Petkov 	if (err)
47221afaf18SBorislav Petkov 		pr_err("%s: Error writing F%dx%03x.\n",
47321afaf18SBorislav Petkov 		       __func__, PCI_FUNC(F3->devfn), NBCFG);
47421afaf18SBorislav Petkov }
47521afaf18SBorislav Petkov 
47621afaf18SBorislav Petkov static void prepare_msrs(void *info)
47721afaf18SBorislav Petkov {
47821afaf18SBorislav Petkov 	struct mce m = *(struct mce *)info;
47921afaf18SBorislav Petkov 	u8 b = m.bank;
48021afaf18SBorislav Petkov 
48121afaf18SBorislav Petkov 	wrmsrl(MSR_IA32_MCG_STATUS, m.mcgstatus);
48221afaf18SBorislav Petkov 
48321afaf18SBorislav Petkov 	if (boot_cpu_has(X86_FEATURE_SMCA)) {
48421afaf18SBorislav Petkov 		if (m.inject_flags == DFR_INT_INJ) {
48521afaf18SBorislav Petkov 			wrmsrl(MSR_AMD64_SMCA_MCx_DESTAT(b), m.status);
48621afaf18SBorislav Petkov 			wrmsrl(MSR_AMD64_SMCA_MCx_DEADDR(b), m.addr);
48721afaf18SBorislav Petkov 		} else {
48821afaf18SBorislav Petkov 			wrmsrl(MSR_AMD64_SMCA_MCx_STATUS(b), m.status);
48921afaf18SBorislav Petkov 			wrmsrl(MSR_AMD64_SMCA_MCx_ADDR(b), m.addr);
49021afaf18SBorislav Petkov 		}
49121afaf18SBorislav Petkov 
49221afaf18SBorislav Petkov 		wrmsrl(MSR_AMD64_SMCA_MCx_MISC(b), m.misc);
49321afaf18SBorislav Petkov 		wrmsrl(MSR_AMD64_SMCA_MCx_SYND(b), m.synd);
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 		wrmsrl(MSR_IA32_MCx_MISC(b), m.misc);
49821afaf18SBorislav Petkov 	}
49921afaf18SBorislav Petkov }
50021afaf18SBorislav Petkov 
50121afaf18SBorislav Petkov static void do_inject(void)
50221afaf18SBorislav Petkov {
50321afaf18SBorislav Petkov 	u64 mcg_status = 0;
50421afaf18SBorislav Petkov 	unsigned int cpu = i_mce.extcpu;
50521afaf18SBorislav Petkov 	u8 b = i_mce.bank;
50621afaf18SBorislav Petkov 
50721afaf18SBorislav Petkov 	i_mce.tsc = rdtsc_ordered();
50821afaf18SBorislav Petkov 
5091e56279aSSmita Koralahalli 	i_mce.status |= MCI_STATUS_VAL;
5101e56279aSSmita Koralahalli 
51121afaf18SBorislav Petkov 	if (i_mce.misc)
51221afaf18SBorislav Petkov 		i_mce.status |= MCI_STATUS_MISCV;
51321afaf18SBorislav Petkov 
51421afaf18SBorislav Petkov 	if (i_mce.synd)
51521afaf18SBorislav Petkov 		i_mce.status |= MCI_STATUS_SYNDV;
51621afaf18SBorislav Petkov 
51721afaf18SBorislav Petkov 	if (inj_type == SW_INJ) {
51881736abdSJan H. Schönherr 		mce_log(&i_mce);
51921afaf18SBorislav Petkov 		return;
52021afaf18SBorislav Petkov 	}
52121afaf18SBorislav Petkov 
52221afaf18SBorislav Petkov 	/* prep MCE global settings for the injection */
52321afaf18SBorislav Petkov 	mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV;
52421afaf18SBorislav Petkov 
52521afaf18SBorislav Petkov 	if (!(i_mce.status & MCI_STATUS_PCC))
52621afaf18SBorislav Petkov 		mcg_status |= MCG_STATUS_RIPV;
52721afaf18SBorislav Petkov 
52821afaf18SBorislav Petkov 	/*
52921afaf18SBorislav Petkov 	 * Ensure necessary status bits for deferred errors:
53021afaf18SBorislav Petkov 	 * - MCx_STATUS[Deferred]: make sure it is a deferred error
53121afaf18SBorislav Petkov 	 * - MCx_STATUS[UC] cleared: deferred errors are _not_ UC
53221afaf18SBorislav Petkov 	 */
53321afaf18SBorislav Petkov 	if (inj_type == DFR_INT_INJ) {
53421afaf18SBorislav Petkov 		i_mce.status |= MCI_STATUS_DEFERRED;
5355d7f7d1dSZhenzhong Duan 		i_mce.status &= ~MCI_STATUS_UC;
53621afaf18SBorislav Petkov 	}
53721afaf18SBorislav Petkov 
53821afaf18SBorislav Petkov 	/*
53921afaf18SBorislav Petkov 	 * For multi node CPUs, logging and reporting of bank 4 errors happens
54021afaf18SBorislav Petkov 	 * only on the node base core. Refer to D18F3x44[NbMcaToMstCpuEn] for
54121afaf18SBorislav Petkov 	 * Fam10h and later BKDGs.
54221afaf18SBorislav Petkov 	 */
54367e87d43SBorislav Petkov 	if (boot_cpu_has(X86_FEATURE_AMD_DCM) &&
54421afaf18SBorislav Petkov 	    b == 4 &&
54521afaf18SBorislav Petkov 	    boot_cpu_data.x86 < 0x17) {
546db970bd2SYazen Ghannam 		toggle_nb_mca_mst_cpu(topology_die_id(cpu));
547db970bd2SYazen Ghannam 		cpu = get_nbc_for_node(topology_die_id(cpu));
54821afaf18SBorislav Petkov 	}
54921afaf18SBorislav Petkov 
5508ae9e3f6SSebastian Andrzej Siewior 	cpus_read_lock();
55121afaf18SBorislav Petkov 	if (!cpu_online(cpu))
55221afaf18SBorislav Petkov 		goto err;
55321afaf18SBorislav Petkov 
55421afaf18SBorislav Petkov 	toggle_hw_mce_inject(cpu, true);
55521afaf18SBorislav Petkov 
55621afaf18SBorislav Petkov 	i_mce.mcgstatus = mcg_status;
55721afaf18SBorislav Petkov 	i_mce.inject_flags = inj_type;
55821afaf18SBorislav Petkov 	smp_call_function_single(cpu, prepare_msrs, &i_mce, 0);
55921afaf18SBorislav Petkov 
56021afaf18SBorislav Petkov 	toggle_hw_mce_inject(cpu, false);
56121afaf18SBorislav Petkov 
56221afaf18SBorislav Petkov 	switch (inj_type) {
56321afaf18SBorislav Petkov 	case DFR_INT_INJ:
56421afaf18SBorislav Petkov 		smp_call_function_single(cpu, trigger_dfr_int, NULL, 0);
56521afaf18SBorislav Petkov 		break;
56621afaf18SBorislav Petkov 	case THR_INT_INJ:
56721afaf18SBorislav Petkov 		smp_call_function_single(cpu, trigger_thr_int, NULL, 0);
56821afaf18SBorislav Petkov 		break;
56921afaf18SBorislav Petkov 	default:
57021afaf18SBorislav Petkov 		smp_call_function_single(cpu, trigger_mce, NULL, 0);
57121afaf18SBorislav Petkov 	}
57221afaf18SBorislav Petkov 
57321afaf18SBorislav Petkov err:
5748ae9e3f6SSebastian Andrzej Siewior 	cpus_read_unlock();
57521afaf18SBorislav Petkov 
57621afaf18SBorislav Petkov }
57721afaf18SBorislav Petkov 
57821afaf18SBorislav Petkov /*
57921afaf18SBorislav Petkov  * This denotes into which bank we're injecting and triggers
58021afaf18SBorislav Petkov  * the injection, at the same time.
58121afaf18SBorislav Petkov  */
58221afaf18SBorislav Petkov static int inj_bank_set(void *data, u64 val)
58321afaf18SBorislav Petkov {
58421afaf18SBorislav Petkov 	struct mce *m = (struct mce *)data;
585006c0770SYazen Ghannam 	u8 n_banks;
586006c0770SYazen Ghannam 	u64 cap;
587006c0770SYazen Ghannam 
588006c0770SYazen Ghannam 	/* Get bank count on target CPU so we can handle non-uniform values. */
589006c0770SYazen Ghannam 	rdmsrl_on_cpu(m->extcpu, MSR_IA32_MCG_CAP, &cap);
590006c0770SYazen Ghannam 	n_banks = cap & MCG_BANKCNT_MASK;
59121afaf18SBorislav Petkov 
59221afaf18SBorislav Petkov 	if (val >= n_banks) {
593006c0770SYazen Ghannam 		pr_err("MCA bank %llu non-existent on CPU%d\n", val, m->extcpu);
59421afaf18SBorislav Petkov 		return -EINVAL;
59521afaf18SBorislav Petkov 	}
59621afaf18SBorislav Petkov 
59721afaf18SBorislav Petkov 	m->bank = val;
598e48d008bSSmita Koralahalli 
599e48d008bSSmita Koralahalli 	/*
600e48d008bSSmita Koralahalli 	 * sw-only injection allows to write arbitrary values into the MCA
601e48d008bSSmita Koralahalli 	 * registers because it tests only the decoding paths.
602e48d008bSSmita Koralahalli 	 */
603e48d008bSSmita Koralahalli 	if (inj_type == SW_INJ)
604e48d008bSSmita Koralahalli 		goto inject;
605e48d008bSSmita Koralahalli 
606e48d008bSSmita Koralahalli 	/*
607e48d008bSSmita Koralahalli 	 * Read IPID value to determine if a bank is populated on the target
608e48d008bSSmita Koralahalli 	 * CPU.
609e48d008bSSmita Koralahalli 	 */
610e48d008bSSmita Koralahalli 	if (cpu_feature_enabled(X86_FEATURE_SMCA)) {
611e48d008bSSmita Koralahalli 		u64 ipid;
612e48d008bSSmita Koralahalli 
613e48d008bSSmita Koralahalli 		if (rdmsrl_on_cpu(m->extcpu, MSR_AMD64_SMCA_MCx_IPID(val), &ipid)) {
614e48d008bSSmita Koralahalli 			pr_err("Error reading IPID on CPU%d\n", m->extcpu);
615e48d008bSSmita Koralahalli 			return -EINVAL;
616e48d008bSSmita Koralahalli 		}
617e48d008bSSmita Koralahalli 
618e48d008bSSmita Koralahalli 		if (!ipid) {
619e48d008bSSmita Koralahalli 			pr_err("Cannot inject into unpopulated bank %llu\n", val);
620e48d008bSSmita Koralahalli 			return -ENODEV;
621e48d008bSSmita Koralahalli 		}
622e48d008bSSmita Koralahalli 	}
623e48d008bSSmita Koralahalli 
624e48d008bSSmita Koralahalli inject:
62521afaf18SBorislav Petkov 	do_inject();
62621afaf18SBorislav Petkov 
62721afaf18SBorislav Petkov 	/* Reset injection struct */
62821afaf18SBorislav Petkov 	setup_inj_struct(&i_mce);
62921afaf18SBorislav Petkov 
63021afaf18SBorislav Petkov 	return 0;
63121afaf18SBorislav Petkov }
63221afaf18SBorislav Petkov 
63321afaf18SBorislav Petkov MCE_INJECT_GET(bank);
63421afaf18SBorislav Petkov 
63521afaf18SBorislav Petkov DEFINE_SIMPLE_ATTRIBUTE(bank_fops, inj_bank_get, inj_bank_set, "%llu\n");
63621afaf18SBorislav Petkov 
63721afaf18SBorislav Petkov static const char readme_msg[] =
63821afaf18SBorislav Petkov "Description of the files and their usages:\n"
63921afaf18SBorislav Petkov "\n"
64021afaf18SBorislav Petkov "Note1: i refers to the bank number below.\n"
64121afaf18SBorislav Petkov "Note2: See respective BKDGs for the exact bit definitions of the files below\n"
64221afaf18SBorislav Petkov "as they mirror the hardware registers.\n"
64321afaf18SBorislav Petkov "\n"
64421afaf18SBorislav Petkov "status:\t Set MCi_STATUS: the bits in that MSR control the error type and\n"
64521afaf18SBorislav Petkov "\t attributes of the error which caused the MCE.\n"
64621afaf18SBorislav Petkov "\n"
64721afaf18SBorislav Petkov "misc:\t Set MCi_MISC: provide auxiliary info about the error. It is mostly\n"
64821afaf18SBorislav Petkov "\t used for error thresholding purposes and its validity is indicated by\n"
64921afaf18SBorislav Petkov "\t MCi_STATUS[MiscV].\n"
65021afaf18SBorislav Petkov "\n"
65121afaf18SBorislav Petkov "synd:\t Set MCi_SYND: provide syndrome info about the error. Only valid on\n"
65221afaf18SBorislav Petkov "\t Scalable MCA systems, and its validity is indicated by MCi_STATUS[SyndV].\n"
65321afaf18SBorislav Petkov "\n"
65421afaf18SBorislav Petkov "addr:\t Error address value to be written to MCi_ADDR. Log address information\n"
65521afaf18SBorislav Petkov "\t associated with the error.\n"
65621afaf18SBorislav Petkov "\n"
65721afaf18SBorislav Petkov "cpu:\t The CPU to inject the error on.\n"
65821afaf18SBorislav Petkov "\n"
65921afaf18SBorislav Petkov "bank:\t Specify the bank you want to inject the error into: the number of\n"
66021afaf18SBorislav Petkov "\t banks in a processor varies and is family/model-specific, therefore, the\n"
66121afaf18SBorislav Petkov "\t supplied value is sanity-checked. Setting the bank value also triggers the\n"
66221afaf18SBorislav Petkov "\t injection.\n"
66321afaf18SBorislav Petkov "\n"
66421afaf18SBorislav Petkov "flags:\t Injection type to be performed. Writing to this file will trigger a\n"
66521afaf18SBorislav Petkov "\t real machine check, an APIC interrupt or invoke the error decoder routines\n"
66621afaf18SBorislav Petkov "\t for AMD processors.\n"
66721afaf18SBorislav Petkov "\n"
66821afaf18SBorislav Petkov "\t Allowed error injection types:\n"
66921afaf18SBorislav Petkov "\t  - \"sw\": Software error injection. Decode error to a human-readable \n"
67021afaf18SBorislav Petkov "\t    format only. Safe to use.\n"
67121afaf18SBorislav Petkov "\t  - \"hw\": Hardware error injection. Causes the #MC exception handler to \n"
67221afaf18SBorislav Petkov "\t    handle the error. Be warned: might cause system panic if MCi_STATUS[PCC] \n"
67321afaf18SBorislav Petkov "\t    is set. Therefore, consider setting (debugfs_mountpoint)/mce/fake_panic \n"
67421afaf18SBorislav Petkov "\t    before injecting.\n"
67521afaf18SBorislav Petkov "\t  - \"df\": Trigger APIC interrupt for Deferred error. Causes deferred \n"
67621afaf18SBorislav Petkov "\t    error APIC interrupt handler to handle the error if the feature is \n"
67721afaf18SBorislav Petkov "\t    is present in hardware. \n"
67821afaf18SBorislav Petkov "\t  - \"th\": Trigger APIC interrupt for Threshold errors. Causes threshold \n"
67921afaf18SBorislav Petkov "\t    APIC interrupt handler to handle the error. \n"
6802ffdc2c3SBorislav Petkov "\n"
6812ffdc2c3SBorislav Petkov "ipid:\t IPID (AMD-specific)\n"
68221afaf18SBorislav Petkov "\n";
68321afaf18SBorislav Petkov 
68421afaf18SBorislav Petkov static ssize_t
68521afaf18SBorislav Petkov inj_readme_read(struct file *filp, char __user *ubuf,
68621afaf18SBorislav Petkov 		       size_t cnt, loff_t *ppos)
68721afaf18SBorislav Petkov {
68821afaf18SBorislav Petkov 	return simple_read_from_buffer(ubuf, cnt, ppos,
68921afaf18SBorislav Petkov 					readme_msg, strlen(readme_msg));
69021afaf18SBorislav Petkov }
69121afaf18SBorislav Petkov 
69221afaf18SBorislav Petkov static const struct file_operations readme_fops = {
69321afaf18SBorislav Petkov 	.read		= inj_readme_read,
69421afaf18SBorislav Petkov };
69521afaf18SBorislav Petkov 
69621afaf18SBorislav Petkov static struct dfs_node {
69721afaf18SBorislav Petkov 	char *name;
69821afaf18SBorislav Petkov 	const struct file_operations *fops;
69921afaf18SBorislav Petkov 	umode_t perm;
70021afaf18SBorislav Petkov } dfs_fls[] = {
70121afaf18SBorislav Petkov 	{ .name = "status",	.fops = &status_fops, .perm = S_IRUSR | S_IWUSR },
70221afaf18SBorislav Petkov 	{ .name = "misc",	.fops = &misc_fops,   .perm = S_IRUSR | S_IWUSR },
70321afaf18SBorislav Petkov 	{ .name = "addr",	.fops = &addr_fops,   .perm = S_IRUSR | S_IWUSR },
70421afaf18SBorislav Petkov 	{ .name = "synd",	.fops = &synd_fops,   .perm = S_IRUSR | S_IWUSR },
7052ffdc2c3SBorislav Petkov 	{ .name = "ipid",	.fops = &ipid_fops,   .perm = S_IRUSR | S_IWUSR },
70621afaf18SBorislav Petkov 	{ .name = "bank",	.fops = &bank_fops,   .perm = S_IRUSR | S_IWUSR },
70721afaf18SBorislav Petkov 	{ .name = "flags",	.fops = &flags_fops,  .perm = S_IRUSR | S_IWUSR },
70821afaf18SBorislav Petkov 	{ .name = "cpu",	.fops = &extcpu_fops, .perm = S_IRUSR | S_IWUSR },
70921afaf18SBorislav Petkov 	{ .name = "README",	.fops = &readme_fops, .perm = S_IRUSR | S_IRGRP | S_IROTH },
71021afaf18SBorislav Petkov };
71121afaf18SBorislav Petkov 
7126e4f929eSGreg Kroah-Hartman static void __init debugfs_init(void)
71321afaf18SBorislav Petkov {
71421afaf18SBorislav Petkov 	unsigned int i;
71521afaf18SBorislav Petkov 
71621afaf18SBorislav Petkov 	dfs_inj = debugfs_create_dir("mce-inject", NULL);
71721afaf18SBorislav Petkov 
7186e4f929eSGreg Kroah-Hartman 	for (i = 0; i < ARRAY_SIZE(dfs_fls); i++)
7196e4f929eSGreg Kroah-Hartman 		debugfs_create_file(dfs_fls[i].name, dfs_fls[i].perm, dfs_inj,
7206e4f929eSGreg Kroah-Hartman 				    &i_mce, dfs_fls[i].fops);
72121afaf18SBorislav Petkov }
72221afaf18SBorislav Petkov 
723891e465aSSmita Koralahalli static void check_hw_inj_possible(void)
724891e465aSSmita Koralahalli {
725891e465aSSmita Koralahalli 	int cpu;
726891e465aSSmita Koralahalli 	u8 bank;
727891e465aSSmita Koralahalli 
728891e465aSSmita Koralahalli 	/*
729891e465aSSmita Koralahalli 	 * This behavior exists only on SMCA systems though its not directly
730891e465aSSmita Koralahalli 	 * related to SMCA.
731891e465aSSmita Koralahalli 	 */
732891e465aSSmita Koralahalli 	if (!cpu_feature_enabled(X86_FEATURE_SMCA))
733891e465aSSmita Koralahalli 		return;
734891e465aSSmita Koralahalli 
735891e465aSSmita Koralahalli 	cpu = get_cpu();
736891e465aSSmita Koralahalli 
737891e465aSSmita Koralahalli 	for (bank = 0; bank < MAX_NR_BANKS; ++bank) {
738891e465aSSmita Koralahalli 		u64 status = MCI_STATUS_VAL, ipid;
739891e465aSSmita Koralahalli 
740891e465aSSmita Koralahalli 		/* Check whether bank is populated */
741891e465aSSmita Koralahalli 		rdmsrl(MSR_AMD64_SMCA_MCx_IPID(bank), ipid);
742891e465aSSmita Koralahalli 		if (!ipid)
743891e465aSSmita Koralahalli 			continue;
744891e465aSSmita Koralahalli 
745891e465aSSmita Koralahalli 		toggle_hw_mce_inject(cpu, true);
746891e465aSSmita Koralahalli 
747891e465aSSmita Koralahalli 		wrmsrl_safe(mca_msr_reg(bank, MCA_STATUS), status);
748891e465aSSmita Koralahalli 		rdmsrl_safe(mca_msr_reg(bank, MCA_STATUS), &status);
749*6175b407SYazen Ghannam 		wrmsrl_safe(mca_msr_reg(bank, MCA_STATUS), 0);
750891e465aSSmita Koralahalli 
751891e465aSSmita Koralahalli 		if (!status) {
752891e465aSSmita Koralahalli 			hw_injection_possible = false;
753891e465aSSmita Koralahalli 			pr_warn("Platform does not allow *hardware* error injection."
754891e465aSSmita Koralahalli 				"Try using APEI EINJ instead.\n");
755891e465aSSmita Koralahalli 		}
756891e465aSSmita Koralahalli 
757891e465aSSmita Koralahalli 		toggle_hw_mce_inject(cpu, false);
758891e465aSSmita Koralahalli 
759891e465aSSmita Koralahalli 		break;
760891e465aSSmita Koralahalli 	}
761891e465aSSmita Koralahalli 
762891e465aSSmita Koralahalli 	put_cpu();
763891e465aSSmita Koralahalli }
764891e465aSSmita Koralahalli 
76521afaf18SBorislav Petkov static int __init inject_init(void)
76621afaf18SBorislav Petkov {
76721afaf18SBorislav Petkov 	if (!alloc_cpumask_var(&mce_inject_cpumask, GFP_KERNEL))
76821afaf18SBorislav Petkov 		return -ENOMEM;
76921afaf18SBorislav Petkov 
770891e465aSSmita Koralahalli 	check_hw_inj_possible();
771891e465aSSmita Koralahalli 
7726e4f929eSGreg Kroah-Hartman 	debugfs_init();
77321afaf18SBorislav Petkov 
77421afaf18SBorislav Petkov 	register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify");
77521afaf18SBorislav Petkov 	mce_register_injector_chain(&inject_nb);
77621afaf18SBorislav Petkov 
77721afaf18SBorislav Petkov 	setup_inj_struct(&i_mce);
77821afaf18SBorislav Petkov 
77921afaf18SBorislav Petkov 	pr_info("Machine check injector initialized\n");
78021afaf18SBorislav Petkov 
78121afaf18SBorislav Petkov 	return 0;
78221afaf18SBorislav Petkov }
78321afaf18SBorislav Petkov 
78421afaf18SBorislav Petkov static void __exit inject_exit(void)
78521afaf18SBorislav Petkov {
78621afaf18SBorislav Petkov 
78721afaf18SBorislav Petkov 	mce_unregister_injector_chain(&inject_nb);
78821afaf18SBorislav Petkov 	unregister_nmi_handler(NMI_LOCAL, "mce_notify");
78921afaf18SBorislav Petkov 
79021afaf18SBorislav Petkov 	debugfs_remove_recursive(dfs_inj);
79121afaf18SBorislav Petkov 	dfs_inj = NULL;
79221afaf18SBorislav Petkov 
79321afaf18SBorislav Petkov 	memset(&dfs_fls, 0, sizeof(dfs_fls));
79421afaf18SBorislav Petkov 
79521afaf18SBorislav Petkov 	free_cpumask_var(mce_inject_cpumask);
79621afaf18SBorislav Petkov }
79721afaf18SBorislav Petkov 
79821afaf18SBorislav Petkov module_init(inject_init);
79921afaf18SBorislav Petkov module_exit(inject_exit);
80021afaf18SBorislav Petkov MODULE_LICENSE("GPL");
801