1/*
2 * Print table of MCA status bit combinations with results in HTML.
3 * Author: Andi Kleen
4 */
5#define _GNU_SOURCE 1
6#include <stdio.h>
7#include <stdlib.h>
8#include <stddef.h>
9#include <string.h>
10#include <assert.h>
11#include <unistd.h>
12#include <errno.h>
13#define __KERNEL__ 1
14#include <asm/types.h>
15#include <asm/mce.h>
16
17#define ARRAY_SIZE(x) (sizeof(x)/sizeof(*(x)))
18
19typedef unsigned long long u64;
20
21
22#define MCI_STATUS_S	 (1ULL<<56)  /* Signaled machine check */
23#define MCI_STATUS_AR	 (1ULL<<55)  /* Action required */
24
25int tolerant = 1;
26int panic_on_oops = 0;
27int mce_ser = 1;
28
29#include "mce-severity.c"
30
31int disable_opt = 0;
32
33struct rname {
34	char *name;
35	unsigned color;
36	char *desc;
37} rnames[] = {
38#define R(x,col,d) [MCE_ ## x ## _SEVERITY] = { #x, col, d }
39	R(NO, 0xc0c0c0, "Ignored"),
40	R(KEEP, 0x800080, "Ignore. Keep for CMC"),
41	R(SOME, 0x808080, "Log & Clear"),
42	R(AO, 0xffff00, "Kill address owner"),
43	R(UC, 0x700000, "Kill or panic"),
44	R(AR, 0x00ff00, "Kill current context"),
45	R(PANIC, 0xff0000, "Shutdown"),
46#undef R
47};
48
49struct bit {
50	char *name;
51	unsigned offset;
52	u64 bit;
53} bits[] = {
54#define O(x) offsetof(struct mce, x)
55#define S(x) { #x, O(status), MCI_STATUS_ ## x }
56	{ "RIPV", O(mcgstatus), MCG_STATUS_RIPV },
57	{ "EIPV", O(mcgstatus), MCG_STATUS_EIPV },
58	{ "MCIP", O(mcgstatus), MCG_STATUS_MCIP },
59	S(EN),
60	S(VAL),
61	S(UC),
62	S(S),
63	S(AR),
64	S(PCC),
65	S(OVER),
66	{ "SCRB-ERR", O(status), 0xc0 },
67#undef S
68#undef O
69};
70
71struct mce basem;
72
73#define bit_for_each(i,v) for (i = 0; i < 64; i++) if ((v) & (1ULL << i))
74
75struct result {
76	int res;
77	unsigned dontcare;
78	char *msg;
79};
80
81void genstate(struct mce *m, unsigned num)
82{
83	int i;
84	*m = basem;
85
86	bit_for_each (i, num)
87		*(u64 *)((char *)m + bits[i].offset) |= bits[i].bit;
88}
89
90// find don't care bits
91// brute force version because andi is not clever enough to make the clever
92// version work. luckily the tables are small
93
94#define for_rr(start, i) for (i = start; i < num; i++) if (rr[i].res >= 0)
95#define mask_of(x) ((1U << (x))-1)
96
97static void disable(struct result *rr, int i, int src)
98{
99	//fprintf(stderr, "disabling %d from %d\n", i, src);
100	rr[i].res = -1;
101}
102
103// handle case: one bit set always the same outcome
104static void one_bit_all(struct result *rr, int num, int mask)
105{
106	int first, k;
107	if (mask >= num)
108		return;
109	first = mask;
110	for_rr (first, k) {
111		if (!(k & mask))
112			continue;
113		if (rr[k].res != rr[first].res)
114			return;
115	}
116	rr[first].dontcare = mask_of(ARRAY_SIZE(bits)) & ~mask;
117	for_rr (first + 1, k) {
118		if (k & mask)
119			disable(rr, k, k);
120	}
121}
122
123// check if toggling one bit gives the same outcome
124static void neighbour_same(struct result *rr, int num, int mask)
125{
126	int k, other;
127	for_rr (mask, k) {
128		if (!(k & mask) || (rr[k].dontcare & mask))
129			continue;
130		other = k ^ mask;
131		if (other >= num)
132			continue;
133		if (rr[other].res == rr[k].res && rr[other].msg == rr[k].msg) {
134			disable(rr, other, k);
135			rr[k].dontcare |= mask;
136		}
137	}
138}
139
140void optimizer(struct result *rr, int num)
141{
142	int i;
143
144	for (i = 1; i <= 1 << ARRAY_SIZE(bits); i <<= 1)
145		one_bit_all(rr, num, i);
146	for (i = 1; i <= 1 << ARRAY_SIZE(bits); i <<= 1)
147		neighbour_same(rr, num, i);
148}
149
150int bitcount(u64 v)
151{
152	int num = 0;
153	while (v) {
154		if (v & 1)
155			num++;
156		v >>= 1;
157	}
158	return num;
159}
160
161void table(char *title)
162{
163	struct mce m;
164	int i, w, num;
165
166	struct result *rr = calloc(sizeof(struct result), 1U << ARRAY_SIZE(bits));
167
168	num = 0;
169	for (i = 0; i < 1U << ARRAY_SIZE(bits); i++) {
170		genstate(&m, i);
171		rr[num].res = mce_severity(&m, tolerant, &rr[num].msg);
172		num++;
173	}
174
175	if (!disable_opt)
176		optimizer(rr, num);
177
178	printf("<p><table border=1>\n");
179	printf("<chaption>%s</chaption>\n", title);
180
181	printf("<tr>\n");
182	for (i = 0; i < ARRAY_SIZE(bits); i++) {
183		printf("<th>%s</th>", bits[i].name);
184	}
185	printf("<th>Result</th><th>Rule</th><th>Action</th>\n");
186	printf("</tr>\n");
187
188	for_rr (0, i) {
189		printf("<tr>");
190		for (w = 0; w < ARRAY_SIZE(bits); w++) {
191			char *p = "0";
192			char *col = "";
193			unsigned mask = 1U << w;
194
195			if (mask & rr[i].dontcare) {
196				p = "x";
197				col = " bgcolor=\"888888\"";
198			} else if (mask & i) {
199				if (bitcount(bits[w].bit) > 1)
200					asprintf(&p, "%llx", bits[w].bit);
201				else
202					p = "1";
203 				col = " bgcolor=\"ffffff\"";
204			}
205			printf("<td%s>%s</td>", col, p);
206		}
207		struct rname *rname = &rnames[rr[i].res];
208		if ((unsigned)rr[i].res >= ARRAY_SIZE(rnames))
209			rname = &((struct rname) { .name = "out of bounds", .color = 0xff00ff });
210		assert(rname->name != NULL);
211		printf("<td bgcolor=\"%06x\">%s</td>", rname->color, rname->name);
212		assert(rr[i].msg != NULL);
213		printf("<td>%s</td>", rr[i].msg);
214		printf("<td>%s</td>", rname->desc);
215		printf("</tr>\n");
216	}
217	printf("</table>\n");
218}
219
220void usage(void)
221{
222	fprintf(stderr, "ttable [-a]\n"
223			"-a don't print don't care bits, but all states\n");
224	exit(1);
225}
226
227int main(int ac, char **av)
228{
229	int opt;
230	while ((opt = getopt(ac, av, "a")) != -1) {
231		switch (opt) {
232		case 'a':
233			disable_opt = 1;
234			break;
235		default:
236			usage();
237		}
238	}
239
240	printf("<html><body>\n");
241	printf("<!-- Auto generated. Changes will be overwritten -->\n");
242	basem.ip = 1;
243	printf("<h1>Linux kernel machine check grading</h1>\n");
244	printf("Caveats: Only scrubber error AO MCACOD. Only applies to exceptions.\n");
245	mce_ser = 1;
246	basem.cs = 0;
247	table("With MCA recovery ring 0");
248	tolerant = 0;
249	table("With MCA recovery ring 0 tolerant = 0");
250	tolerant = 1;
251	basem.cs = 3;
252	table("With MCA recovery ring 3");
253	basem.cs = 0;
254	mce_ser = 0;
255	table("Without MCA recovery ring 0");
256	basem.cs = 3;
257	table("Without MCA recovery ring 3");
258	printf("</body></html>\n");
259	return 0;
260}
261