blob: 5a758d63a8ccf1f4427f9893a0f959c5bf9d1565 [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 Petkov9cdeb402010-09-02 18:33:24 +020017#include <asm/mce.h>
18
Borislav Petkov47ca08a2010-09-27 15:30:39 +020019#include "mce_amd.h"
Borislav Petkov9cdeb402010-09-02 18:33:24 +020020
Borislav Petkov9cdeb402010-09-02 18:33:24 +020021/*
22 * Collect all the MCi_XXX settings
23 */
24static struct mce i_mce;
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010025static struct dentry *dfs_inj;
Borislav Petkov9cdeb402010-09-02 18:33:24 +020026
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010027#define MCE_INJECT_SET(reg) \
28static int inj_##reg##_set(void *data, u64 val) \
Borislav Petkov9cdeb402010-09-02 18:33:24 +020029{ \
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010030 struct mce *m = (struct mce *)data; \
Borislav Petkov9cdeb402010-09-02 18:33:24 +020031 \
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010032 m->reg = val; \
33 return 0; \
Borislav Petkov9cdeb402010-09-02 18:33:24 +020034}
35
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010036MCE_INJECT_SET(status);
37MCE_INJECT_SET(misc);
38MCE_INJECT_SET(addr);
Borislav Petkov9cdeb402010-09-02 18:33:24 +020039
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010040#define MCE_INJECT_GET(reg) \
41static int inj_##reg##_get(void *data, u64 *val) \
Borislav Petkov9cdeb402010-09-02 18:33:24 +020042{ \
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010043 struct mce *m = (struct mce *)data; \
44 \
45 *val = m->reg; \
46 return 0; \
Borislav Petkov9cdeb402010-09-02 18:33:24 +020047}
48
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010049MCE_INJECT_GET(status);
50MCE_INJECT_GET(misc);
51MCE_INJECT_GET(addr);
Borislav Petkov9cdeb402010-09-02 18:33:24 +020052
Borislav Petkovfd19fcd2014-11-22 11:09:12 +010053DEFINE_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");
Borislav Petkov9cdeb402010-09-02 18:33:24 +020056
57/*
Borislav Petkov21690932014-11-22 11:22:35 +010058 * 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
Borislav Petkovb18f3862014-11-22 11:35:26 +010081static 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
Borislav Petkov21690932014-11-22 11:22:35 +0100119/*
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200120 * This denotes into which bank we're injecting and triggers
121 * the injection, at the same time.
122 */
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100123static int inj_bank_set(void *data, u64 val)
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200124{
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100125 struct mce *m = (struct mce *)data;
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200126
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100127 if (val > 5) {
128 if (boot_cpu_data.x86 != 0x15 || val > 6) {
129 pr_err("Non-existent MCE bank: %llu\n", val);
Borislav Petkov1b07ca42010-11-09 19:41:49 +0100130 return -EINVAL;
131 }
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100132 }
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200133
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100134 m->bank = val;
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200135
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100136 amd_decode_mce(NULL, 0, m);
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200137
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100138 return 0;
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200139}
140
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100141static int inj_bank_get(void *data, u64 *val)
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200142{
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100143 struct mce *m = (struct mce *)data;
144
145 *val = m->bank;
146 return 0;
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200147}
148
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100149DEFINE_SIMPLE_ATTRIBUTE(bank_fops, inj_bank_get, inj_bank_set, "%llu\n");
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200150
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100151struct 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 },
Borislav Petkovb18f3862014-11-22 11:35:26 +0100160 { .name = "flags", .fops = &flags_fops },
161 { .name = "cpu", .fops = &extcpu_fops },
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200162};
163
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100164static int __init init_mce_inject(void)
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200165{
166 int i;
167
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100168 dfs_inj = debugfs_create_dir("mce-inject", NULL);
169 if (!dfs_inj)
170 return -EINVAL;
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200171
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100172 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);
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200178
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100179 if (!dfs_fls[i].d)
180 goto err_dfs_add;
181 }
182
183 return 0;
184
185err_dfs_add:
186 while (--i >= 0)
187 debugfs_remove(dfs_fls[i].d);
188
189 debugfs_remove(dfs_inj);
190 dfs_inj = NULL;
191
192 return -ENOMEM;
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200193}
194
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100195static void __exit exit_mce_inject(void)
196{
197 int i;
198
199 for (i = 0; i < ARRAY_SIZE(dfs_fls); i++)
200 debugfs_remove(dfs_fls[i].d);
201
202 memset(&dfs_fls, 0, sizeof(dfs_fls));
203
204 debugfs_remove(dfs_inj);
205 dfs_inj = NULL;
206}
207module_init(init_mce_inject);
208module_exit(exit_mce_inject);
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200209
210MODULE_LICENSE("GPL");
Borislav Petkov43aff262012-10-29 18:40:09 +0100211MODULE_AUTHOR("Borislav Petkov <bp@alien8.de>");
Borislav Petkov9cdeb402010-09-02 18:33:24 +0200212MODULE_AUTHOR("AMD Inc.");
Borislav Petkovfd19fcd2014-11-22 11:09:12 +0100213MODULE_DESCRIPTION("MCE injection facility for RAS testing");