mce-severity.c 7.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
/*
 * MCE grading rules.
 * Copyright 2008, 2009 Intel Corporation.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; version 2
 * of the License.
 *
 * Author: Andi Kleen
 */
#include <linux/kernel.h>
13 14 15
#include <linux/seq_file.h>
#include <linux/init.h>
#include <linux/debugfs.h>
16 17 18 19 20 21 22 23 24
#include <asm/mce.h>

#include "mce-internal.h"

/*
 * Grade an mce by severity. In general the most severe ones are processed
 * first. Since there are quite a lot of combinations test the bits in a
 * table-driven way. The rules are simply processed in order, first
 * match wins.
A
Andi Kleen 已提交
25 26 27 28 29
 *
 * Note this is only used for machine check exceptions, the corrected
 * errors use much simpler rules. The exceptions still check for the corrected
 * errors, but only to leave them alone for the CMCI handler (except for
 * panic situations)
30 31
 */

A
Andi Kleen 已提交
32 33 34
enum context { IN_KERNEL = 1, IN_USER = 2 };
enum ser { SER_REQUIRED = 1, NO_SER = 2 };

35 36 37 38 39 40
static struct severity {
	u64 mask;
	u64 result;
	unsigned char sev;
	unsigned char mcgmask;
	unsigned char mcgres;
A
Andi Kleen 已提交
41 42
	unsigned char ser;
	unsigned char context;
43
	unsigned char covered;
44 45
	char *msg;
} severities[] = {
46 47 48 49 50 51 52 53 54
#define MCESEV(s, m, c...) { .sev = MCE_ ## s ## _SEVERITY, .msg = m, ## c }
#define  KERNEL		.context = IN_KERNEL
#define  USER		.context = IN_USER
#define  SER		.ser = SER_REQUIRED
#define  NOSER		.ser = NO_SER
#define  BITCLR(x)	.mask = x, .result = 0
#define  BITSET(x)	.mask = x, .result = x
#define  MCGMASK(x, y)	.mcgmask = x, .mcgres = y
#define  MASK(x, y)	.mask = x, .result = y
A
Andi Kleen 已提交
55 56
#define MCI_UC_S (MCI_STATUS_UC|MCI_STATUS_S)
#define MCI_UC_SAR (MCI_STATUS_UC|MCI_STATUS_S|MCI_STATUS_AR)
57
#define	MCI_ADDR (MCI_STATUS_ADDRV|MCI_STATUS_MISCV)
A
Andi Kleen 已提交
58

59 60 61
	MCESEV(
		NO, "Invalid",
		BITCLR(MCI_STATUS_VAL)
62
		),
63 64 65
	MCESEV(
		NO, "Not enabled",
		BITCLR(MCI_STATUS_EN)
66
		),
67 68 69
	MCESEV(
		PANIC, "Processor context corrupt",
		BITSET(MCI_STATUS_PCC)
70
		),
A
Andi Kleen 已提交
71
	/* When MCIP is not set something is very confused */
72 73 74
	MCESEV(
		PANIC, "MCIP not set in MCA handler",
		MCGMASK(MCG_STATUS_MCIP, 0)
75
		),
A
Andi Kleen 已提交
76
	/* Neither return not error IP -- no chance to recover -> PANIC */
77 78 79
	MCESEV(
		PANIC, "Neither restart nor error IP",
		MCGMASK(MCG_STATUS_RIPV|MCG_STATUS_EIPV, 0)
80
		),
81
	MCESEV(
82
		PANIC, "In kernel and no restart IP",
83
		KERNEL, MCGMASK(MCG_STATUS_RIPV, 0)
84
		),
85
	MCESEV(
86
		KEEP, "Corrected error",
87
		NOSER, BITCLR(MCI_STATUS_UC)
88
		),
A
Andi Kleen 已提交
89 90

	/* ignore OVER for UCNA */
91
	MCESEV(
92
		KEEP, "Uncorrected no action required",
93
		SER, MASK(MCI_UC_SAR, MCI_STATUS_UC)
94
		),
95
	MCESEV(
96
		PANIC, "Illegal combination (UCNA with AR=1)",
97 98
		SER,
		MASK(MCI_STATUS_OVER|MCI_UC_SAR, MCI_STATUS_UC|MCI_STATUS_AR)
99
		),
100
	MCESEV(
101
		KEEP, "Non signalled machine check",
102
		SER, BITCLR(MCI_STATUS_S)
103
		),
A
Andi Kleen 已提交
104

105
	MCESEV(
106
		PANIC, "Action required with lost events",
107
		SER, BITSET(MCI_STATUS_OVER|MCI_UC_SAR)
108
		),
109 110 111 112 113

	/* known AR MCACODs: */
#ifdef	CONFIG_MEMORY_FAILURE
	MCESEV(
		KEEP, "HT thread notices Action required: data load error",
114
		SER, MASK(MCI_STATUS_OVER|MCI_UC_SAR|MCI_ADDR|MCACOD, MCI_UC_SAR|MCI_ADDR|MCACOD_DATA),
115 116 117 118
		MCGMASK(MCG_STATUS_EIPV, 0)
		),
	MCESEV(
		AR, "Action required: data load error",
119
		SER, MASK(MCI_STATUS_OVER|MCI_UC_SAR|MCI_ADDR|MCACOD, MCI_UC_SAR|MCI_ADDR|MCACOD_DATA),
120 121
		USER
		),
122 123 124 125 126 127 128 129 130 131
	MCESEV(
		KEEP, "HT thread notices Action required: instruction fetch error",
		SER, MASK(MCI_STATUS_OVER|MCI_UC_SAR|MCI_ADDR|MCACOD, MCI_UC_SAR|MCI_ADDR|MCACOD_INSTR),
		MCGMASK(MCG_STATUS_EIPV, 0)
		),
	MCESEV(
		AR, "Action required: instruction fetch error",
		SER, MASK(MCI_STATUS_OVER|MCI_UC_SAR|MCI_ADDR|MCACOD, MCI_UC_SAR|MCI_ADDR|MCACOD_INSTR),
		USER
		),
132
#endif
133
	MCESEV(
134
		PANIC, "Action required: unknown MCACOD",
135
		SER, MASK(MCI_STATUS_OVER|MCI_UC_SAR, MCI_UC_SAR)
136
		),
A
Andi Kleen 已提交
137 138

	/* known AO MCACODs: */
139
	MCESEV(
140
		AO, "Action optional: memory scrubbing error",
141
		SER, MASK(MCI_STATUS_OVER|MCI_UC_SAR|MCACOD_SCRUBMSK, MCI_UC_S|MCACOD_SCRUB)
142
		),
143
	MCESEV(
144
		AO, "Action optional: last level cache writeback error",
145
		SER, MASK(MCI_STATUS_OVER|MCI_UC_SAR|MCACOD, MCI_UC_S|MCACOD_L3WB)
146
		),
147
	MCESEV(
148
		SOME, "Action optional: unknown MCACOD",
149
		SER, MASK(MCI_STATUS_OVER|MCI_UC_SAR, MCI_UC_S)
150
		),
151
	MCESEV(
152
		SOME, "Action optional with lost events",
153
		SER, MASK(MCI_STATUS_OVER|MCI_UC_SAR, MCI_STATUS_OVER|MCI_UC_S)
154
		),
155 156 157

	MCESEV(
		PANIC, "Overflowed uncorrected",
158
		BITSET(MCI_STATUS_OVER|MCI_STATUS_UC)
159
		),
160 161 162
	MCESEV(
		UC, "Uncorrected",
		BITSET(MCI_STATUS_UC)
163
		),
164 165 166
	MCESEV(
		SOME, "No match",
		BITSET(0)
167
		)	/* always matches. keep at end */
168 169
};

