x86/ras/mce_amd_inj: Trigger deferred and thresholding errors interrupts
[linux-2.6-block.git] / arch / x86 / ras / mce_amd_inj.c
CommitLineData
9cdeb404 1/*
fd19fcd6
BP
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.
9cdeb404
BP
5 *
6 * This file may be distributed under the terms of the GNU General Public
7 * License version 2.
8 *
6c36dfe9 9 * Copyright (c) 2010-15: Borislav Petkov <bp@alien8.de>
9cdeb404
BP
10 * Advanced Micro Devices Inc.
11 */
12
13#include <linux/kobject.h>
fd19fcd6 14#include <linux/debugfs.h>
51990e82 15#include <linux/device.h>
80a2e2e3 16#include <linux/module.h>
51756a50 17#include <linux/cpu.h>
0451d14d
AG
18#include <linux/string.h>
19#include <linux/uaccess.h>
a1300e50 20
9cdeb404 21#include <asm/mce.h>
a1300e50 22#include <asm/irq_vectors.h>
9cdeb404 23
6c36dfe9 24#include "../kernel/cpu/mcheck/mce-internal.h"
9cdeb404 25
9cdeb404
BP
26/*
27 * Collect all the MCi_XXX settings
28 */
29static struct mce i_mce;
fd19fcd6 30static struct dentry *dfs_inj;
9cdeb404 31
685d46d7
AG
32static u8 n_banks;
33
0451d14d
AG
34#define MAX_FLAG_OPT_SIZE 3
35
36enum injection_type {
37 SW_INJ = 0, /* SW injection, simply decode the error */
38 HW_INJ, /* Trigger a #MC */
a1300e50
AG
39 DFR_INT_INJ, /* Trigger Deferred error interrupt */
40 THR_INT_INJ, /* Trigger threshold interrupt */
0451d14d
AG
41 N_INJ_TYPES,
42};
43
44static const char * const flags_options[] = {
45 [SW_INJ] = "sw",
46 [HW_INJ] = "hw",
a1300e50
AG
47 [DFR_INT_INJ] = "df",
48 [THR_INT_INJ] = "th",
0451d14d
AG
49 NULL
50};
51
52/* Set default injection to SW_INJ */
de277678 53static enum injection_type inj_type = SW_INJ;
0451d14d 54
fd19fcd6
BP
55#define MCE_INJECT_SET(reg) \
56static int inj_##reg##_set(void *data, u64 val) \
9cdeb404 57{ \
fd19fcd6 58 struct mce *m = (struct mce *)data; \
9cdeb404 59 \
fd19fcd6
BP
60 m->reg = val; \
61 return 0; \
9cdeb404
BP
62}
63
fd19fcd6
BP
64MCE_INJECT_SET(status);
65MCE_INJECT_SET(misc);
66MCE_INJECT_SET(addr);
9cdeb404 67
fd19fcd6
BP
68#define MCE_INJECT_GET(reg) \
69static int inj_##reg##_get(void *data, u64 *val) \
9cdeb404 70{ \
fd19fcd6
BP
71 struct mce *m = (struct mce *)data; \
72 \
73 *val = m->reg; \
74 return 0; \
9cdeb404
BP
75}
76
fd19fcd6
BP
77MCE_INJECT_GET(status);
78MCE_INJECT_GET(misc);
79MCE_INJECT_GET(addr);
9cdeb404 80
fd19fcd6
BP
81DEFINE_SIMPLE_ATTRIBUTE(status_fops, inj_status_get, inj_status_set, "%llx\n");
82DEFINE_SIMPLE_ATTRIBUTE(misc_fops, inj_misc_get, inj_misc_set, "%llx\n");
83DEFINE_SIMPLE_ATTRIBUTE(addr_fops, inj_addr_get, inj_addr_set, "%llx\n");
9cdeb404 84
21690934
BP
85/*
86 * Caller needs to be make sure this cpu doesn't disappear
87 * from under us, i.e.: get_cpu/put_cpu.
88 */
89static int toggle_hw_mce_inject(unsigned int cpu, bool enable)
90{
91 u32 l, h;
92 int err;
93
94 err = rdmsr_on_cpu(cpu, MSR_K7_HWCR, &l, &h);
95 if (err) {
96 pr_err("%s: error reading HWCR\n", __func__);
97 return err;
98 }
99
100 enable ? (l |= BIT(18)) : (l &= ~BIT(18));
101
102 err = wrmsr_on_cpu(cpu, MSR_K7_HWCR, l, h);
103 if (err)
104 pr_err("%s: error writing HWCR\n", __func__);
105
106 return err;
107}
108
0451d14d 109static int __set_inj(const char *buf)
b18f3864 110{
0451d14d
AG
111 int i;
112
113 for (i = 0; i < N_INJ_TYPES; i++) {
114 if (!strncmp(flags_options[i], buf, strlen(flags_options[i]))) {
115 inj_type = i;
116 return 0;
117 }
118 }
119 return -EINVAL;
120}
121
122static ssize_t flags_read(struct file *filp, char __user *ubuf,
123 size_t cnt, loff_t *ppos)
124{
125 char buf[MAX_FLAG_OPT_SIZE];
126 int n;
b18f3864 127
0451d14d 128 n = sprintf(buf, "%s\n", flags_options[inj_type]);
b18f3864 129
0451d14d 130 return simple_read_from_buffer(ubuf, cnt, ppos, buf, n);
b18f3864
BP
131}
132
0451d14d
AG
133static ssize_t flags_write(struct file *filp, const char __user *ubuf,
134 size_t cnt, loff_t *ppos)
b18f3864 135{
0451d14d
AG
136 char buf[MAX_FLAG_OPT_SIZE], *__buf;
137 int err;
b18f3864 138
0451d14d 139 if (cnt > MAX_FLAG_OPT_SIZE)
85c9306d 140 return -EINVAL;
0451d14d
AG
141
142 if (copy_from_user(&buf, ubuf, cnt))
143 return -EFAULT;
144
145 buf[cnt - 1] = 0;
146
147 /* strip whitespace */
148 __buf = strstrip(buf);
149
150 err = __set_inj(__buf);
151 if (err) {
152 pr_err("%s: Invalid flags value: %s\n", __func__, __buf);
153 return err;
154 }
155
85c9306d 156 *ppos += cnt;
0451d14d 157
85c9306d 158 return cnt;
b18f3864
BP
159}
160
0451d14d
AG
161static const struct file_operations flags_fops = {
162 .read = flags_read,
163 .write = flags_write,
164 .llseek = generic_file_llseek,
165};
b18f3864
BP
166
167/*
168 * On which CPU to inject?
169 */
170MCE_INJECT_GET(extcpu);
171
172static int inj_extcpu_set(void *data, u64 val)
173{
174 struct mce *m = (struct mce *)data;
175
176 if (val >= nr_cpu_ids || !cpu_online(val)) {
177 pr_err("%s: Invalid CPU: %llu\n", __func__, val);
178 return -EINVAL;
179 }
180 m->extcpu = val;
181 return 0;
182}
183
184DEFINE_SIMPLE_ATTRIBUTE(extcpu_fops, inj_extcpu_get, inj_extcpu_set, "%llu\n");
185
51756a50
BP
186static void trigger_mce(void *info)
187{
188 asm volatile("int $18");
189}
190
a1300e50
AG
191static void trigger_dfr_int(void *info)
192{
193 asm volatile("int %0" :: "i" (DEFERRED_ERROR_VECTOR));
194}
195
196static void trigger_thr_int(void *info)
197{
198 asm volatile("int %0" :: "i" (THRESHOLD_APIC_VECTOR));
199}
200
51756a50
BP
201static void do_inject(void)
202{
203 u64 mcg_status = 0;
204 unsigned int cpu = i_mce.extcpu;
205 u8 b = i_mce.bank;
206
cda9459d
BP
207 if (i_mce.misc)
208 i_mce.status |= MCI_STATUS_MISCV;
209
0451d14d 210 if (inj_type == SW_INJ) {
6c36dfe9 211 mce_inject_log(&i_mce);
51756a50
BP
212 return;
213 }
214
51756a50
BP
215 /* prep MCE global settings for the injection */
216 mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV;
217
218 if (!(i_mce.status & MCI_STATUS_PCC))
219 mcg_status |= MCG_STATUS_RIPV;
220
a1300e50
AG
221 /*
222 * Ensure necessary status bits for deferred errors:
223 * - MCx_STATUS[Deferred]: make sure it is a deferred error
224 * - MCx_STATUS[UC] cleared: deferred errors are _not_ UC
225 */
226 if (inj_type == DFR_INT_INJ) {
227 i_mce.status |= MCI_STATUS_DEFERRED;
228 i_mce.status |= (i_mce.status & ~MCI_STATUS_UC);
229 }
230
6d1e9bf5
BP
231 get_online_cpus();
232 if (!cpu_online(cpu))
233 goto err;
234
51756a50
BP
235 toggle_hw_mce_inject(cpu, true);
236
237 wrmsr_on_cpu(cpu, MSR_IA32_MCG_STATUS,
238 (u32)mcg_status, (u32)(mcg_status >> 32));
239
240 wrmsr_on_cpu(cpu, MSR_IA32_MCx_STATUS(b),
241 (u32)i_mce.status, (u32)(i_mce.status >> 32));
242
243 wrmsr_on_cpu(cpu, MSR_IA32_MCx_ADDR(b),
244 (u32)i_mce.addr, (u32)(i_mce.addr >> 32));
245
246 wrmsr_on_cpu(cpu, MSR_IA32_MCx_MISC(b),
247 (u32)i_mce.misc, (u32)(i_mce.misc >> 32));
248
249 toggle_hw_mce_inject(cpu, false);
250
a1300e50
AG
251 switch (inj_type) {
252 case DFR_INT_INJ:
253 smp_call_function_single(cpu, trigger_dfr_int, NULL, 0);
254 break;
255 case THR_INT_INJ:
256 smp_call_function_single(cpu, trigger_thr_int, NULL, 0);
257 break;
258 default:
259 smp_call_function_single(cpu, trigger_mce, NULL, 0);
260 }
51756a50
BP
261
262err:
263 put_online_cpus();
264
265}
266
9cdeb404
BP
267/*
268 * This denotes into which bank we're injecting and triggers
269 * the injection, at the same time.
270 */
fd19fcd6 271static int inj_bank_set(void *data, u64 val)
9cdeb404 272{
fd19fcd6 273 struct mce *m = (struct mce *)data;
9cdeb404 274
685d46d7
AG
275 if (val >= n_banks) {
276 pr_err("Non-existent MCE bank: %llu\n", val);
277 return -EINVAL;
fd19fcd6 278 }
9cdeb404 279
fd19fcd6 280 m->bank = val;
51756a50 281 do_inject();
9cdeb404 282
fd19fcd6 283 return 0;
9cdeb404
BP
284}
285
e7f2ea1d 286MCE_INJECT_GET(bank);
9cdeb404 287
fd19fcd6
BP
288DEFINE_SIMPLE_ATTRIBUTE(bank_fops, inj_bank_get, inj_bank_set, "%llu\n");
289
99e21fea 290static const char readme_msg[] =
f2f3dca1
BP
291"Description of the files and their usages:\n"
292"\n"
293"Note1: i refers to the bank number below.\n"
294"Note2: See respective BKDGs for the exact bit definitions of the files below\n"
295"as they mirror the hardware registers.\n"
296"\n"
297"status:\t Set MCi_STATUS: the bits in that MSR control the error type and\n"
298"\t attributes of the error which caused the MCE.\n"
299"\n"
300"misc:\t Set MCi_MISC: provide auxiliary info about the error. It is mostly\n"
301"\t used for error thresholding purposes and its validity is indicated by\n"
302"\t MCi_STATUS[MiscV].\n"
303"\n"
304"addr:\t Error address value to be written to MCi_ADDR. Log address information\n"
305"\t associated with the error.\n"
306"\n"
307"cpu:\t The CPU to inject the error on.\n"
308"\n"
309"bank:\t Specify the bank you want to inject the error into: the number of\n"
310"\t banks in a processor varies and is family/model-specific, therefore, the\n"
311"\t supplied value is sanity-checked. Setting the bank value also triggers the\n"
312"\t injection.\n"
313"\n"
314"flags:\t Injection type to be performed. Writing to this file will trigger a\n"
315"\t real machine check, an APIC interrupt or invoke the error decoder routines\n"
316"\t for AMD processors.\n"
317"\n"
318"\t Allowed error injection types:\n"
319"\t - \"sw\": Software error injection. Decode error to a human-readable \n"
320"\t format only. Safe to use.\n"
321"\t - \"hw\": Hardware error injection. Causes the #MC exception handler to \n"
322"\t handle the error. Be warned: might cause system panic if MCi_STATUS[PCC] \n"
323"\t is set. Therefore, consider setting (debugfs_mountpoint)/mce/fake_panic \n"
324"\t before injecting.\n"
a1300e50
AG
325"\t - \"df\": Trigger APIC interrupt for Deferred error. Causes deferred \n"
326"\t error APIC interrupt handler to handle the error if the feature is \n"
327"\t is present in hardware. \n"
328"\t - \"th\": Trigger APIC interrupt for Threshold errors. Causes threshold \n"
329"\t APIC interrupt handler to handle the error. \n"
f2f3dca1 330"\n";
99e21fea
AG
331
332static ssize_t
333inj_readme_read(struct file *filp, char __user *ubuf,
334 size_t cnt, loff_t *ppos)
335{
336 return simple_read_from_buffer(ubuf, cnt, ppos,
337 readme_msg, strlen(readme_msg));
338}
339
340static const struct file_operations readme_fops = {
341 .read = inj_readme_read,
342};
343
8c2b117f 344static struct dfs_node {
fd19fcd6
BP
345 char *name;
346 struct dentry *d;
347 const struct file_operations *fops;
4c6034e8 348 umode_t perm;
fd19fcd6 349} dfs_fls[] = {
4c6034e8
AG
350 { .name = "status", .fops = &status_fops, .perm = S_IRUSR | S_IWUSR },
351 { .name = "misc", .fops = &misc_fops, .perm = S_IRUSR | S_IWUSR },
352 { .name = "addr", .fops = &addr_fops, .perm = S_IRUSR | S_IWUSR },
353 { .name = "bank", .fops = &bank_fops, .perm = S_IRUSR | S_IWUSR },
354 { .name = "flags", .fops = &flags_fops, .perm = S_IRUSR | S_IWUSR },
355 { .name = "cpu", .fops = &extcpu_fops, .perm = S_IRUSR | S_IWUSR },
99e21fea 356 { .name = "README", .fops = &readme_fops, .perm = S_IRUSR | S_IRGRP | S_IROTH },
9cdeb404
BP
357};
358
fd19fcd6 359static int __init init_mce_inject(void)
9cdeb404 360{
fd19fcd6 361 int i;
685d46d7
AG
362 u64 cap;
363
364 rdmsrl(MSR_IA32_MCG_CAP, cap);
365 n_banks = cap & MCG_BANKCNT_MASK;
9cdeb404 366
fd19fcd6
BP
367 dfs_inj = debugfs_create_dir("mce-inject", NULL);
368 if (!dfs_inj)
9cdeb404
BP
369 return -EINVAL;
370
fd19fcd6
BP
371 for (i = 0; i < ARRAY_SIZE(dfs_fls); i++) {
372 dfs_fls[i].d = debugfs_create_file(dfs_fls[i].name,
4c6034e8 373 dfs_fls[i].perm,
fd19fcd6
BP
374 dfs_inj,
375 &i_mce,
376 dfs_fls[i].fops);
9cdeb404 377
fd19fcd6
BP
378 if (!dfs_fls[i].d)
379 goto err_dfs_add;
9cdeb404 380 }
fd19fcd6 381
9cdeb404
BP
382 return 0;
383
fd19fcd6 384err_dfs_add:
df4b2a30 385 while (--i >= 0)
fd19fcd6 386 debugfs_remove(dfs_fls[i].d);
9cdeb404 387
fd19fcd6
BP
388 debugfs_remove(dfs_inj);
389 dfs_inj = NULL;
9cdeb404 390
fd19fcd6 391 return -ENOMEM;
9cdeb404
BP
392}
393
fd19fcd6 394static void __exit exit_mce_inject(void)
9cdeb404
BP
395{
396 int i;
397
fd19fcd6
BP
398 for (i = 0; i < ARRAY_SIZE(dfs_fls); i++)
399 debugfs_remove(dfs_fls[i].d);
9cdeb404 400
fd19fcd6 401 memset(&dfs_fls, 0, sizeof(dfs_fls));
9cdeb404 402
fd19fcd6
BP
403 debugfs_remove(dfs_inj);
404 dfs_inj = NULL;
9cdeb404 405}
fd19fcd6
BP
406module_init(init_mce_inject);
407module_exit(exit_mce_inject);
9cdeb404
BP
408
409MODULE_LICENSE("GPL");
43aff26c 410MODULE_AUTHOR("Borislav Petkov <bp@alien8.de>");
9cdeb404 411MODULE_AUTHOR("AMD Inc.");
fd19fcd6 412MODULE_DESCRIPTION("MCE injection facility for RAS testing");