Commit | Line | Data |
---|---|---|
817f32d0 AK |
1 | /* |
2 | * MCE grading rules. | |
3 | * Copyright 2008, 2009 Intel Corporation. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or | |
6 | * modify it under the terms of the GNU General Public License | |
7 | * as published by the Free Software Foundation; version 2 | |
8 | * of the License. | |
9 | * | |
10 | * Author: Andi Kleen | |
11 | */ | |
12 | #include <linux/kernel.h> | |
4611a6fa HY |
13 | #include <linux/seq_file.h> |
14 | #include <linux/init.h> | |
15 | #include <linux/debugfs.h> | |
817f32d0 AK |
16 | #include <asm/mce.h> |
17 | ||
18 | #include "mce-internal.h" | |
19 | ||
20 | /* | |
21 | * Grade an mce by severity. In general the most severe ones are processed | |
22 | * first. Since there are quite a lot of combinations test the bits in a | |
23 | * table-driven way. The rules are simply processed in order, first | |
24 | * match wins. | |
ed7290d0 AK |
25 | * |
26 | * Note this is only used for machine check exceptions, the corrected | |
27 | * errors use much simpler rules. The exceptions still check for the corrected | |
28 | * errors, but only to leave them alone for the CMCI handler (except for | |
29 | * panic situations) | |
817f32d0 AK |
30 | */ |
31 | ||
ed7290d0 AK |
32 | enum context { IN_KERNEL = 1, IN_USER = 2 }; |
33 | enum ser { SER_REQUIRED = 1, NO_SER = 2 }; | |
34 | ||
817f32d0 AK |
35 | static struct severity { |
36 | u64 mask; | |
37 | u64 result; | |
38 | unsigned char sev; | |
39 | unsigned char mcgmask; | |
40 | unsigned char mcgres; | |
ed7290d0 AK |
41 | unsigned char ser; |
42 | unsigned char context; | |
4611a6fa | 43 | unsigned char covered; |
817f32d0 AK |
44 | char *msg; |
45 | } severities[] = { | |
ed7290d0 AK |
46 | #define KERNEL .context = IN_KERNEL |
47 | #define USER .context = IN_USER | |
48 | #define SER .ser = SER_REQUIRED | |
49 | #define NOSER .ser = NO_SER | |
817f32d0 AK |
50 | #define SEV(s) .sev = MCE_ ## s ## _SEVERITY |
51 | #define BITCLR(x, s, m, r...) { .mask = x, .result = 0, SEV(s), .msg = m, ## r } | |
52 | #define BITSET(x, s, m, r...) { .mask = x, .result = x, SEV(s), .msg = m, ## r } | |
53 | #define MCGMASK(x, res, s, m, r...) \ | |
54 | { .mcgmask = x, .mcgres = res, SEV(s), .msg = m, ## r } | |
ed7290d0 AK |
55 | #define MASK(x, y, s, m, r...) \ |
56 | { .mask = x, .result = y, SEV(s), .msg = m, ## r } | |
57 | #define MCI_UC_S (MCI_STATUS_UC|MCI_STATUS_S) | |
58 | #define MCI_UC_SAR (MCI_STATUS_UC|MCI_STATUS_S|MCI_STATUS_AR) | |
59 | #define MCACOD 0xffff | |
60 | ||
817f32d0 AK |
61 | BITCLR(MCI_STATUS_VAL, NO, "Invalid"), |
62 | BITCLR(MCI_STATUS_EN, NO, "Not enabled"), | |
63 | BITSET(MCI_STATUS_PCC, PANIC, "Processor context corrupt"), | |
ed7290d0 AK |
64 | /* When MCIP is not set something is very confused */ |
65 | MCGMASK(MCG_STATUS_MCIP, 0, PANIC, "MCIP not set in MCA handler"), | |
66 | /* Neither return not error IP -- no chance to recover -> PANIC */ | |
67 | MCGMASK(MCG_STATUS_RIPV|MCG_STATUS_EIPV, 0, PANIC, | |
68 | "Neither restart nor error IP"), | |
69 | MCGMASK(MCG_STATUS_RIPV, 0, PANIC, "In kernel and no restart IP", | |
70 | KERNEL), | |
71 | BITCLR(MCI_STATUS_UC, KEEP, "Corrected error", NOSER), | |
72 | MASK(MCI_STATUS_OVER|MCI_STATUS_UC|MCI_STATUS_EN, MCI_STATUS_UC, SOME, | |
73 | "Spurious not enabled", SER), | |
74 | ||
75 | /* ignore OVER for UCNA */ | |
76 | MASK(MCI_UC_SAR, MCI_STATUS_UC, KEEP, | |
77 | "Uncorrected no action required", SER), | |
78 | MASK(MCI_STATUS_OVER|MCI_UC_SAR, MCI_STATUS_UC|MCI_STATUS_AR, PANIC, | |
79 | "Illegal combination (UCNA with AR=1)", SER), | |
80 | MASK(MCI_STATUS_S, 0, KEEP, "Non signalled machine check", SER), | |
81 | ||
82 | /* AR add known MCACODs here */ | |
83 | MASK(MCI_STATUS_OVER|MCI_UC_SAR, MCI_STATUS_OVER|MCI_UC_SAR, PANIC, | |
84 | "Action required with lost events", SER), | |
85 | MASK(MCI_STATUS_OVER|MCI_UC_SAR|MCACOD, MCI_UC_SAR, PANIC, | |
86 | "Action required; unknown MCACOD", SER), | |
87 | ||
88 | /* known AO MCACODs: */ | |
89 | MASK(MCI_UC_SAR|MCI_STATUS_OVER|0xfff0, MCI_UC_S|0xc0, AO, | |
90 | "Action optional: memory scrubbing error", SER), | |
91 | MASK(MCI_UC_SAR|MCI_STATUS_OVER|MCACOD, MCI_UC_S|0x17a, AO, | |
92 | "Action optional: last level cache writeback error", SER), | |
93 | ||
94 | MASK(MCI_STATUS_OVER|MCI_UC_SAR, MCI_UC_S, SOME, | |
95 | "Action optional unknown MCACOD", SER), | |
96 | MASK(MCI_STATUS_OVER|MCI_UC_SAR, MCI_UC_S|MCI_STATUS_OVER, SOME, | |
97 | "Action optional with lost events", SER), | |
817f32d0 AK |
98 | BITSET(MCI_STATUS_UC|MCI_STATUS_OVER, PANIC, "Overflowed uncorrected"), |
99 | BITSET(MCI_STATUS_UC, UC, "Uncorrected"), | |
100 | BITSET(0, SOME, "No match") /* always matches. keep at end */ | |
101 | }; | |
102 | ||
ed7290d0 AK |
103 | /* |
104 | * If the EIPV bit is set, it means the saved IP is the | |
105 | * instruction which caused the MCE. | |
106 | */ | |
107 | static int error_context(struct mce *m) | |
108 | { | |
109 | if (m->mcgstatus & MCG_STATUS_EIPV) | |
110 | return (m->ip && (m->cs & 3) == 3) ? IN_USER : IN_KERNEL; | |
111 | /* Unknown, assume kernel */ | |
112 | return IN_KERNEL; | |
113 | } | |
114 | ||
817f32d0 AK |
115 | int mce_severity(struct mce *a, int tolerant, char **msg) |
116 | { | |
ed7290d0 | 117 | enum context ctx = error_context(a); |
817f32d0 | 118 | struct severity *s; |
ed7290d0 | 119 | |
817f32d0 AK |
120 | for (s = severities;; s++) { |
121 | if ((a->status & s->mask) != s->result) | |
122 | continue; | |
123 | if ((a->mcgstatus & s->mcgmask) != s->mcgres) | |
124 | continue; | |
ed7290d0 AK |
125 | if (s->ser == SER_REQUIRED && !mce_ser) |
126 | continue; | |
127 | if (s->ser == NO_SER && mce_ser) | |
128 | continue; | |
129 | if (s->context && ctx != s->context) | |
130 | continue; | |
817f32d0 AK |
131 | if (msg) |
132 | *msg = s->msg; | |
4611a6fa | 133 | s->covered = 1; |
ed7290d0 AK |
134 | if (s->sev >= MCE_UC_SEVERITY && ctx == IN_KERNEL) { |
135 | if (panic_on_oops || tolerant < 1) | |
136 | return MCE_PANIC_SEVERITY; | |
137 | } | |
817f32d0 AK |
138 | return s->sev; |
139 | } | |
140 | } | |
4611a6fa | 141 | |
e34e77ce | 142 | #ifdef CONFIG_DEBUG_FS |
4611a6fa HY |
143 | static void *s_start(struct seq_file *f, loff_t *pos) |
144 | { | |
145 | if (*pos >= ARRAY_SIZE(severities)) | |
146 | return NULL; | |
147 | return &severities[*pos]; | |
148 | } | |
149 | ||
150 | static void *s_next(struct seq_file *f, void *data, loff_t *pos) | |
151 | { | |
152 | if (++(*pos) >= ARRAY_SIZE(severities)) | |
153 | return NULL; | |
154 | return &severities[*pos]; | |
155 | } | |
156 | ||
157 | static void s_stop(struct seq_file *f, void *data) | |
158 | { | |
159 | } | |
160 | ||
161 | static int s_show(struct seq_file *f, void *data) | |
162 | { | |
163 | struct severity *ser = data; | |
164 | seq_printf(f, "%d\t%s\n", ser->covered, ser->msg); | |
165 | return 0; | |
166 | } | |
167 | ||
168 | static const struct seq_operations severities_seq_ops = { | |
169 | .start = s_start, | |
170 | .next = s_next, | |
171 | .stop = s_stop, | |
172 | .show = s_show, | |
173 | }; | |
174 | ||
175 | static int severities_coverage_open(struct inode *inode, struct file *file) | |
176 | { | |
177 | return seq_open(file, &severities_seq_ops); | |
178 | } | |
179 | ||
180 | static ssize_t severities_coverage_write(struct file *file, | |
181 | const char __user *ubuf, | |
182 | size_t count, loff_t *ppos) | |
183 | { | |
184 | int i; | |
185 | for (i = 0; i < ARRAY_SIZE(severities); i++) | |
186 | severities[i].covered = 0; | |
187 | return count; | |
188 | } | |
189 | ||
190 | static const struct file_operations severities_coverage_fops = { | |
191 | .open = severities_coverage_open, | |
192 | .release = seq_release, | |
193 | .read = seq_read, | |
194 | .write = severities_coverage_write, | |
6038f373 | 195 | .llseek = seq_lseek, |
4611a6fa HY |
196 | }; |
197 | ||
198 | static int __init severities_debugfs_init(void) | |
199 | { | |
200 | struct dentry *dmce = NULL, *fseverities_coverage = NULL; | |
201 | ||
5be9ed25 | 202 | dmce = mce_get_debugfs_dir(); |
4611a6fa HY |
203 | if (dmce == NULL) |
204 | goto err_out; | |
205 | fseverities_coverage = debugfs_create_file("severities-coverage", | |
206 | 0444, dmce, NULL, | |
207 | &severities_coverage_fops); | |
208 | if (fseverities_coverage == NULL) | |
209 | goto err_out; | |
210 | ||
211 | return 0; | |
212 | ||
213 | err_out: | |
4611a6fa HY |
214 | return -ENOMEM; |
215 | } | |
216 | late_initcall(severities_debugfs_init); | |
e34e77ce | 217 | #endif |