A
Andi Kleen 已提交
170
/*
171 172 173 174 175 176 177 178 179
 * If mcgstatus indicated that ip/cs on the stack were
 * no good, then "m->cs" will be zero and we will have
 * to assume the worst case (IN_KERNEL) as we actually
 * have no idea what we were executing when the machine
 * check hit.
 * If we do have a good "m->cs" (or a faked one in the
 * case we were executing in VM86 mode) we can use it to
 * distinguish an exception taken in user from from one
 * taken in the kernel.
A
Andi Kleen 已提交
180 181 182
 */
static int error_context(struct mce *m)
{
183
	return ((m->cs & 3) == 3) ? IN_USER : IN_KERNEL;
A
Andi Kleen 已提交
184 185
}

186
int mce_severity(struct mce *m, int tolerant, char **msg)
187
{
188
	enum context ctx = error_context(m);
189
	struct severity *s;
A
Andi Kleen 已提交
190

191
	for (s = severities;; s++) {
192
		if ((m->status & s->mask) != s->result)
193
			continue;
194
		if ((m->mcgstatus & s->mcgmask) != s->mcgres)
195
			continue;
196
		if (s->ser == SER_REQUIRED && !mca_cfg.ser)
A
Andi Kleen 已提交
197
			continue;
198
		if (s->ser == NO_SER && mca_cfg.ser)
A
Andi Kleen 已提交
199 200 201
			continue;
		if (s->context && ctx != s->context)
			continue;
202 203
		if (msg)
			*msg = s->msg;
204
		s->covered = 1;
A
Andi Kleen 已提交
205 206 207 208
		if (s->sev >= MCE_UC_SEVERITY && ctx == IN_KERNEL) {
			if (panic_on_oops || tolerant < 1)
				return MCE_PANIC_SEVERITY;
		}
209 210 211
		return s->sev;
	}
}
212

