Linux 4.2.1
[linux/fpc-iii.git] / drivers / edac / mce_amd_inj.c
blob4c73e4d03d465c14d206b5e66955cc22ddce2986
1 /*
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
7 * License version 2.
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>
18 #include <linux/string.h>
19 #include <linux/uaccess.h>
20 #include <asm/mce.h>
22 #include "mce_amd.h"
25 * Collect all the MCi_XXX settings
27 static struct mce i_mce;
28 static struct dentry *dfs_inj;
30 static u8 n_banks;
32 #define MAX_FLAG_OPT_SIZE 3
34 enum injection_type {
35 SW_INJ = 0, /* SW injection, simply decode the error */
36 HW_INJ, /* Trigger a #MC */
37 N_INJ_TYPES,
40 static const char * const flags_options[] = {
41 [SW_INJ] = "sw",
42 [HW_INJ] = "hw",
43 NULL
46 /* Set default injection to SW_INJ */
47 static enum injection_type inj_type = SW_INJ;
49 #define MCE_INJECT_SET(reg) \
50 static int inj_##reg##_set(void *data, u64 val) \
51 { \
52 struct mce *m = (struct mce *)data; \
54 m->reg = val; \
55 return 0; \
58 MCE_INJECT_SET(status);
59 MCE_INJECT_SET(misc);
60 MCE_INJECT_SET(addr);
62 #define MCE_INJECT_GET(reg) \
63 static int inj_##reg##_get(void *data, u64 *val) \
64 { \
65 struct mce *m = (struct mce *)data; \
67 *val = m->reg; \
68 return 0; \
71 MCE_INJECT_GET(status);
72 MCE_INJECT_GET(misc);
73 MCE_INJECT_GET(addr);
75 DEFINE_SIMPLE_ATTRIBUTE(status_fops, inj_status_get, inj_status_set, "%llx\n");
76 DEFINE_SIMPLE_ATTRIBUTE(misc_fops, inj_misc_get, inj_misc_set, "%llx\n");
77 DEFINE_SIMPLE_ATTRIBUTE(addr_fops, inj_addr_get, inj_addr_set, "%llx\n");
80 * Caller needs to be make sure this cpu doesn't disappear
81 * from under us, i.e.: get_cpu/put_cpu.
83 static int toggle_hw_mce_inject(unsigned int cpu, bool enable)
85 u32 l, h;
86 int err;
88 err = rdmsr_on_cpu(cpu, MSR_K7_HWCR, &l, &h);
89 if (err) {
90 pr_err("%s: error reading HWCR\n", __func__);
91 return err;
94 enable ? (l |= BIT(18)) : (l &= ~BIT(18));
96 err = wrmsr_on_cpu(cpu, MSR_K7_HWCR, l, h);
97 if (err)
98 pr_err("%s: error writing HWCR\n", __func__);
100 return err;
103 static int __set_inj(const char *buf)
105 int i;
107 for (i = 0; i < N_INJ_TYPES; i++) {
108 if (!strncmp(flags_options[i], buf, strlen(flags_options[i]))) {
109 inj_type = i;
110 return 0;
113 return -EINVAL;
116 static ssize_t flags_read(struct file *filp, char __user *ubuf,
117 size_t cnt, loff_t *ppos)
119 char buf[MAX_FLAG_OPT_SIZE];
120 int n;
122 n = sprintf(buf, "%s\n", flags_options[inj_type]);
124 return simple_read_from_buffer(ubuf, cnt, ppos, buf, n);
127 static ssize_t flags_write(struct file *filp, const char __user *ubuf,
128 size_t cnt, loff_t *ppos)
130 char buf[MAX_FLAG_OPT_SIZE], *__buf;
131 int err;
132 size_t ret;
134 if (cnt > MAX_FLAG_OPT_SIZE)
135 cnt = MAX_FLAG_OPT_SIZE;
137 ret = cnt;
139 if (copy_from_user(&buf, ubuf, cnt))
140 return -EFAULT;
142 buf[cnt - 1] = 0;
144 /* strip whitespace */
145 __buf = strstrip(buf);
147 err = __set_inj(__buf);
148 if (err) {
149 pr_err("%s: Invalid flags value: %s\n", __func__, __buf);
150 return err;
153 *ppos += ret;
155 return ret;
158 static const struct file_operations flags_fops = {
159 .read = flags_read,
160 .write = flags_write,
161 .llseek = generic_file_llseek,
165 * On which CPU to inject?
167 MCE_INJECT_GET(extcpu);
169 static int inj_extcpu_set(void *data, u64 val)
171 struct mce *m = (struct mce *)data;
173 if (val >= nr_cpu_ids || !cpu_online(val)) {
174 pr_err("%s: Invalid CPU: %llu\n", __func__, val);
175 return -EINVAL;
177 m->extcpu = val;
178 return 0;
181 DEFINE_SIMPLE_ATTRIBUTE(extcpu_fops, inj_extcpu_get, inj_extcpu_set, "%llu\n");
183 static void trigger_mce(void *info)
185 asm volatile("int $18");
188 static void do_inject(void)
190 u64 mcg_status = 0;
191 unsigned int cpu = i_mce.extcpu;
192 u8 b = i_mce.bank;
194 if (i_mce.misc)
195 i_mce.status |= MCI_STATUS_MISCV;
197 if (inj_type == SW_INJ) {
198 amd_decode_mce(NULL, 0, &i_mce);
199 return;
202 /* prep MCE global settings for the injection */
203 mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV;
205 if (!(i_mce.status & MCI_STATUS_PCC))
206 mcg_status |= MCG_STATUS_RIPV;
208 get_online_cpus();
209 if (!cpu_online(cpu))
210 goto err;
212 toggle_hw_mce_inject(cpu, true);
214 wrmsr_on_cpu(cpu, MSR_IA32_MCG_STATUS,
215 (u32)mcg_status, (u32)(mcg_status >> 32));
217 wrmsr_on_cpu(cpu, MSR_IA32_MCx_STATUS(b),
218 (u32)i_mce.status, (u32)(i_mce.status >> 32));
220 wrmsr_on_cpu(cpu, MSR_IA32_MCx_ADDR(b),
221 (u32)i_mce.addr, (u32)(i_mce.addr >> 32));
223 wrmsr_on_cpu(cpu, MSR_IA32_MCx_MISC(b),
224 (u32)i_mce.misc, (u32)(i_mce.misc >> 32));
226 toggle_hw_mce_inject(cpu, false);
228 smp_call_function_single(cpu, trigger_mce, NULL, 0);
230 err:
231 put_online_cpus();
236 * This denotes into which bank we're injecting and triggers
237 * the injection, at the same time.
239 static int inj_bank_set(void *data, u64 val)
241 struct mce *m = (struct mce *)data;
243 if (val >= n_banks) {
244 pr_err("Non-existent MCE bank: %llu\n", val);
245 return -EINVAL;
248 m->bank = val;
249 do_inject();
251 return 0;
254 MCE_INJECT_GET(bank);
256 DEFINE_SIMPLE_ATTRIBUTE(bank_fops, inj_bank_get, inj_bank_set, "%llu\n");
258 static const char readme_msg[] =
259 "Description of the files and their usages:\n"
260 "\n"
261 "Note1: i refers to the bank number below.\n"
262 "Note2: See respective BKDGs for the exact bit definitions of the files below\n"
263 "as they mirror the hardware registers.\n"
264 "\n"
265 "status:\t Set MCi_STATUS: the bits in that MSR control the error type and\n"
266 "\t attributes of the error which caused the MCE.\n"
267 "\n"
268 "misc:\t Set MCi_MISC: provide auxiliary info about the error. It is mostly\n"
269 "\t used for error thresholding purposes and its validity is indicated by\n"
270 "\t MCi_STATUS[MiscV].\n"
271 "\n"
272 "addr:\t Error address value to be written to MCi_ADDR. Log address information\n"
273 "\t associated with the error.\n"
274 "\n"
275 "cpu:\t The CPU to inject the error on.\n"
276 "\n"
277 "bank:\t Specify the bank you want to inject the error into: the number of\n"
278 "\t banks in a processor varies and is family/model-specific, therefore, the\n"
279 "\t supplied value is sanity-checked. Setting the bank value also triggers the\n"
280 "\t injection.\n"
281 "\n"
282 "flags:\t Injection type to be performed. Writing to this file will trigger a\n"
283 "\t real machine check, an APIC interrupt or invoke the error decoder routines\n"
284 "\t for AMD processors.\n"
285 "\n"
286 "\t Allowed error injection types:\n"
287 "\t - \"sw\": Software error injection. Decode error to a human-readable \n"
288 "\t format only. Safe to use.\n"
289 "\t - \"hw\": Hardware error injection. Causes the #MC exception handler to \n"
290 "\t handle the error. Be warned: might cause system panic if MCi_STATUS[PCC] \n"
291 "\t is set. Therefore, consider setting (debugfs_mountpoint)/mce/fake_panic \n"
292 "\t before injecting.\n"
293 "\n";
295 static ssize_t
296 inj_readme_read(struct file *filp, char __user *ubuf,
297 size_t cnt, loff_t *ppos)
299 return simple_read_from_buffer(ubuf, cnt, ppos,
300 readme_msg, strlen(readme_msg));
303 static const struct file_operations readme_fops = {
304 .read = inj_readme_read,
307 static struct dfs_node {
308 char *name;
309 struct dentry *d;
310 const struct file_operations *fops;
311 umode_t perm;
312 } dfs_fls[] = {
313 { .name = "status", .fops = &status_fops, .perm = S_IRUSR | S_IWUSR },
314 { .name = "misc", .fops = &misc_fops, .perm = S_IRUSR | S_IWUSR },
315 { .name = "addr", .fops = &addr_fops, .perm = S_IRUSR | S_IWUSR },
316 { .name = "bank", .fops = &bank_fops, .perm = S_IRUSR | S_IWUSR },
317 { .name = "flags", .fops = &flags_fops, .perm = S_IRUSR | S_IWUSR },
318 { .name = "cpu", .fops = &extcpu_fops, .perm = S_IRUSR | S_IWUSR },
319 { .name = "README", .fops = &readme_fops, .perm = S_IRUSR | S_IRGRP | S_IROTH },
322 static int __init init_mce_inject(void)
324 int i;
325 u64 cap;
327 rdmsrl(MSR_IA32_MCG_CAP, cap);
328 n_banks = cap & MCG_BANKCNT_MASK;
330 dfs_inj = debugfs_create_dir("mce-inject", NULL);
331 if (!dfs_inj)
332 return -EINVAL;
334 for (i = 0; i < ARRAY_SIZE(dfs_fls); i++) {
335 dfs_fls[i].d = debugfs_create_file(dfs_fls[i].name,
336 dfs_fls[i].perm,
337 dfs_inj,
338 &i_mce,
339 dfs_fls[i].fops);
341 if (!dfs_fls[i].d)
342 goto err_dfs_add;
345 return 0;
347 err_dfs_add:
348 while (--i >= 0)
349 debugfs_remove(dfs_fls[i].d);
351 debugfs_remove(dfs_inj);
352 dfs_inj = NULL;
354 return -ENOMEM;
357 static void __exit exit_mce_inject(void)
359 int i;
361 for (i = 0; i < ARRAY_SIZE(dfs_fls); i++)
362 debugfs_remove(dfs_fls[i].d);
364 memset(&dfs_fls, 0, sizeof(dfs_fls));
366 debugfs_remove(dfs_inj);
367 dfs_inj = NULL;
369 module_init(init_mce_inject);
370 module_exit(exit_mce_inject);
372 MODULE_LICENSE("GPL");
373 MODULE_AUTHOR("Borislav Petkov <bp@alien8.de>");
374 MODULE_AUTHOR("AMD Inc.");
375 MODULE_DESCRIPTION("MCE injection facility for RAS testing");