x86/ras/mce_amd_inj: Return early on invalid input
[deliverable/linux.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>
9cdeb404
BP
20#include <asm/mce.h>
21
6c36dfe9 22#include "../kernel/cpu/mcheck/mce-internal.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 */
de277678 47static enum injection_type inj_type = SW_INJ;
0451d14d 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;
b18f3864 132
0451d14d 133 if (cnt > MAX_FLAG_OPT_SIZE)
85c9306d 134 return -EINVAL;
0451d14d
AG
135
136 if (copy_from_user(&buf, ubuf, cnt))
137 return -EFAULT;
138
139 buf[cnt - 1] = 0;
140
141 /* strip whitespace */
142 __buf = strstrip(buf);
143
144 err = __set_inj(__buf);
145 if (err) {
146 pr_err("%s: Invalid flags value: %s\n", __func__, __buf);
147 return err;
148 }
149
85c9306d 150 *ppos += cnt;
0451d14d 151
85c9306d 152 return cnt;
b18f3864
BP
153}
154
0451d14d
AG
155static const struct file_operations flags_fops = {
156 .read = flags_read,
157 .write = flags_write,
158 .llseek = generic_file_llseek,
159};
b18f3864
BP
160
161/*
162 * On which CPU to inject?
163 */
164MCE_INJECT_GET(extcpu);
165
166static int inj_extcpu_set(void *data, u64 val)
167{
168 struct mce *m = (struct mce *)data;
169
170 if (val >= nr_cpu_ids || !cpu_online(val)) {
171 pr_err("%s: Invalid CPU: %llu\n", __func__, val);
172 return -EINVAL;
173 }
174 m->extcpu = val;
175 return 0;
176}
177
178DEFINE_SIMPLE_ATTRIBUTE(extcpu_fops, inj_extcpu_get, inj_extcpu_set, "%llu\n");
179
51756a50
BP
180static void trigger_mce(void *info)
181{
182 asm volatile("int $18");
183}
184
185static void do_inject(void)
186{
187 u64 mcg_status = 0;
188 unsigned int cpu = i_mce.extcpu;
189 u8 b = i_mce.bank;
190
cda9459d
BP
191 if (i_mce.misc)
192 i_mce.status |= MCI_STATUS_MISCV;
193
0451d14d 194 if (inj_type == SW_INJ) {
6c36dfe9 195 mce_inject_log(&i_mce);
51756a50
BP
196 return;
197 }
198
51756a50
BP
199 /* prep MCE global settings for the injection */
200 mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV;
201
202 if (!(i_mce.status & MCI_STATUS_PCC))
203 mcg_status |= MCG_STATUS_RIPV;
204
6d1e9bf5
BP
205 get_online_cpus();
206 if (!cpu_online(cpu))
207 goto err;
208
51756a50
BP
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 255static const char readme_msg[] =
f2f3dca1
BP
256"Description of the files and their usages:\n"
257"\n"
258"Note1: i refers to the bank number below.\n"
259"Note2: See respective BKDGs for the exact bit definitions of the files below\n"
260"as they mirror the hardware registers.\n"
261"\n"
262"status:\t Set MCi_STATUS: the bits in that MSR control the error type and\n"
263"\t attributes of the error which caused the MCE.\n"
264"\n"
265"misc:\t Set MCi_MISC: provide auxiliary info about the error. It is mostly\n"
266"\t used for error thresholding purposes and its validity is indicated by\n"
267"\t MCi_STATUS[MiscV].\n"
268"\n"
269"addr:\t Error address value to be written to MCi_ADDR. Log address information\n"
270"\t associated with the error.\n"
271"\n"
272"cpu:\t The CPU to inject the error on.\n"
273"\n"
274"bank:\t Specify the bank you want to inject the error into: the number of\n"
275"\t banks in a processor varies and is family/model-specific, therefore, the\n"
276"\t supplied value is sanity-checked. Setting the bank value also triggers the\n"
277"\t injection.\n"
278"\n"
279"flags:\t Injection type to be performed. Writing to this file will trigger a\n"
280"\t real machine check, an APIC interrupt or invoke the error decoder routines\n"
281"\t for AMD processors.\n"
282"\n"
283"\t Allowed error injection types:\n"
284"\t - \"sw\": Software error injection. Decode error to a human-readable \n"
285"\t format only. Safe to use.\n"
286"\t - \"hw\": Hardware error injection. Causes the #MC exception handler to \n"
287"\t handle the error. Be warned: might cause system panic if MCi_STATUS[PCC] \n"
288"\t is set. Therefore, consider setting (debugfs_mountpoint)/mce/fake_panic \n"
289"\t before injecting.\n"
290"\n";
99e21fea
AG
291
292static ssize_t
293inj_readme_read(struct file *filp, char __user *ubuf,
294 size_t cnt, loff_t *ppos)
295{
296 return simple_read_from_buffer(ubuf, cnt, ppos,
297 readme_msg, strlen(readme_msg));
298}
299
300static const struct file_operations readme_fops = {
301 .read = inj_readme_read,
302};
303
8c2b117f 304static struct dfs_node {
fd19fcd6
BP
305 char *name;
306 struct dentry *d;
307 const struct file_operations *fops;
4c6034e8 308 umode_t perm;
fd19fcd6 309} dfs_fls[] = {
4c6034e8
AG
310 { .name = "status", .fops = &status_fops, .perm = S_IRUSR | S_IWUSR },
311 { .name = "misc", .fops = &misc_fops, .perm = S_IRUSR | S_IWUSR },
312 { .name = "addr", .fops = &addr_fops, .perm = S_IRUSR | S_IWUSR },
313 { .name = "bank", .fops = &bank_fops, .perm = S_IRUSR | S_IWUSR },
314 { .name = "flags", .fops = &flags_fops, .perm = S_IRUSR | S_IWUSR },
315 { .name = "cpu", .fops = &extcpu_fops, .perm = S_IRUSR | S_IWUSR },
99e21fea 316 { .name = "README", .fops = &readme_fops, .perm = S_IRUSR | S_IRGRP | S_IROTH },
9cdeb404
BP
317};
318
fd19fcd6 319static int __init init_mce_inject(void)
9cdeb404 320{
fd19fcd6 321 int i;
685d46d7
AG
322 u64 cap;
323
324 rdmsrl(MSR_IA32_MCG_CAP, cap);
325 n_banks = cap & MCG_BANKCNT_MASK;
9cdeb404 326
fd19fcd6
BP
327 dfs_inj = debugfs_create_dir("mce-inject", NULL);
328 if (!dfs_inj)
9cdeb404
BP
329 return -EINVAL;
330
fd19fcd6
BP
331 for (i = 0; i < ARRAY_SIZE(dfs_fls); i++) {
332 dfs_fls[i].d = debugfs_create_file(dfs_fls[i].name,
4c6034e8 333 dfs_fls[i].perm,
fd19fcd6
BP
334 dfs_inj,
335 &i_mce,
336 dfs_fls[i].fops);
9cdeb404 337
fd19fcd6
BP
338 if (!dfs_fls[i].d)
339 goto err_dfs_add;
9cdeb404 340 }
fd19fcd6 341
9cdeb404
BP
342 return 0;
343
fd19fcd6 344err_dfs_add:
df4b2a30 345 while (--i >= 0)
fd19fcd6 346 debugfs_remove(dfs_fls[i].d);
9cdeb404 347
fd19fcd6
BP
348 debugfs_remove(dfs_inj);
349 dfs_inj = NULL;
9cdeb404 350
fd19fcd6 351 return -ENOMEM;
9cdeb404
BP
352}
353
fd19fcd6 354static void __exit exit_mce_inject(void)
9cdeb404
BP
355{
356 int i;
357
fd19fcd6
BP
358 for (i = 0; i < ARRAY_SIZE(dfs_fls); i++)
359 debugfs_remove(dfs_fls[i].d);
9cdeb404 360
fd19fcd6 361 memset(&dfs_fls, 0, sizeof(dfs_fls));
9cdeb404 362
fd19fcd6
BP
363 debugfs_remove(dfs_inj);
364 dfs_inj = NULL;
9cdeb404 365}
fd19fcd6
BP
366module_init(init_mce_inject);
367module_exit(exit_mce_inject);
9cdeb404
BP
368
369MODULE_LICENSE("GPL");
43aff26c 370MODULE_AUTHOR("Borislav Petkov <bp@alien8.de>");
9cdeb404 371MODULE_AUTHOR("AMD Inc.");
fd19fcd6 372MODULE_DESCRIPTION("MCE injection facility for RAS testing");
This page took 0.279371 seconds and 5 git commands to generate.