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");