2 * A simple MCE injection facility for testing different aspects of the RAS
3 * code. This driver should be built as module so that it can be loaded
4 * on production kernels for testing purposes.
6 * This file may be distributed under the terms of the GNU General Public
9 * Copyright (c) 2010-14: Borislav Petkov <bp@alien8.de>
10 * Advanced Micro Devices Inc.
13 #include <linux/kobject.h>
14 #include <linux/debugfs.h>
15 #include <linux/device.h>
16 #include <linux/module.h>
17 #include <linux/cpu.h>
23 * Collect all the MCi_XXX settings
25 static struct mce i_mce;
26 static struct dentry *dfs_inj;
28 #define MCE_INJECT_SET(reg) \
29 static int inj_##reg##_set(void *data, u64 val) \
31 struct mce *m = (struct mce *)data; \
37 MCE_INJECT_SET(status);
41 #define MCE_INJECT_GET(reg) \
42 static int inj_##reg##_get(void *data, u64 *val) \
44 struct mce *m = (struct mce *)data; \
50 MCE_INJECT_GET(status);
54 DEFINE_SIMPLE_ATTRIBUTE(status_fops, inj_status_get, inj_status_set, "%llx\n");
55 DEFINE_SIMPLE_ATTRIBUTE(misc_fops, inj_misc_get, inj_misc_set, "%llx\n");
56 DEFINE_SIMPLE_ATTRIBUTE(addr_fops, inj_addr_get, inj_addr_set, "%llx\n");
59 * Caller needs to be make sure this cpu doesn't disappear
60 * from under us, i.e.: get_cpu/put_cpu.
62 static int toggle_hw_mce_inject(unsigned int cpu, bool enable)
67 err = rdmsr_on_cpu(cpu, MSR_K7_HWCR, &l, &h);
69 pr_err("%s: error reading HWCR\n", __func__);
73 enable ? (l |= BIT(18)) : (l &= ~BIT(18));
75 err = wrmsr_on_cpu(cpu, MSR_K7_HWCR, l, h);
77 pr_err("%s: error writing HWCR\n", __func__);
82 static int flags_get(void *data, u64 *val)
84 struct mce *m = (struct mce *)data;
86 *val = m->inject_flags;
91 static int flags_set(void *data, u64 val)
93 struct mce *m = (struct mce *)data;
95 m->inject_flags = (u8)val;
99 DEFINE_SIMPLE_ATTRIBUTE(flags_fops, flags_get, flags_set, "%llu\n");
102 * On which CPU to inject?
104 MCE_INJECT_GET(extcpu);
106 static int inj_extcpu_set(void *data, u64 val)
108 struct mce *m = (struct mce *)data;
110 if (val >= nr_cpu_ids || !cpu_online(val)) {
111 pr_err("%s: Invalid CPU: %llu\n", __func__, val);
118 DEFINE_SIMPLE_ATTRIBUTE(extcpu_fops, inj_extcpu_get, inj_extcpu_set, "%llu\n");
120 static void trigger_mce(void *info)
122 asm volatile("int $18");
125 static void do_inject(void)
128 unsigned int cpu = i_mce.extcpu;
131 if (!(i_mce.inject_flags & MCJ_EXCEPTION)) {
132 amd_decode_mce(NULL, 0, &i_mce);
137 if (!cpu_online(cpu))
140 /* prep MCE global settings for the injection */
141 mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV;
143 if (!(i_mce.status & MCI_STATUS_PCC))
144 mcg_status |= MCG_STATUS_RIPV;
146 toggle_hw_mce_inject(cpu, true);
148 wrmsr_on_cpu(cpu, MSR_IA32_MCG_STATUS,
149 (u32)mcg_status, (u32)(mcg_status >> 32));
151 wrmsr_on_cpu(cpu, MSR_IA32_MCx_STATUS(b),
152 (u32)i_mce.status, (u32)(i_mce.status >> 32));
154 wrmsr_on_cpu(cpu, MSR_IA32_MCx_ADDR(b),
155 (u32)i_mce.addr, (u32)(i_mce.addr >> 32));
157 wrmsr_on_cpu(cpu, MSR_IA32_MCx_MISC(b),
158 (u32)i_mce.misc, (u32)(i_mce.misc >> 32));
160 toggle_hw_mce_inject(cpu, false);
162 smp_call_function_single(cpu, trigger_mce, NULL, 0);
170 * This denotes into which bank we're injecting and triggers
171 * the injection, at the same time.
173 static int inj_bank_set(void *data, u64 val)
175 struct mce *m = (struct mce *)data;
178 if (boot_cpu_data.x86 != 0x15 || val > 6) {
179 pr_err("Non-existent MCE bank: %llu\n", val);
190 static int inj_bank_get(void *data, u64 *val)
192 struct mce *m = (struct mce *)data;
198 DEFINE_SIMPLE_ATTRIBUTE(bank_fops, inj_bank_get, inj_bank_set, "%llu\n");
200 static struct dfs_node {
203 const struct file_operations *fops;
205 { .name = "status", .fops = &status_fops },
206 { .name = "misc", .fops = &misc_fops },
207 { .name = "addr", .fops = &addr_fops },
208 { .name = "bank", .fops = &bank_fops },
209 { .name = "flags", .fops = &flags_fops },
210 { .name = "cpu", .fops = &extcpu_fops },
213 static int __init init_mce_inject(void)
217 dfs_inj = debugfs_create_dir("mce-inject", NULL);
221 for (i = 0; i < ARRAY_SIZE(dfs_fls); i++) {
222 dfs_fls[i].d = debugfs_create_file(dfs_fls[i].name,
236 debugfs_remove(dfs_fls[i].d);
238 debugfs_remove(dfs_inj);
244 static void __exit exit_mce_inject(void)
248 for (i = 0; i < ARRAY_SIZE(dfs_fls); i++)
249 debugfs_remove(dfs_fls[i].d);
251 memset(&dfs_fls, 0, sizeof(dfs_fls));
253 debugfs_remove(dfs_inj);
256 module_init(init_mce_inject);
257 module_exit(exit_mce_inject);
259 MODULE_LICENSE("GPL");
260 MODULE_AUTHOR("Borislav Petkov <bp@alien8.de>");
261 MODULE_AUTHOR("AMD Inc.");
262 MODULE_DESCRIPTION("MCE injection facility for RAS testing");