blob: b92986b03f93300de106d9207adde1a086e23fc2 [file] [log] [blame]
Borislav Petkov9cdeb402010-09-02 18:33:24 +02001/*
Borislav Petkovfd19fcd2014-11-22 11:09:12 +01002 * 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.
Borislav Petkov9cdeb402010-09-02 18:33:24 +02005 *
6 * This file may be distributed under the terms of the GNU General Public
7 * License version 2.
8 *
Borislav Petkovfd19fcd2014-11-22 11:09:12 +01009 * Copyright (c) 2010-14: Borislav Petkov <bp@alien8.de>
Borislav Petkov9cdeb402010-09-02 18:33:24 +020010 * Advanced Micro Devices Inc.
11 */
12
13#include <linux/kobject.h>
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010014#include <linux/debugfs.h>
Paul Gortmaker51990e82012-01-22 11:23:42 -050015#include <linux/device.h>
Paul Gortmaker80a2e2e2011-07-03 13:37:56 -040016#include <linux/module.h>
Borislav Petkov51756a52014-11-22 11:47:07 +010017#include <linux/cpu.h>
Aravind Gopalakrishnan0451d142015-06-02 15:35:56 -050018#include <linux/string.h>
19#include <linux/uaccess.h>
Borislav Petkov9cdeb402010-09-02 18:33:24 +020020#include <asm/mce.h>
21
Borislav Petkov47ca08a2010-09-27 15:30:39 +020022#include "mce_amd.h"
Borislav Petkov9cdeb402010-09-02 18:33:24 +020023
Borislav Petkov9cdeb402010-09-02 18:33:24 +020024/*
25 * Collect all the MCi_XXX settings
26 */
27static struct mce i_mce;
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010028static struct dentry *dfs_inj;
Borislav Petkov9cdeb402010-09-02 18:33:24 +020029
Aravind Gopalakrishnan685d46d2015-05-27 14:03:34 -050030static u8 n_banks;
31
Aravind Gopalakrishnan0451d142015-06-02 15:35:56 -050032#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 */
kbuild test robotde277672015-06-05 19:24:26 +080047static enum injection_type inj_type = SW_INJ;
Aravind Gopalakrishnan0451d142015-06-02 15:35:56 -050048
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010049#define MCE_INJECT_SET(reg) \
50static int inj_##reg##_set(void *data, u64 val) \
Borislav Petkov9cdeb402010-09-02 18:33:24 +020051{ \
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010052 struct mce *m = (struct mce *)data; \
Borislav Petkov9cdeb402010-09-02 18:33:24 +020053 \
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010054 m->reg = val; \
55 return 0; \
Borislav Petkov9cdeb402010-09-02 18:33:24 +020056}
57
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010058MCE_INJECT_SET(status);
59MCE_INJECT_SET(misc);
60MCE_INJECT_SET(addr);
Borislav Petkov9cdeb402010-09-02 18:33:24 +020061
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010062#define MCE_INJECT_GET(reg) \
63static int inj_##reg##_get(void *data, u64 *val) \
Borislav Petkov9cdeb402010-09-02 18:33:24 +020064{ \
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010065 struct mce *m = (struct mce *)data; \
66 \
67 *val = m->reg; \
68 return 0; \
Borislav Petkov9cdeb402010-09-02 18:33:24 +020069}
70
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010071MCE_INJECT_GET(status);
72MCE_INJECT_GET(misc);
73MCE_INJECT_GET(addr);
Borislav Petkov9cdeb402010-09-02 18:33:24 +020074
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010075DEFINE_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");
Borislav Petkov9cdeb402010-09-02 18:33:24 +020078
79/*
Borislav Petkov21690932014-11-22 11:22:35 +010080 * 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
Aravind Gopalakrishnan0451d142015-06-02 15:35:56 -0500103static int __set_inj(const char *buf)
Borislav Petkovb18f3862014-11-22 11:35:26 +0100104{
Aravind Gopalakrishnan0451d142015-06-02 15:35:56 -0500105 int i;
Borislav Petkovb18f3862014-11-22 11:35:26 +0100106
Aravind Gopalakrishnan0451d142015-06-02 15:35:56 -0500107 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;
Borislav Petkovb18f3862014-11-22 11:35:26 +0100114}
115
Aravind Gopalakrishnan0451d142015-06-02 15:35:56 -0500116static ssize_t flags_read(struct file *filp, char __user *ubuf,
117 size_t cnt, loff_t *ppos)
Borislav Petkovb18f3862014-11-22 11:35:26 +0100118{
Aravind Gopalakrishnan0451d142015-06-02 15:35:56 -0500119 char buf[MAX_FLAG_OPT_SIZE];
120 int n;
Borislav Petkovb18f3862014-11-22 11:35:26 +0100121
Aravind Gopalakrishnan0451d142015-06-02 15:35:56 -0500122 n = sprintf(buf, "%s\n", flags_options[inj_type]);
123
124 return simple_read_from_buffer(ubuf, cnt, ppos, buf, n);
Borislav Petkovb18f3862014-11-22 11:35:26 +0100125}
126
Aravind Gopalakrishnan0451d142015-06-02 15:35:56 -0500127static ssize_t flags_write(struct file *filp, const char __user *ubuf,
128 size_t cnt, loff_t *ppos)
129{
130 char buf[MAX_FLAG_OPT_SIZE], *__buf;
131 int err;
132 size_t ret;
133
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;
156}
157
158static const struct file_operations flags_fops = {
159 .read = flags_read,
160 .write = flags_write,
161 .llseek = generic_file_llseek,
162};
Borislav Petkovb18f3862014-11-22 11:35:26 +0100163
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
Borislav Petkov51756a52014-11-22 11:47:07 +0100183static 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
Aravind Gopalakrishnan0451d142015-06-02 15:35:56 -0500194 if (inj_type == SW_INJ) {
Borislav Petkov51756a52014-11-22 11:47:07 +0100195 amd_decode_mce(NULL, 0, &i_mce);
196 return;
197 }
198
Borislav Petkov51756a52014-11-22 11:47:07 +0100199 /* 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
Borislav Petkov6d1e9bf2015-06-10 16:17:13 +0200205 get_online_cpus();
206 if (!cpu_online(cpu))
207 goto err;
208
Borislav Petkov51756a52014-11-22 11:47:07 +0100209 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
Borislav Petkov21690932014-11-22 11:22:35 +0100232/*
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200233 * This denotes into which bank we're injecting and triggers
234 * the injection, at the same time.
235 */
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100236static int inj_bank_set(void *data, u64 val)
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200237{
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100238 struct mce *m = (struct mce *)data;
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200239
Aravind Gopalakrishnan685d46d2015-05-27 14:03:34 -0500240 if (val >= n_banks) {
241 pr_err("Non-existent MCE bank: %llu\n", val);
242 return -EINVAL;
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100243 }
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200244
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100245 m->bank = val;
Borislav Petkov51756a52014-11-22 11:47:07 +0100246 do_inject();
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200247
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100248 return 0;
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200249}
250
Aravind Gopalakrishnane7f2ea12015-06-02 15:35:54 -0500251MCE_INJECT_GET(bank);
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200252
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100253DEFINE_SIMPLE_ATTRIBUTE(bank_fops, inj_bank_get, inj_bank_set, "%llu\n");
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200254
Aravind Gopalakrishnan99e21fe2015-06-03 17:13:58 +0200255static const char readme_msg[] =
Borislav Petkovf2f3dca2015-06-10 16:09:36 +0200256"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";
Aravind Gopalakrishnan99e21fe2015-06-03 17:13:58 +0200291
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
Wei Yongjun8c2b1172014-12-09 09:04:55 +0800304static struct dfs_node {
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100305 char *name;
306 struct dentry *d;
307 const struct file_operations *fops;
Aravind Gopalakrishnan4c6034e2015-06-02 15:35:58 -0500308 umode_t perm;
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100309} dfs_fls[] = {
Aravind Gopalakrishnan4c6034e2015-06-02 15:35:58 -0500310 { .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 },
Aravind Gopalakrishnan99e21fe2015-06-03 17:13:58 +0200316 { .name = "README", .fops = &readme_fops, .perm = S_IRUSR | S_IRGRP | S_IROTH },
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200317};
318
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100319static int __init init_mce_inject(void)
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200320{
321 int i;
Aravind Gopalakrishnan685d46d2015-05-27 14:03:34 -0500322 u64 cap;
323
324 rdmsrl(MSR_IA32_MCG_CAP, cap);
325 n_banks = cap & MCG_BANKCNT_MASK;
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200326
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100327 dfs_inj = debugfs_create_dir("mce-inject", NULL);
328 if (!dfs_inj)
329 return -EINVAL;
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200330
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100331 for (i = 0; i < ARRAY_SIZE(dfs_fls); i++) {
332 dfs_fls[i].d = debugfs_create_file(dfs_fls[i].name,
Aravind Gopalakrishnan4c6034e2015-06-02 15:35:58 -0500333 dfs_fls[i].perm,
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100334 dfs_inj,
335 &i_mce,
336 dfs_fls[i].fops);
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200337
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100338 if (!dfs_fls[i].d)
339 goto err_dfs_add;
340 }
341
342 return 0;
343
344err_dfs_add:
345 while (--i >= 0)
346 debugfs_remove(dfs_fls[i].d);
347
348 debugfs_remove(dfs_inj);
349 dfs_inj = NULL;
350
351 return -ENOMEM;
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200352}
353
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100354static void __exit exit_mce_inject(void)
355{
356 int i;
357
358 for (i = 0; i < ARRAY_SIZE(dfs_fls); i++)
359 debugfs_remove(dfs_fls[i].d);
360
361 memset(&dfs_fls, 0, sizeof(dfs_fls));
362
363 debugfs_remove(dfs_inj);
364 dfs_inj = NULL;
365}
366module_init(init_mce_inject);
367module_exit(exit_mce_inject);
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200368
369MODULE_LICENSE("GPL");
Borislav Petkov43aff262012-10-29 18:40:09 +0100370MODULE_AUTHOR("Borislav Petkov <bp@alien8.de>");
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200371MODULE_AUTHOR("AMD Inc.");
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100372MODULE_DESCRIPTION("MCE injection facility for RAS testing");