EDAC, mce_amd_inj: Modify flags attribute to use string arguments
[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
8c2b117f 255static struct dfs_node {
fd19fcd6
BP
256 char *name;
257 struct dentry *d;
258 const struct file_operations *fops;
259} dfs_fls[] = {
260 { .name = "status", .fops = &status_fops },
261 { .name = "misc", .fops = &misc_fops },
262 { .name = "addr", .fops = &addr_fops },
263 { .name = "bank", .fops = &bank_fops },
b18f3864
BP
264 { .name = "flags", .fops = &flags_fops },
265 { .name = "cpu", .fops = &extcpu_fops },
9cdeb404
BP
266};
267
fd19fcd6 268static int __init init_mce_inject(void)
9cdeb404 269{
fd19fcd6 270 int i;
685d46d7
AG
271 u64 cap;
272
273 rdmsrl(MSR_IA32_MCG_CAP, cap);
274 n_banks = cap & MCG_BANKCNT_MASK;
9cdeb404 275
fd19fcd6
BP
276 dfs_inj = debugfs_create_dir("mce-inject", NULL);
277 if (!dfs_inj)
9cdeb404
BP
278 return -EINVAL;
279
fd19fcd6
BP
280 for (i = 0; i < ARRAY_SIZE(dfs_fls); i++) {
281 dfs_fls[i].d = debugfs_create_file(dfs_fls[i].name,
282 S_IRUSR | S_IWUSR,
283 dfs_inj,
284 &i_mce,
285 dfs_fls[i].fops);
9cdeb404 286
fd19fcd6
BP
287 if (!dfs_fls[i].d)
288 goto err_dfs_add;
9cdeb404 289 }
fd19fcd6 290
9cdeb404
BP
291 return 0;
292
fd19fcd6 293err_dfs_add:
df4b2a30 294 while (--i >= 0)
fd19fcd6 295 debugfs_remove(dfs_fls[i].d);
9cdeb404 296
fd19fcd6
BP
297 debugfs_remove(dfs_inj);
298 dfs_inj = NULL;
9cdeb404 299
fd19fcd6 300 return -ENOMEM;
9cdeb404
BP
301}
302
fd19fcd6 303static void __exit exit_mce_inject(void)
9cdeb404
BP
304{
305 int i;
306
fd19fcd6
BP
307 for (i = 0; i < ARRAY_SIZE(dfs_fls); i++)
308 debugfs_remove(dfs_fls[i].d);
9cdeb404 309
fd19fcd6 310 memset(&dfs_fls, 0, sizeof(dfs_fls));
9cdeb404 311
fd19fcd6
BP
312 debugfs_remove(dfs_inj);
313 dfs_inj = NULL;
9cdeb404 314}
fd19fcd6
BP
315module_init(init_mce_inject);
316module_exit(exit_mce_inject);
9cdeb404
BP
317
318MODULE_LICENSE("GPL");
43aff26c 319MODULE_AUTHOR("Borislav Petkov <bp@alien8.de>");
9cdeb404 320MODULE_AUTHOR("AMD Inc.");
fd19fcd6 321MODULE_DESCRIPTION("MCE injection facility for RAS testing");
This page took 0.265243 seconds and 5 git commands to generate.