xref: /linux/arch/x86/kernel/cpu/mce/inject.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
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(&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 
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);
491ede18982SYazen Ghannam 
492ede18982SYazen Ghannam 		if (m.misc)
493ede18982SYazen Ghannam 			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);
497ede18982SYazen Ghannam 
498ede18982SYazen Ghannam 		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;
50721afaf18SBorislav Petkov 	u8 b = i_mce.bank;
50821afaf18SBorislav Petkov 
50921afaf18SBorislav Petkov 	i_mce.tsc = rdtsc_ordered();
51021afaf18SBorislav Petkov 
5111e56279aSSmita Koralahalli 	i_mce.status |= MCI_STATUS_VAL;
5121e56279aSSmita Koralahalli 
51321afaf18SBorislav Petkov 	if (i_mce.misc)
51421afaf18SBorislav Petkov 		i_mce.status |= MCI_STATUS_MISCV;
51521afaf18SBorislav Petkov 
51621afaf18SBorislav Petkov 	if (i_mce.synd)
51721afaf18SBorislav Petkov 		i_mce.status |= MCI_STATUS_SYNDV;
51821afaf18SBorislav Petkov 
51921afaf18SBorislav Petkov 	if (inj_type == SW_INJ) {
52081736abdSJan H. Schönherr 		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
53321afaf18SBorislav Petkov 	 * - 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;
5375d7f7d1dSZhenzhong Duan 		i_mce.status &= ~MCI_STATUS_UC;
53821afaf18SBorislav Petkov 	}
53921afaf18SBorislav Petkov 
54021afaf18SBorislav Petkov 	/*
54121afaf18SBorislav 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.
54421afaf18SBorislav Petkov 	 */
54567e87d43SBorislav Petkov 	if (boot_cpu_has(X86_FEATURE_AMD_DCM) &&
54621afaf18SBorislav Petkov 	    b == 4 &&
54721afaf18SBorislav Petkov 	    boot_cpu_data.x86 < 0x17) {
5487e3ec628SThomas Gleixner 		toggle_nb_mca_mst_cpu(topology_amd_node_id(cpu));
5497e3ec628SThomas Gleixner 		cpu = get_nbc_for_node(topology_amd_node_id(cpu));
55021afaf18SBorislav Petkov 	}
55121afaf18SBorislav Petkov 
5528ae9e3f6SSebastian Andrzej Siewior 	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:
57221afaf18SBorislav Petkov 		smp_call_function_single(cpu, trigger_mce, NULL, 0);
57321afaf18SBorislav Petkov 	}
57421afaf18SBorislav Petkov 
57521afaf18SBorislav Petkov err:
5768ae9e3f6SSebastian Andrzej Siewior 	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.
58321afaf18SBorislav Petkov  */
inj_bank_set(void * data,u64 val)58421afaf18SBorislav Petkov static int inj_bank_set(void *data, u64 val)
58521afaf18SBorislav Petkov {
58621afaf18SBorislav Petkov 	struct mce *m = (struct mce *)data;
587006c0770SYazen Ghannam 	u8 n_banks;
588006c0770SYazen Ghannam 	u64 cap;
589006c0770SYazen Ghannam 
590006c0770SYazen Ghannam 	/* 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);
592006c0770SYazen Ghannam 	n_banks = cap & MCG_BANKCNT_MASK;
59321afaf18SBorislav Petkov 
59421afaf18SBorislav Petkov 	if (val >= n_banks) {
595006c0770SYazen Ghannam 		pr_err("MCA bank %llu non-existent on CPU%d\n", val, m->extcpu);
59621afaf18SBorislav Petkov 		return -EINVAL;
59721afaf18SBorislav Petkov 	}
59821afaf18SBorislav Petkov 
59921afaf18SBorislav Petkov 	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;
623e48d008bSSmita Koralahalli 		}
624e48d008bSSmita Koralahalli 	}
625e48d008bSSmita Koralahalli 
626e48d008bSSmita Koralahalli 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"
67821afaf18SBorislav Petkov "\t    error APIC interrupt handler to handle the error if the feature is \n"
67921afaf18SBorislav 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"
6822ffdc2c3SBorislav Petkov "\n"
6832ffdc2c3SBorislav 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[] = {
70321afaf18SBorislav 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 },
7072ffdc2c3SBorislav 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 },
71021afaf18SBorislav Petkov 	{ .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)7146e4f929eSGreg Kroah-Hartman static void __init debugfs_init(void)
71521afaf18SBorislav Petkov {
71621afaf18SBorislav Petkov 	unsigned int i;
71721afaf18SBorislav Petkov 
71821afaf18SBorislav Petkov 	dfs_inj = debugfs_create_dir("mce-inject", NULL);
71921afaf18SBorislav Petkov 
7206e4f929eSGreg Kroah-Hartman 	for (i = 0; i < ARRAY_SIZE(dfs_fls); i++)
7216e4f929eSGreg Kroah-Hartman 		debugfs_create_file(dfs_fls[i].name, dfs_fls[i].perm, dfs_inj,
7226e4f929eSGreg Kroah-Hartman 				    &i_mce, dfs_fls[i].fops);
72321afaf18SBorislav Petkov }
72421afaf18SBorislav Petkov 
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 
747891e465aSSmita Koralahalli 		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);
7516175b407SYazen Ghannam 		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 	}
763891e465aSSmita Koralahalli 
764891e465aSSmita Koralahalli 	put_cpu();
765891e465aSSmita Koralahalli }
766891e465aSSmita Koralahalli 
inject_init(void)76721afaf18SBorislav Petkov static int __init inject_init(void)
76821afaf18SBorislav Petkov {
76921afaf18SBorislav Petkov 	if (!alloc_cpumask_var(&mce_inject_cpumask, GFP_KERNEL))
77021afaf18SBorislav Petkov 		return -ENOMEM;
77121afaf18SBorislav Petkov 
772891e465aSSmita Koralahalli 	check_hw_inj_possible();
773891e465aSSmita Koralahalli 
7746e4f929eSGreg Kroah-Hartman 	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);
79821afaf18SBorislav Petkov }
79921afaf18SBorislav Petkov 
80021afaf18SBorislav Petkov module_init(inject_init);
80121afaf18SBorislav Petkov module_exit(inject_exit);
802*eb9d3c0bSJeff Johnson MODULE_DESCRIPTION("Machine check injection support");
80321afaf18SBorislav Petkov MODULE_LICENSE("GPL");
804