mce-severity.c 7.0 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

	/* known AR MCACODs: */
#ifdef	CONFIG_MEMORY_FAILURE
	MCESEV(
113
		KEEP, "Action required but unaffected thread is continuable",
114 115
		SER, MASK(MCI_STATUS_OVER|MCI_UC_SAR|MCI_ADDR, MCI_UC_SAR|MCI_ADDR),
		MCGMASK(MCG_STATUS_RIPV|MCG_STATUS_EIPV, MCG_STATUS_RIPV)
116 117
		),
	MCESEV(
118
		AR, "Action required: data load error in a user process",
119
		SER, MASK(MCI_STATUS_OVER|MCI_UC_SAR|MCI_ADDR|MCACOD, MCI_UC_SAR|MCI_ADDR|MCACOD_DATA),
120 121
		USER
		),
122
	MCESEV(
123
		AR, "Action required: instruction fetch error in a user process",
124 125 126
		SER, MASK(MCI_STATUS_OVER|MCI_UC_SAR|MCI_ADDR|MCACOD, MCI_UC_SAR|MCI_ADDR|MCACOD_INSTR),
		USER
		),
127
#endif
128
	MCESEV(
129
		PANIC, "Action required: unknown MCACOD",
130
		SER, MASK(MCI_STATUS_OVER|MCI_UC_SAR, MCI_UC_SAR)
131
		),
A
Andi Kleen 已提交
132 133

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

	MCESEV(
		PANIC, "Overflowed uncorrected",
153
		BITSET(MCI_STATUS_OVER|MCI_STATUS_UC)
154
		),
155 156 157
	MCESEV(
		UC, "Uncorrected",
		BITSET(MCI_STATUS_UC)
158
		),
159 160 161
	MCESEV(
		SOME, "No match",
		BITSET(0)
162
		)	/* always matches. keep at end */
163 164
};

A
Andi Kleen 已提交
165
/*
166 167 168 169 170 171 172 173 174
 * 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 已提交
175 176 177
 */
static int error_context(struct mce *m)
{
178
	return ((m->cs & 3) == 3) ? IN_USER : IN_KERNEL;
A
Andi Kleen 已提交
179 180
}

181
int mce_severity(struct mce *m, int tolerant, char **msg)
182
{
183
	enum context ctx = error_context(m);
184
	struct severity *s;
A
Andi Kleen 已提交
185

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

208
#ifdef CONFIG_DEBUG_FS
209 210 211 212 213 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
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,
261
	.llseek		= seq_lseek,
262 263 264 265
};

static int __init severities_debugfs_init(void)
{
266
	struct dentry *dmce, *fsev;
267

268
	dmce = mce_get_debugfs_dir();
269
	if (!dmce)
270
		goto err_out;
271 272 273 274

	fsev = debugfs_create_file("severities-coverage", 0444, dmce, NULL,
				   &severities_coverage_fops);
	if (!fsev)
275 276 277 278 279 280 281 282
		goto err_out;

	return 0;

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