EDAC, mce_amd_inj: Read out number of MCE banks from the hardware
[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>
9cdeb404
BP
18#include <asm/mce.h>
19
47ca08a4 20#include "mce_amd.h"
9cdeb404 21
9cdeb404
BP
22/*
23 * Collect all the MCi_XXX settings
24 */
25static struct mce i_mce;
fd19fcd6 26static struct dentry *dfs_inj;
9cdeb404 27
685d46d7
AG
28static u8 n_banks;
29
fd19fcd6
BP
30#define MCE_INJECT_SET(reg) \
31static int inj_##reg##_set(void *data, u64 val) \
9cdeb404 32{ \
fd19fcd6 33 struct mce *m = (struct mce *)data; \
9cdeb404 34 \
fd19fcd6
BP
35 m->reg = val; \
36 return 0; \
9cdeb404
BP
37}
38
fd19fcd6
BP
39MCE_INJECT_SET(status);
40MCE_INJECT_SET(misc);
41MCE_INJECT_SET(addr);
9cdeb404 42
fd19fcd6
BP
43#define MCE_INJECT_GET(reg) \
44static int inj_##reg##_get(void *data, u64 *val) \
9cdeb404 45{ \
fd19fcd6
BP
46 struct mce *m = (struct mce *)data; \
47 \
48 *val = m->reg; \
49 return 0; \
9cdeb404
BP
50}
51
fd19fcd6
BP
52MCE_INJECT_GET(status);
53MCE_INJECT_GET(misc);
54MCE_INJECT_GET(addr);
9cdeb404 55
fd19fcd6
BP
56DEFINE_SIMPLE_ATTRIBUTE(status_fops, inj_status_get, inj_status_set, "%llx\n");
57DEFINE_SIMPLE_ATTRIBUTE(misc_fops, inj_misc_get, inj_misc_set, "%llx\n");
58DEFINE_SIMPLE_ATTRIBUTE(addr_fops, inj_addr_get, inj_addr_set, "%llx\n");
9cdeb404 59
21690934
BP
60/*
61 * Caller needs to be make sure this cpu doesn't disappear
62 * from under us, i.e.: get_cpu/put_cpu.
63 */
64static int toggle_hw_mce_inject(unsigned int cpu, bool enable)
65{
66 u32 l, h;
67 int err;
68
69 err = rdmsr_on_cpu(cpu, MSR_K7_HWCR, &l, &h);
70 if (err) {
71 pr_err("%s: error reading HWCR\n", __func__);
72 return err;
73 }
74
75 enable ? (l |= BIT(18)) : (l &= ~BIT(18));
76
77 err = wrmsr_on_cpu(cpu, MSR_K7_HWCR, l, h);
78 if (err)
79 pr_err("%s: error writing HWCR\n", __func__);
80
81 return err;
82}
83
b18f3864
BP
84static int flags_get(void *data, u64 *val)
85{
86 struct mce *m = (struct mce *)data;
87
88 *val = m->inject_flags;
89
90 return 0;
91}
92
93static int flags_set(void *data, u64 val)
94{
95 struct mce *m = (struct mce *)data;
96
97 m->inject_flags = (u8)val;
98 return 0;
99}
100
101DEFINE_SIMPLE_ATTRIBUTE(flags_fops, flags_get, flags_set, "%llu\n");
102
103/*
104 * On which CPU to inject?
105 */
106MCE_INJECT_GET(extcpu);
107
108static int inj_extcpu_set(void *data, u64 val)
109{
110 struct mce *m = (struct mce *)data;
111
112 if (val >= nr_cpu_ids || !cpu_online(val)) {
113 pr_err("%s: Invalid CPU: %llu\n", __func__, val);
114 return -EINVAL;
115 }
116 m->extcpu = val;
117 return 0;
118}
119
120DEFINE_SIMPLE_ATTRIBUTE(extcpu_fops, inj_extcpu_get, inj_extcpu_set, "%llu\n");
121
51756a50
BP
122static void trigger_mce(void *info)
123{
124 asm volatile("int $18");
125}
126
127static void do_inject(void)
128{
129 u64 mcg_status = 0;
130 unsigned int cpu = i_mce.extcpu;
131 u8 b = i_mce.bank;
132
133 if (!(i_mce.inject_flags & MCJ_EXCEPTION)) {
134 amd_decode_mce(NULL, 0, &i_mce);
135 return;
136 }
137
138 get_online_cpus();
139 if (!cpu_online(cpu))
140 goto err;
141
142 /* prep MCE global settings for the injection */
143 mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV;
144
145 if (!(i_mce.status & MCI_STATUS_PCC))
146 mcg_status |= MCG_STATUS_RIPV;
147
148 toggle_hw_mce_inject(cpu, true);
149
150 wrmsr_on_cpu(cpu, MSR_IA32_MCG_STATUS,
151 (u32)mcg_status, (u32)(mcg_status >> 32));
152
153 wrmsr_on_cpu(cpu, MSR_IA32_MCx_STATUS(b),
154 (u32)i_mce.status, (u32)(i_mce.status >> 32));
155
156 wrmsr_on_cpu(cpu, MSR_IA32_MCx_ADDR(b),
157 (u32)i_mce.addr, (u32)(i_mce.addr >> 32));
158
159 wrmsr_on_cpu(cpu, MSR_IA32_MCx_MISC(b),
160 (u32)i_mce.misc, (u32)(i_mce.misc >> 32));
161
162 toggle_hw_mce_inject(cpu, false);
163
164 smp_call_function_single(cpu, trigger_mce, NULL, 0);
165
166err:
167 put_online_cpus();
168
169}
170
9cdeb404
BP
171/*
172 * This denotes into which bank we're injecting and triggers
173 * the injection, at the same time.
174 */
fd19fcd6 175static int inj_bank_set(void *data, u64 val)
9cdeb404 176{
fd19fcd6 177 struct mce *m = (struct mce *)data;
9cdeb404 178
685d46d7
AG
179 if (val >= n_banks) {
180 pr_err("Non-existent MCE bank: %llu\n", val);
181 return -EINVAL;
fd19fcd6 182 }
9cdeb404 183
fd19fcd6 184 m->bank = val;
51756a50 185 do_inject();
9cdeb404 186
fd19fcd6 187 return 0;
9cdeb404
BP
188}
189
e7f2ea1d 190MCE_INJECT_GET(bank);
9cdeb404 191
fd19fcd6
BP
192DEFINE_SIMPLE_ATTRIBUTE(bank_fops, inj_bank_get, inj_bank_set, "%llu\n");
193
8c2b117f 194static struct dfs_node {
fd19fcd6
BP
195 char *name;
196 struct dentry *d;
197 const struct file_operations *fops;
198} dfs_fls[] = {
199 { .name = "status", .fops = &status_fops },
200 { .name = "misc", .fops = &misc_fops },
201 { .name = "addr", .fops = &addr_fops },
202 { .name = "bank", .fops = &bank_fops },
b18f3864
BP
203 { .name = "flags", .fops = &flags_fops },
204 { .name = "cpu", .fops = &extcpu_fops },
9cdeb404
BP
205};
206
fd19fcd6 207static int __init init_mce_inject(void)
9cdeb404 208{
fd19fcd6 209 int i;
685d46d7
AG
210 u64 cap;
211
212 rdmsrl(MSR_IA32_MCG_CAP, cap);
213 n_banks = cap & MCG_BANKCNT_MASK;
9cdeb404 214
fd19fcd6
BP
215 dfs_inj = debugfs_create_dir("mce-inject", NULL);
216 if (!dfs_inj)
9cdeb404
BP
217 return -EINVAL;
218
fd19fcd6
BP
219 for (i = 0; i < ARRAY_SIZE(dfs_fls); i++) {
220 dfs_fls[i].d = debugfs_create_file(dfs_fls[i].name,
221 S_IRUSR | S_IWUSR,
222 dfs_inj,
223 &i_mce,
224 dfs_fls[i].fops);
9cdeb404 225
fd19fcd6
BP
226 if (!dfs_fls[i].d)
227 goto err_dfs_add;
9cdeb404 228 }
fd19fcd6 229
9cdeb404
BP
230 return 0;
231
fd19fcd6 232err_dfs_add:
df4b2a30 233 while (--i >= 0)
fd19fcd6 234 debugfs_remove(dfs_fls[i].d);
9cdeb404 235
fd19fcd6
BP
236 debugfs_remove(dfs_inj);
237 dfs_inj = NULL;
9cdeb404 238
fd19fcd6 239 return -ENOMEM;
9cdeb404
BP
240}
241
fd19fcd6 242static void __exit exit_mce_inject(void)
9cdeb404
BP
243{
244 int i;
245
fd19fcd6
BP
246 for (i = 0; i < ARRAY_SIZE(dfs_fls); i++)
247 debugfs_remove(dfs_fls[i].d);
9cdeb404 248
fd19fcd6 249 memset(&dfs_fls, 0, sizeof(dfs_fls));
9cdeb404 250
fd19fcd6
BP
251 debugfs_remove(dfs_inj);
252 dfs_inj = NULL;
9cdeb404 253}
fd19fcd6
BP
254module_init(init_mce_inject);
255module_exit(exit_mce_inject);
9cdeb404
BP
256
257MODULE_LICENSE("GPL");
43aff26c 258MODULE_AUTHOR("Borislav Petkov <bp@alien8.de>");
9cdeb404 259MODULE_AUTHOR("AMD Inc.");
fd19fcd6 260MODULE_DESCRIPTION("MCE injection facility for RAS testing");
This page took 0.258804 seconds and 5 git commands to generate.