arm: socfpga: dts: Add Arria10 SDRAM EDAC DTS support
[deliverable/linux.git] / drivers / edac / 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 *
fd19fcd6 9 * Copyright (c) 2010-14: 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>
9cdeb404
BP
20#include <asm/mce.h>
21
47ca08a4 22#include "mce_amd.h"
9cdeb404 23
9cdeb404
BP
24/*
25 * Collect all the MCi_XXX settings
26 */
27static struct mce i_mce;
fd19fcd6 28static struct dentry *dfs_inj;
9cdeb404 29
685d46d7
AG
30static u8 n_banks;
31
0451d14d
AG
32#define MAX_FLAG_OPT_SIZE 3
33
34enum injection_type {
35 SW_INJ = 0, /* SW injection, simply decode the error */
36 HW_INJ, /* Trigger a #MC */
37 N_INJ_TYPES,
38};
39
40static const char * const flags_options[] = {
41 [SW_INJ] = "sw",
42 [HW_INJ] = "hw",
43 NULL
44};
45
46/* Set default injection to SW_INJ */
47enum injection_type inj_type = SW_INJ;
48
fd19fcd6
BP
49#define MCE_INJECT_SET(reg) \
50static int inj_##reg##_set(void *data, u64 val) \
9cdeb404 51{ \
fd19fcd6 52 struct mce *m = (struct mce *)data; \
9cdeb404 53 \
fd19fcd6
BP
54 m->reg = val; \
55 return 0; \
9cdeb404
BP
56}
57
fd19fcd6
BP
58MCE_INJECT_SET(status);
59MCE_INJECT_SET(misc);
60MCE_INJECT_SET(addr);
9cdeb404 61
fd19fcd6
BP
62#define MCE_INJECT_GET(reg) \
63static int inj_##reg##_get(void *data, u64 *val) \
9cdeb404 64{ \
fd19fcd6
BP
65 struct mce *m = (struct mce *)data; \
66 \
67 *val = m->reg; \
68 return 0; \
9cdeb404
BP
69}
70
fd19fcd6
BP
71MCE_INJECT_GET(status);
72MCE_INJECT_GET(misc);
73MCE_INJECT_GET(addr);
9cdeb404 74
fd19fcd6
BP
75DEFINE_SIMPLE_ATTRIBUTE(status_fops, inj_status_get, inj_status_set, "%llx\n");
76DEFINE_SIMPLE_ATTRIBUTE(misc_fops, inj_misc_get, inj_misc_set, "%llx\n");
77DEFINE_SIMPLE_ATTRIBUTE(addr_fops, inj_addr_get, inj_addr_set, "%llx\n");
9cdeb404 78
21690934
BP
79/*
80 * Caller needs to be make sure this cpu doesn't disappear
81 * from under us, i.e.: get_cpu/put_cpu.
82 */
83static int toggle_hw_mce_inject(unsigned int cpu, bool enable)
84{
85 u32 l, h;
86 int err;
87
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;
92 }
93
94 enable ? (l |= BIT(18)) : (l &= ~BIT(18));
95
96 err = wrmsr_on_cpu(cpu, MSR_K7_HWCR, l, h);
97 if (err)
98 pr_err("%s: error writing HWCR\n", __func__);
99
100 return err;
101}
102
0451d14d 103static int __set_inj(const char *buf)
b18f3864 104{
0451d14d
AG
105 int i;
106
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;
111 }
112 }
113 return -EINVAL;
114}
115
116static ssize_t flags_read(struct file *filp, char __user *ubuf,
117 size_t cnt, loff_t *ppos)
118{
119 char buf[MAX_FLAG_OPT_SIZE];
120 int n;
b18f3864 121
0451d14d 122 n = sprintf(buf, "%s\n", flags_options[inj_type]);
b18f3864 123
0451d14d 124 return simple_read_from_buffer(ubuf, cnt, ppos, buf, n);
b18f3864
BP
125}
126
0451d14d
AG
127static ssize_t flags_write(struct file *filp, const char __user *ubuf,
128 size_t cnt, loff_t *ppos)
b18f3864 129{
0451d14d
AG
130 char buf[MAX_FLAG_OPT_SIZE], *__buf;
131 int err;
132 size_t ret;
b18f3864 133
0451d14d
AG
134 if (cnt > MAX_FLAG_OPT_SIZE)
135 cnt = MAX_FLAG_OPT_SIZE;
136
137 ret = cnt;
138
139 if (copy_from_user(&buf, ubuf, cnt))
140 return -EFAULT;
141
142 buf[cnt - 1] = 0;
143
144 /* strip whitespace */
145 __buf = strstrip(buf);
146
147 err = __set_inj(__buf);
148 if (err) {
149 pr_err("%s: Invalid flags value: %s\n", __func__, __buf);
150 return err;
151 }
152
153 *ppos += ret;
154
155 return ret;
b18f3864
BP
156}
157
0451d14d
AG
158static const struct file_operations flags_fops = {
159 .read = flags_read,
160 .write = flags_write,
161 .llseek = generic_file_llseek,
162};
b18f3864
BP
163
164/*
165 * On which CPU to inject?
166 */
167MCE_INJECT_GET(extcpu);
168
169static int inj_extcpu_set(void *data, u64 val)
170{
171 struct mce *m = (struct mce *)data;
172
173 if (val >= nr_cpu_ids || !cpu_online(val)) {
174 pr_err("%s: Invalid CPU: %llu\n", __func__, val);
175 return -EINVAL;
176 }
177 m->extcpu = val;
178 return 0;
179}
180
181DEFINE_SIMPLE_ATTRIBUTE(extcpu_fops, inj_extcpu_get, inj_extcpu_set, "%llu\n");
182
51756a50
BP
183static void trigger_mce(void *info)
184{
185 asm volatile("int $18");
186}
187
188static void do_inject(void)
189{
190 u64 mcg_status = 0;
191 unsigned int cpu = i_mce.extcpu;
192 u8 b = i_mce.bank;
193
0451d14d 194 if (inj_type == SW_INJ) {
51756a50
BP
195 amd_decode_mce(NULL, 0, &i_mce);
196 return;
197 }
198
199 get_online_cpus();
200 if (!cpu_online(cpu))
201 goto err;
202
203 /* prep MCE global settings for the injection */
204 mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV;
205
206 if (!(i_mce.status & MCI_STATUS_PCC))
207 mcg_status |= MCG_STATUS_RIPV;
208
209 toggle_hw_mce_inject(cpu, true);
210
211 wrmsr_on_cpu(cpu, MSR_IA32_MCG_STATUS,
212 (u32)mcg_status, (u32)(mcg_status >> 32));
213
214 wrmsr_on_cpu(cpu, MSR_IA32_MCx_STATUS(b),
215 (u32)i_mce.status, (u32)(i_mce.status >> 32));
216
217 wrmsr_on_cpu(cpu, MSR_IA32_MCx_ADDR(b),
218 (u32)i_mce.addr, (u32)(i_mce.addr >> 32));
219
220 wrmsr_on_cpu(cpu, MSR_IA32_MCx_MISC(b),
221 (u32)i_mce.misc, (u32)(i_mce.misc >> 32));
222
223 toggle_hw_mce_inject(cpu, false);
224
225 smp_call_function_single(cpu, trigger_mce, NULL, 0);
226
227err:
228 put_online_cpus();
229
230}
231
9cdeb404
BP
232/*
233 * This denotes into which bank we're injecting and triggers
234 * the injection, at the same time.
235 */
fd19fcd6 236static int inj_bank_set(void *data, u64 val)
9cdeb404 237{
fd19fcd6 238 struct mce *m = (struct mce *)data;
9cdeb404 239
685d46d7
AG
240 if (val >= n_banks) {
241 pr_err("Non-existent MCE bank: %llu\n", val);
242 return -EINVAL;
fd19fcd6 243 }
9cdeb404 244
fd19fcd6 245 m->bank = val;
51756a50 246 do_inject();
9cdeb404 247
fd19fcd6 248 return 0;
9cdeb404
BP
249}
250
e7f2ea1d 251MCE_INJECT_GET(bank);
9cdeb404 252
fd19fcd6
BP
253DEFINE_SIMPLE_ATTRIBUTE(bank_fops, inj_bank_get, inj_bank_set, "%llu\n");
254
99e21fea
AG
255static const char readme_msg[] =
256 "\nDescription of the files and their usages:\n\n"
257 "status: Set a value to be programmed into MCx_STATUS(bank)\n"
258 "\t The status bits provide insight into the type of\n"
259 "\t error that caused the MCE.\n\n"
260 "misc: Set value of MCx_MISC(bank)\n"
261 "\t misc register provides auxiliary info about the error. This\n"
262 "\t register is typically used for error thresholding purpose and\n"
263 "\t validity of the register is indicated by MCx_STATUS[MiscV]\n\n"
264 "addr: Error address value to be written to MCx_ADDR(bank)\n"
265 "\t This register is used to log address information associated\n"
266 "\t with the error.\n\n"
267 "Note: See respective BKDGs for the exact bit definitions of the\n"
268 "\t above registers as they mirror the MCi_[STATUS | MISC | ADDR]\n"
269 "\t hardware registers.\n\n"
270 "bank: Specify the bank you want to inject the error into.\n"
271 "\t The number of banks in a processor varies and is family/model\n"
272 "\t dependent. So, a sanity check performed while writing.\n"
273 "\t Writing to this file will trigger a #MC or APIC interrupts or\n"
274 "\t invoke the error decoder routines for AMD processors. The value\n"
275 "\t in 'flags' file decides which of above actions is triggered.\n\n"
276 "flags: Write to this file to speficy the error injection policy.\n"
277 "\t Allowed values:\n"
278 "\t\t\"sw\" - SW error injection, Only calls error decoder\n"
279 "\t\t\troutines to print error info in human readable format\n"
280 "\t\t\"hw\" - HW error injection, Forces a #MC,\n"
281 "\t\t\tcauses exception handler to handle the error\n"
282 "\t\t\tif UC or poll handler catches it if CE\n"
283 "\t\t\tWarning: Might cause system panic if MCx_STATUS[PCC]\n"
284 "\t\t\tis set. For debug purposes, consider setting\n"
285 "\t\t\t/<debugfs_mountpoint>/mce/fake_panic\n"
286 "cpu: The cpu to inject the error on.\n\n";
287
288static ssize_t
289inj_readme_read(struct file *filp, char __user *ubuf,
290 size_t cnt, loff_t *ppos)
291{
292 return simple_read_from_buffer(ubuf, cnt, ppos,
293 readme_msg, strlen(readme_msg));
294}
295
296static const struct file_operations readme_fops = {
297 .read = inj_readme_read,
298};
299
8c2b117f 300static struct dfs_node {
fd19fcd6
BP
301 char *name;
302 struct dentry *d;
303 const struct file_operations *fops;
4c6034e8 304 umode_t perm;
fd19fcd6 305} dfs_fls[] = {
4c6034e8
AG
306 { .name = "status", .fops = &status_fops, .perm = S_IRUSR | S_IWUSR },
307 { .name = "misc", .fops = &misc_fops, .perm = S_IRUSR | S_IWUSR },
308 { .name = "addr", .fops = &addr_fops, .perm = S_IRUSR | S_IWUSR },
309 { .name = "bank", .fops = &bank_fops, .perm = S_IRUSR | S_IWUSR },
310 { .name = "flags", .fops = &flags_fops, .perm = S_IRUSR | S_IWUSR },
311 { .name = "cpu", .fops = &extcpu_fops, .perm = S_IRUSR | S_IWUSR },
99e21fea 312 { .name = "README", .fops = &readme_fops, .perm = S_IRUSR | S_IRGRP | S_IROTH },
9cdeb404
BP
313};
314
fd19fcd6 315static int __init init_mce_inject(void)
9cdeb404 316{
fd19fcd6 317 int i;
685d46d7
AG
318 u64 cap;
319
320 rdmsrl(MSR_IA32_MCG_CAP, cap);
321 n_banks = cap & MCG_BANKCNT_MASK;
9cdeb404 322
fd19fcd6
BP
323 dfs_inj = debugfs_create_dir("mce-inject", NULL);
324 if (!dfs_inj)
9cdeb404
BP
325 return -EINVAL;
326
fd19fcd6
BP
327 for (i = 0; i < ARRAY_SIZE(dfs_fls); i++) {
328 dfs_fls[i].d = debugfs_create_file(dfs_fls[i].name,
4c6034e8 329 dfs_fls[i].perm,
fd19fcd6
BP
330 dfs_inj,
331 &i_mce,
332 dfs_fls[i].fops);
9cdeb404 333
fd19fcd6
BP
334 if (!dfs_fls[i].d)
335 goto err_dfs_add;
9cdeb404 336 }
fd19fcd6 337
9cdeb404
BP
338 return 0;
339
fd19fcd6 340err_dfs_add:
df4b2a30 341 while (--i >= 0)
fd19fcd6 342 debugfs_remove(dfs_fls[i].d);
9cdeb404 343
fd19fcd6
BP
344 debugfs_remove(dfs_inj);
345 dfs_inj = NULL;
9cdeb404 346
fd19fcd6 347 return -ENOMEM;
9cdeb404
BP
348}
349
fd19fcd6 350static void __exit exit_mce_inject(void)
9cdeb404
BP
351{
352 int i;
353
fd19fcd6
BP
354 for (i = 0; i < ARRAY_SIZE(dfs_fls); i++)
355 debugfs_remove(dfs_fls[i].d);
9cdeb404 356
fd19fcd6 357 memset(&dfs_fls, 0, sizeof(dfs_fls));
9cdeb404 358
fd19fcd6
BP
359 debugfs_remove(dfs_inj);
360 dfs_inj = NULL;
9cdeb404 361}
fd19fcd6
BP
362module_init(init_mce_inject);
363module_exit(exit_mce_inject);
9cdeb404
BP
364
365MODULE_LICENSE("GPL");
43aff26c 366MODULE_AUTHOR("Borislav Petkov <bp@alien8.de>");
9cdeb404 367MODULE_AUTHOR("AMD Inc.");
fd19fcd6 368MODULE_DESCRIPTION("MCE injection facility for RAS testing");
This page took 0.315264 seconds and 5 git commands to generate.