213
#ifdef CONFIG_DEBUG_FS
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
static void *s_start(struct seq_file *f, loff_t *pos)
{
	if (*pos >= ARRAY_SIZE(severities))
		return NULL;
	return &severities[*pos];
}

static void *s_next(struct seq_file *f, void *data, loff_t *pos)
{
	if (++(*pos) >= ARRAY_SIZE(severities))
		return NULL;
	return &severities[*pos];
}

static void s_stop(struct seq_file *f, void *data)
{
}

static int s_show(struct seq_file *f, void *data)
{
	struct severity *ser = data;
	seq_printf(f, "%d\t%s\n", ser->covered, ser->msg);
	return 0;
}

static const struct seq_operations severities_seq_ops = {
	.start	= s_start,
	.next	= s_next,
	.stop	= s_stop,
	.show	= s_show,
};

static int severities_coverage_open(struct inode *inode, struct file *file)
{
	return seq_open(file, &severities_seq_ops);
}

static ssize_t severities_coverage_write(struct file *file,
					 const char __user *ubuf,
					 size_t count, loff_t *ppos)
{
	int i;
	for (i = 0; i < ARRAY_SIZE(severities); i++)
		severities[i].covered = 0;
	return count;
}

static const struct file_operations severities_coverage_fops = {
	.open		= severities_coverage_open,
	.release	= seq_release,
	.read		= seq_read,
	.write		= severities_coverage_write,
266
	.llseek		= seq_lseek,
267 268 269 270
};

static int __init severities_debugfs_init(void)
{
271
	struct dentry *dmce, *fsev;
272

273
	dmce = mce_get_debugfs_dir();
274
	if (!dmce)
275
		goto err_out;
276 277 278 279

	fsev = debugfs_create_file("severities-coverage", 0444, dmce, NULL,
				   &severities_coverage_fops);
	if (!fsev)
280 281 282 283 284 285 286 287
		goto err_out;

	return 0;

err_out:
	return -ENOMEM;
}
late_initcall(severities_debugfs_init);
288
#endif /* CONFIG_DEBUG_FS */