blob: f7681b553fd5d34cc94d062d43b92413af1487f6 [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>
Borislav Petkov9cdeb402010-09-02 18:33:24 +020018#include <asm/mce.h>
19
Borislav Petkov47ca08a2010-09-27 15:30:39 +020020#include "mce_amd.h"
Borislav Petkov9cdeb402010-09-02 18:33:24 +020021
Borislav Petkov9cdeb402010-09-02 18:33:24 +020022/*
23 * Collect all the MCi_XXX settings
24 */
25static struct mce i_mce;
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010026static struct dentry *dfs_inj;
Borislav Petkov9cdeb402010-09-02 18:33:24 +020027
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010028#define MCE_INJECT_SET(reg) \
29static int inj_##reg##_set(void *data, u64 val) \
Borislav Petkov9cdeb402010-09-02 18:33:24 +020030{ \
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010031 struct mce *m = (struct mce *)data; \
Borislav Petkov9cdeb402010-09-02 18:33:24 +020032 \
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010033 m->reg = val; \
34 return 0; \
Borislav Petkov9cdeb402010-09-02 18:33:24 +020035}
36
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010037MCE_INJECT_SET(status);
38MCE_INJECT_SET(misc);
39MCE_INJECT_SET(addr);
Borislav Petkov9cdeb402010-09-02 18:33:24 +020040
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010041#define MCE_INJECT_GET(reg) \
42static int inj_##reg##_get(void *data, u64 *val) \
Borislav Petkov9cdeb402010-09-02 18:33:24 +020043{ \
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010044 struct mce *m = (struct mce *)data; \
45 \
46 *val = m->reg; \
47 return 0; \
Borislav Petkov9cdeb402010-09-02 18:33:24 +020048}
49
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010050MCE_INJECT_GET(status);
51MCE_INJECT_GET(misc);
52MCE_INJECT_GET(addr);
Borislav Petkov9cdeb402010-09-02 18:33:24 +020053
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010054DEFINE_SIMPLE_ATTRIBUTE(status_fops, inj_status_get, inj_status_set, "%llx\n");
55DEFINE_SIMPLE_ATTRIBUTE(misc_fops, inj_misc_get, inj_misc_set, "%llx\n");
56DEFINE_SIMPLE_ATTRIBUTE(addr_fops, inj_addr_get, inj_addr_set, "%llx\n");
Borislav Petkov9cdeb402010-09-02 18:33:24 +020057
58/*
Borislav Petkov21690932014-11-22 11:22:35 +010059 * Caller needs to be make sure this cpu doesn't disappear
60 * from under us, i.e.: get_cpu/put_cpu.
61 */
62static int toggle_hw_mce_inject(unsigned int cpu, bool enable)
63{
64 u32 l, h;
65 int err;
66
67 err = rdmsr_on_cpu(cpu, MSR_K7_HWCR, &l, &h);
68 if (err) {
69 pr_err("%s: error reading HWCR\n", __func__);
70 return err;
71 }
72
73 enable ? (l |= BIT(18)) : (l &= ~BIT(18));
74
75 err = wrmsr_on_cpu(cpu, MSR_K7_HWCR, l, h);
76 if (err)
77 pr_err("%s: error writing HWCR\n", __func__);
78
79 return err;
80}
81
Borislav Petkovb18f3862014-11-22 11:35:26 +010082static int flags_get(void *data, u64 *val)
83{
84 struct mce *m = (struct mce *)data;
85
86 *val = m->inject_flags;
87
88 return 0;
89}
90
91static int flags_set(void *data, u64 val)
92{
93 struct mce *m = (struct mce *)data;
94
95 m->inject_flags = (u8)val;
96 return 0;
97}
98
99DEFINE_SIMPLE_ATTRIBUTE(flags_fops, flags_get, flags_set, "%llu\n");
100
101/*
102 * On which CPU to inject?
103 */
104MCE_INJECT_GET(extcpu);
105
106static int inj_extcpu_set(void *data, u64 val)
107{
108 struct mce *m = (struct mce *)data;
109
110 if (val >= nr_cpu_ids || !cpu_online(val)) {
111 pr_err("%s: Invalid CPU: %llu\n", __func__, val);
112 return -EINVAL;
113 }
114 m->extcpu = val;
115 return 0;
116}
117
118DEFINE_SIMPLE_ATTRIBUTE(extcpu_fops, inj_extcpu_get, inj_extcpu_set, "%llu\n");
119
Borislav Petkov51756a52014-11-22 11:47:07 +0100120static void trigger_mce(void *info)
121{
122 asm volatile("int $18");
123}
124
125static void do_inject(void)
126{
127 u64 mcg_status = 0;
128 unsigned int cpu = i_mce.extcpu;
129 u8 b = i_mce.bank;
130
131 if (!(i_mce.inject_flags & MCJ_EXCEPTION)) {
132 amd_decode_mce(NULL, 0, &i_mce);
133 return;
134 }
135
136 get_online_cpus();
137 if (!cpu_online(cpu))
138 goto err;
139
140 /* prep MCE global settings for the injection */
141 mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV;
142
143 if (!(i_mce.status & MCI_STATUS_PCC))
144 mcg_status |= MCG_STATUS_RIPV;
145
146 toggle_hw_mce_inject(cpu, true);
147
148 wrmsr_on_cpu(cpu, MSR_IA32_MCG_STATUS,
149 (u32)mcg_status, (u32)(mcg_status >> 32));
150
151 wrmsr_on_cpu(cpu, MSR_IA32_MCx_STATUS(b),
152 (u32)i_mce.status, (u32)(i_mce.status >> 32));
153
154 wrmsr_on_cpu(cpu, MSR_IA32_MCx_ADDR(b),
155 (u32)i_mce.addr, (u32)(i_mce.addr >> 32));
156
157 wrmsr_on_cpu(cpu, MSR_IA32_MCx_MISC(b),
158 (u32)i_mce.misc, (u32)(i_mce.misc >> 32));
159
160 toggle_hw_mce_inject(cpu, false);
161
162 smp_call_function_single(cpu, trigger_mce, NULL, 0);
163
164err:
165 put_online_cpus();
166
167}
168
Borislav Petkov21690932014-11-22 11:22:35 +0100169/*
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200170 * This denotes into which bank we're injecting and triggers
171 * the injection, at the same time.
172 */
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100173static int inj_bank_set(void *data, u64 val)
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200174{
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100175 struct mce *m = (struct mce *)data;
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200176
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100177 if (val > 5) {
178 if (boot_cpu_data.x86 != 0x15 || val > 6) {
179 pr_err("Non-existent MCE bank: %llu\n", val);
Borislav Petkov1b07ca42010-11-09 19:41:49 +0100180 return -EINVAL;
181 }
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100182 }
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200183
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100184 m->bank = val;
Borislav Petkov51756a52014-11-22 11:47:07 +0100185 do_inject();
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200186
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100187 return 0;
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200188}
189
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100190static int inj_bank_get(void *data, u64 *val)
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200191{
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100192 struct mce *m = (struct mce *)data;
193
194 *val = m->bank;
195 return 0;
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200196}
197
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100198DEFINE_SIMPLE_ATTRIBUTE(bank_fops, inj_bank_get, inj_bank_set, "%llu\n");
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200199
Wei Yongjun8c2b1172014-12-09 09:04:55 +0800200static struct dfs_node {
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100201 char *name;
202 struct dentry *d;
203 const struct file_operations *fops;
204} dfs_fls[] = {
205 { .name = "status", .fops = &status_fops },
206 { .name = "misc", .fops = &misc_fops },
207 { .name = "addr", .fops = &addr_fops },
208 { .name = "bank", .fops = &bank_fops },
Borislav Petkovb18f3862014-11-22 11:35:26 +0100209 { .name = "flags", .fops = &flags_fops },
210 { .name = "cpu", .fops = &extcpu_fops },
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200211};
212
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100213static int __init init_mce_inject(void)
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200214{
215 int i;
216
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100217 dfs_inj = debugfs_create_dir("mce-inject", NULL);
218 if (!dfs_inj)
219 return -EINVAL;
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200220
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100221 for (i = 0; i < ARRAY_SIZE(dfs_fls); i++) {
222 dfs_fls[i].d = debugfs_create_file(dfs_fls[i].name,
223 S_IRUSR | S_IWUSR,
224 dfs_inj,
225 &i_mce,
226 dfs_fls[i].fops);
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200227
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100228 if (!dfs_fls[i].d)
229 goto err_dfs_add;
230 }
231
232 return 0;
233
234err_dfs_add:
235 while (--i >= 0)
236 debugfs_remove(dfs_fls[i].d);
237
238 debugfs_remove(dfs_inj);
239 dfs_inj = NULL;
240
241 return -ENOMEM;
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200242}
243
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100244static void __exit exit_mce_inject(void)
245{
246 int i;
247
248 for (i = 0; i < ARRAY_SIZE(dfs_fls); i++)
249 debugfs_remove(dfs_fls[i].d);
250
251 memset(&dfs_fls, 0, sizeof(dfs_fls));
252
253 debugfs_remove(dfs_inj);
254 dfs_inj = NULL;
255}
256module_init(init_mce_inject);
257module_exit(exit_mce_inject);
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200258
259MODULE_LICENSE("GPL");
Borislav Petkov43aff262012-10-29 18:40:09 +0100260MODULE_AUTHOR("Borislav Petkov <bp@alien8.de>");
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200261MODULE_AUTHOR("AMD Inc.");
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100262MODULE_DESCRIPTION("MCE injection facility for RAS testing");