1/*
2 * Copyright (c) 2002 Marcel Moolenaar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/types.h>
31#include <sys/mman.h>
32#include <sys/sysctl.h>
33#include <sys/uuid.h>
34
35/*
36 * Hack to make this compile on non-ia64 machines.
37 */
38#ifdef __ia64__
39#include <machine/mca.h>
40#else
41#include "../../sys/ia64/include/mca.h"
42#endif
43
44#include <err.h>
45#include <errno.h>
46#include <fcntl.h>
47#include <stdarg.h>
48#include <stdio.h>
49#include <stdlib.h>
50#include <string.h>
51#include <unistd.h>
52#include <uuid.h>
53
54#define	BCD(x)	((x >> 4) * 10 + (x & 15))
55
56#define	HW_MCA_MAX_CPUID	255
57
58static const char hw_mca_count[] = "hw.mca.count";
59static const char hw_mca_first[] = "hw.mca.first";
60static const char hw_mca_last[] = "hw.mca.last";
61static const char hw_mca_recid[] = "hw.mca.%d.%u";
62
63static char default_dumpfile[] = "/var/log/mca.log";
64
65int fl_dump;
66char *file;
67
68static const char *
69severity(int error)
70{
71
72	switch (error) {
73	case MCA_RH_ERROR_RECOVERABLE:
74		return ("recoverable");
75	case MCA_RH_ERROR_FATAL:
76		return ("fatal");
77	case MCA_RH_ERROR_CORRECTED:
78		return ("corrected");
79	}
80
81	return ("unknown");
82}
83
84static const char *
85uuid(uuid_t *id)
86{
87	static char buffer[64];
88	char *s;
89
90	uuid_to_string(id, &s, NULL);
91	strcpy(buffer, s);
92	free(s);
93	return (buffer);
94}
95
96static int
97show_value(int indent, const char *var, const char *fmt, ...)
98{
99	va_list ap;
100	int len;
101
102	len = indent;
103	while (indent--)
104		putchar(' ');
105	len += printf("<%s>", var);
106	va_start(ap, fmt);
107	len += vprintf(fmt, ap);
108	len += printf("</%s>\n", var);
109	return (len);
110}
111
112static size_t
113show_header(struct mca_record_header *rh)
114{
115
116	printf("  <header>\n");
117	show_value(4, "seqnr", "%lld", (long long)rh->rh_seqnr);
118	show_value(4, "revision", "%d.%d", BCD(rh->rh_major),
119	    BCD(rh->rh_minor));
120	show_value(4, "severity", "%s", severity(rh->rh_error));
121	show_value(4, "length", "%lld", (long long)rh->rh_length);
122	show_value(4, "date", "%d%02d/%02d/%02d",
123	    BCD(rh->rh_time[MCA_RH_TIME_CENT]),
124	    BCD(rh->rh_time[MCA_RH_TIME_YEAR]),
125	    BCD(rh->rh_time[MCA_RH_TIME_MON]),
126	    BCD(rh->rh_time[MCA_RH_TIME_MDAY]));
127	show_value(4, "time", "%02d:%02d:%02d",
128	    BCD(rh->rh_time[MCA_RH_TIME_HOUR]),
129	    BCD(rh->rh_time[MCA_RH_TIME_MIN]),
130	    BCD(rh->rh_time[MCA_RH_TIME_SEC]));
131	if (rh->rh_flags & MCA_RH_FLAGS_PLATFORM_ID)
132		show_value(4, "platform", "%s", uuid(&rh->rh_platform));
133	printf("  </header>\n");
134	return (rh->rh_length);
135}
136
137static void
138show_cpu_mod(const char *what, int idx, struct mca_cpu_mod *cpu_mod)
139{
140	printf("      <%s-%d>\n", what, idx);
141	if (cpu_mod->cpu_mod_flags & MCA_CPU_MOD_FLAGS_INFO)
142		show_value(8, "info", "0x%016llx",
143		    (long long)cpu_mod->cpu_mod_info);
144	if (cpu_mod->cpu_mod_flags & MCA_CPU_MOD_FLAGS_REQID)
145		show_value(8, "requester", "0x%016llx",
146		    (long long)cpu_mod->cpu_mod_reqid);
147	if (cpu_mod->cpu_mod_flags & MCA_CPU_MOD_FLAGS_RSPID)
148		show_value(8, "responder", "0x%016llx",
149		    (long long)cpu_mod->cpu_mod_rspid);
150	if (cpu_mod->cpu_mod_flags & MCA_CPU_MOD_FLAGS_TGTID)
151		show_value(8, "target", "0x%016llx",
152		    (long long)cpu_mod->cpu_mod_tgtid);
153	if (cpu_mod->cpu_mod_flags & MCA_CPU_MOD_FLAGS_IP)
154		show_value(8, "ip", "0x%016llx",
155		    (long long)cpu_mod->cpu_mod_ip);
156	printf("      </%s-%d>\n", what, idx);
157}
158
159static void
160show_cpu(struct mca_cpu_record *cpu)
161{
162	char var[16];
163	struct mca_cpu_mod *mod;
164	struct mca_cpu_cpuid *cpuid;
165#ifdef notyet
166	struct mca_cpu_psi *psi;
167#endif
168	int i, n;
169
170	printf("    <cpu>\n");
171
172	if (cpu->cpu_flags & MCA_CPU_FLAGS_ERRMAP)
173		show_value(6, "errmap", "0x%016llx",
174		    (long long)cpu->cpu_errmap);
175	if (cpu->cpu_flags & MCA_CPU_FLAGS_STATE)
176		show_value(6, "state", "0x%016llx",
177		    (long long)cpu->cpu_state);
178	if (cpu->cpu_flags & MCA_CPU_FLAGS_CR_LID)
179		show_value(6, "cr_lid", "0x%016llx",
180		    (long long)cpu->cpu_cr_lid);
181
182	mod = (struct mca_cpu_mod*)(cpu + 1);
183	n = MCA_CPU_FLAGS_CACHE(cpu->cpu_flags);
184	for (i = 0; i < n; i++)
185		show_cpu_mod("cache", i, mod++);
186	n = MCA_CPU_FLAGS_TLB(cpu->cpu_flags);
187	for (i = 0; i < n; i++)
188		show_cpu_mod("tlb", i, mod++);
189	n = MCA_CPU_FLAGS_BUS(cpu->cpu_flags);
190	for (i = 0; i < n; i++)
191		show_cpu_mod("bus", i, mod++);
192	n = MCA_CPU_FLAGS_REG(cpu->cpu_flags);
193	for (i = 0; i < n; i++)
194		show_cpu_mod("reg", i, mod++);
195	n = MCA_CPU_FLAGS_MS(cpu->cpu_flags);
196	for (i = 0; i < n; i++)
197		show_cpu_mod("ms", i, mod++);
198
199	cpuid = (struct mca_cpu_cpuid*)mod;
200	for (i = 0; i < 6; i++) {
201		sprintf(var, "cpuid-%d", i);
202		show_value(6, var, "0x%016llx", (long long)cpuid->cpuid[i]);
203	}
204
205#ifdef notyet
206	 psi = (struct mca_cpu_psi*)(cpuid + 1);
207#endif
208	 /* TODO: Dump PSI */
209
210	printf("    </cpu>\n");
211}
212
213static void
214show_memory(struct mca_mem_record *mem)
215{
216	printf("    <memory>\n");
217
218	if (mem->mem_flags & MCA_MEM_FLAGS_STATUS)
219		show_value(6, "status", "0x%016llx",
220		    (long long)mem->mem_status);
221	if (mem->mem_flags & MCA_MEM_FLAGS_ADDR)
222		show_value(6, "address", "0x%016llx",
223		    (long long)mem->mem_addr);
224	if (mem->mem_flags & MCA_MEM_FLAGS_ADDRMASK)
225		show_value(6, "mask", "0x%016llx",
226		    (long long)mem->mem_addrmask);
227	if (mem->mem_flags & MCA_MEM_FLAGS_NODE)
228		show_value(6, "node", "0x%04x", mem->mem_node);
229	if (mem->mem_flags & MCA_MEM_FLAGS_CARD)
230		show_value(6, "card", "0x%04x", mem->mem_card);
231	if (mem->mem_flags & MCA_MEM_FLAGS_MODULE)
232		show_value(6, "module", "0x%04x", mem->mem_module);
233	if (mem->mem_flags & MCA_MEM_FLAGS_BANK)
234		show_value(6, "bank", "0x%04x", mem->mem_bank);
235	if (mem->mem_flags & MCA_MEM_FLAGS_DEVICE)
236		show_value(6, "device", "0x%04x", mem->mem_device);
237	if (mem->mem_flags & MCA_MEM_FLAGS_ROW)
238		show_value(6, "row", "0x%04x", mem->mem_row);
239	if (mem->mem_flags & MCA_MEM_FLAGS_COLUMN)
240		show_value(6, "column", "0x%04x", mem->mem_column);
241	if (mem->mem_flags & MCA_MEM_FLAGS_BITPOS)
242		show_value(6, "bit", "0x%04x", mem->mem_bitpos);
243	if (mem->mem_flags & MCA_MEM_FLAGS_REQID)
244		show_value(6, "requester", "0x%016llx",
245		    (long long)mem->mem_reqid);
246	if (mem->mem_flags & MCA_MEM_FLAGS_RSPID)
247		show_value(6, "responder", "0x%016llx",
248		    (long long)mem->mem_rspid);
249	if (mem->mem_flags & MCA_MEM_FLAGS_TGTID)
250		show_value(6, "target", "0x%016llx",
251		    (long long)mem->mem_tgtid);
252	if (mem->mem_flags & MCA_MEM_FLAGS_BUSDATA)
253		show_value(6, "status", "0x%016llx",
254		    (long long)mem->mem_busdata);
255	if (mem->mem_flags & MCA_MEM_FLAGS_OEM_ID)
256		show_value(6, "oem", "%s", uuid(&mem->mem_oem_id));
257	/* TODO: Dump OEM data */
258
259	printf("    </memory>\n");
260}
261
262static void
263show_sel(void)
264{
265	printf("    # SEL\n");
266}
267
268static void
269show_pci_bus(struct mca_pcibus_record *pcibus)
270{
271	printf("    <pci-bus>\n");
272
273	if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_STATUS)
274		show_value(6, "status", "0x%016llx",
275		    (long long)pcibus->pcibus_status);
276	if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_ERROR)
277		show_value(6, "error", "0x%04x", pcibus->pcibus_error);
278	if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_BUS)
279		show_value(6, "bus", "0x%04x", pcibus->pcibus_bus);
280	if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_ADDR)
281		show_value(6, "address", "0x%016llx",
282		    (long long)pcibus->pcibus_addr);
283	if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_DATA)
284		show_value(6, "data", "0x%016llx",
285		    (long long)pcibus->pcibus_data);
286	if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_CMD)
287		show_value(6, "cmd", "0x%016llx",
288		    (long long)pcibus->pcibus_cmd);
289	if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_REQID)
290		show_value(6, "requester", "0x%016llx",
291		    (long long)pcibus->pcibus_reqid);
292	if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_RSPID)
293		show_value(6, "responder", "0x%016llx",
294		    (long long)pcibus->pcibus_rspid);
295	if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_TGTID)
296		show_value(6, "target", "0x%016llx",
297		    (long long)pcibus->pcibus_tgtid);
298	if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_OEM_ID)
299		show_value(6, "oem", "%s", uuid(&pcibus->pcibus_oem_id));
300	/* TODO: Dump OEM data */
301
302	printf("    </pci-bus>\n");
303}
304
305static void
306show_smbios(void)
307{
308	printf("    # SMBIOS\n");
309}
310
311static void
312show_pci_dev(struct mca_pcidev_record *pcidev)
313{
314	printf("    <pci-dev>\n");
315
316	if (pcidev->pcidev_flags & MCA_PCIDEV_FLAGS_STATUS)
317		show_value(6, "status", "0x%016llx",
318		    (long long)pcidev->pcidev_status);
319	if (pcidev->pcidev_flags & MCA_PCIDEV_FLAGS_INFO) {
320		show_value(6, "vendor", "0x%04x",
321		    pcidev->pcidev_info.info_vendor);
322		show_value(6, "device", "0x%04x",
323		    pcidev->pcidev_info.info_device);
324		show_value(6, "class", "0x%06x",
325		    MCA_PCIDEV_INFO_CLASS(pcidev->pcidev_info.info_ccfn));
326		show_value(6, "function", "0x%02x",
327		    MCA_PCIDEV_INFO_FUNCTION(pcidev->pcidev_info.info_ccfn));
328		show_value(6, "slot", "0x%02x", pcidev->pcidev_info.info_slot);
329		show_value(6, "bus", "0x%04x", pcidev->pcidev_info.info_bus);
330		show_value(6, "segment", "0x%04x",
331		    pcidev->pcidev_info.info_segment);
332	}
333	/* TODO: dump registers */
334	/* TODO: Dump OEM data */
335
336	printf("    </pci-dev>\n");
337}
338
339static void
340show_generic(void)
341{
342	printf("    # GENERIC\n");
343}
344
345static size_t
346show_section(struct mca_section_header *sh)
347{
348	static uuid_t uuid_cpu = MCA_UUID_CPU;
349	static uuid_t uuid_memory = MCA_UUID_MEMORY;
350	static uuid_t uuid_sel = MCA_UUID_SEL;
351	static uuid_t uuid_pci_bus = MCA_UUID_PCI_BUS;
352	static uuid_t uuid_smbios = MCA_UUID_SMBIOS;
353	static uuid_t uuid_pci_dev = MCA_UUID_PCI_DEV;
354	static uuid_t uuid_generic = MCA_UUID_GENERIC;
355
356	printf("  <section>\n");
357	show_value(4, "uuid", "%s", uuid(&sh->sh_uuid));
358	show_value(4, "revision", "%d.%d", BCD(sh->sh_major),
359	    BCD(sh->sh_minor));
360
361	if (uuid_equal(&sh->sh_uuid, &uuid_cpu, NULL))
362		show_cpu((void*)(sh + 1));
363	else if (uuid_equal(&sh->sh_uuid, &uuid_memory, NULL))
364		show_memory((void*)(sh + 1));
365	else if (uuid_equal(&sh->sh_uuid, &uuid_sel, NULL))
366		show_sel();
367	else if (uuid_equal(&sh->sh_uuid, &uuid_pci_bus, NULL))
368		show_pci_bus((void*)(sh + 1));
369	else if (uuid_equal(&sh->sh_uuid, &uuid_smbios, NULL))
370		show_smbios();
371	else if (uuid_equal(&sh->sh_uuid, &uuid_pci_dev, NULL))
372		show_pci_dev((void*)(sh + 1));
373	else if (uuid_equal(&sh->sh_uuid, &uuid_generic, NULL))
374		show_generic();
375
376	printf("  </section>\n");
377	return (sh->sh_length);
378}
379
380static void
381show(char *data, const char *mib)
382{
383	size_t reclen, seclen;
384
385	if (mib != NULL)
386		printf("<!-- MIB: %s -->\n", mib);
387
388	printf("<record>\n");
389	reclen = show_header((void*)data) - sizeof(struct mca_record_header);
390	data += sizeof(struct mca_record_header);
391	while (reclen > sizeof(struct mca_section_header)) {
392		seclen = show_section((void*)data);
393		reclen -= seclen;
394		data += seclen;
395	}
396	printf("</record>\n");
397}
398
399static void
400showall(char *buf, size_t buflen)
401{
402	struct mca_record_header *rh;
403	size_t reclen;
404
405	do {
406		if (buflen < sizeof(struct mca_record_header))
407			return;
408
409		rh = (void*)buf;
410		reclen = rh->rh_length;
411		if (buflen < reclen)
412			return;
413
414		show(buf, NULL);
415
416		buf += reclen;
417		buflen -= reclen;
418	}
419	while (1);
420}
421
422static void
423dump(char *data)
424{
425	struct mca_record_header *rh;
426	const char *fn;
427	int fd;
428
429	rh = (void*)data;
430	fn = (file) ? file : default_dumpfile;
431	fd = open(fn, O_WRONLY|O_CREAT|O_APPEND, 0660);
432	if (fd == -1)
433		err(2, "open(%s)", fn);
434	if (write(fd, (void*)rh, rh->rh_length) == -1)
435		err(2, "write(%s)", fn);
436	close(fd);
437}
438
439static void
440usage(void)
441{
442
443	fprintf(stderr, "usage: mca [-df]\n");
444	exit (1);
445}
446
447int
448main(int argc, char **argv)
449{
450	char mib[32];
451	char *buf;
452	size_t len;
453	int ch, error, fd;
454	int count, first, last, cpuid;
455
456	while ((ch = getopt(argc, argv, "df:")) != -1) {
457		switch(ch) {
458		case 'd':	/* dump */
459			fl_dump = 1;
460			break;
461		case 'f':
462			if (file)
463				free(file);		/* XXX complain! */
464			file = strdup(optarg);
465			break;
466		default:
467			usage();
468		}
469	}
470
471	argc -= optind;
472	argv += optind;
473
474	if (file == NULL || fl_dump) {
475		len = sizeof(count);
476		if (sysctlbyname(hw_mca_count, &count, &len, NULL, 0) == -1)
477			err(1, hw_mca_count);
478
479		if (count == 0)
480			errx(0, "no error records found");
481
482		len = sizeof(first);
483		if (sysctlbyname(hw_mca_first, &first, &len, NULL, 0) == -1)
484			err(1, hw_mca_first);
485
486		len = sizeof(last);
487		if (sysctlbyname(hw_mca_last, &last, &len, NULL, 0) == -1)
488			err(1, hw_mca_last);
489
490		cpuid = 0;
491		error = 0;
492		while (count && first <= last) {
493			do {
494				sprintf(mib, hw_mca_recid, first, cpuid);
495				len = 0;
496				ch = sysctlbyname(mib, NULL, &len, NULL, 0);
497				error = (ch == -1) ? errno : 0;
498				if (error != ENOENT)
499					break;
500				cpuid++;
501			} while (cpuid <= HW_MCA_MAX_CPUID);
502			if (error == ENOENT && cpuid > HW_MCA_MAX_CPUID) {
503				first++;
504				cpuid = 0;
505				continue;
506			}
507			if (error)
508				errc(1, error, "%s(1)", mib);
509
510			buf = malloc(len);
511			if (buf == NULL)
512				err(1, "buffer");
513
514			if (sysctlbyname(mib, buf, &len, NULL, 0) == -1)
515				err(1, "%s(2)", mib);
516
517			if (fl_dump)
518				dump(buf);
519			else
520				show(buf, mib);
521
522			free(buf);
523			count--;
524			if (cpuid == HW_MCA_MAX_CPUID) {
525				first++;
526				cpuid = 0;
527			} else
528				cpuid++;
529		}
530	} else {
531		fd = open(file, O_RDONLY);
532		if (fd == -1)
533			err(1, "open(%s)", file);
534
535		len = lseek(fd, 0LL, SEEK_END);
536		buf = mmap(NULL, len, PROT_READ, 0U, fd, 0LL);
537		if (buf == MAP_FAILED)
538			err(1, "mmap(%s)", file);
539
540		showall(buf, len);
541
542		munmap(buf, len);
543		close(fd);
544	}
545
546	return (0);
547}
548