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