blob: 2a0c829fef3dc096e2b280699ff9941d303f7bd6 [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 */
47enum injection_type inj_type = SW_INJ;
48
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
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
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
Wei Yongjun8c2b1172014-12-09 09:04:55 +0800255static struct dfs_node {
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100256 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 },
Borislav Petkovb18f3862014-11-22 11:35:26 +0100264 { .name = "flags", .fops = &flags_fops },
265 { .name = "cpu", .fops = &extcpu_fops },
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200266};
267
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100268static int __init init_mce_inject(void)
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200269{
270 int i;
Aravind Gopalakrishnan685d46d2015-05-27 14:03:34 -0500271 u64 cap;
272
273 rdmsrl(MSR_IA32_MCG_CAP, cap);
274 n_banks = cap & MCG_BANKCNT_MASK;
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200275
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100276 dfs_inj = debugfs_create_dir("mce-inject", NULL);
277 if (!dfs_inj)
278 return -EINVAL;
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200279
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100280 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);
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200286
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100287 if (!dfs_fls[i].d)
288 goto err_dfs_add;
289 }
290
291 return 0;
292
293err_dfs_add:
294 while (--i >= 0)
295 debugfs_remove(dfs_fls[i].d);
296
297 debugfs_remove(dfs_inj);
298 dfs_inj = NULL;
299
300 return -ENOMEM;
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200301}
302
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100303static void __exit exit_mce_inject(void)
304{
305 int i;
306
307 for (i = 0; i < ARRAY_SIZE(dfs_fls); i++)
308 debugfs_remove(dfs_fls[i].d);
309
310 memset(&dfs_fls, 0, sizeof(dfs_fls));
311
312 debugfs_remove(dfs_inj);
313 dfs_inj = NULL;
314}
315module_init(init_mce_inject);
316module_exit(exit_mce_inject);
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200317
318MODULE_LICENSE("GPL");
Borislav Petkov43aff262012-10-29 18:40:09 +0100319MODULE_AUTHOR("Borislav Petkov <bp@alien8.de>");
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200320MODULE_AUTHOR("AMD Inc.");
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100321MODULE_DESCRIPTION("MCE injection facility for RAS testing");