mca.c revision 105309
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: head/sbin/mca/mca.c 105309 2002-10-17 05:41:10Z marcel $");
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
53#define	BCD(x)	((x >> 4) * 10 + (x & 15))
54
55static char hw_mca_count[] = "hw.mca.count";
56static char hw_mca_first[] = "hw.mca.first";
57static char hw_mca_last[] = "hw.mca.last";
58static char hw_mca_recid[] = "hw.mca.%d";
59
60static char default_dumpfile[] = "/var/log/mca.log";
61
62int fl_dump;
63char *file;
64
65static const char *
66severity(int error)
67{
68
69	switch (error) {
70	case MCA_RH_ERROR_RECOVERABLE:
71		return ("recoverable");
72	case MCA_RH_ERROR_FATAL:
73		return ("fatal");
74	case MCA_RH_ERROR_CORRECTED:
75		return ("corrected");
76	}
77
78	return ("unknown");
79}
80
81static const char *
82uuid(struct uuid *id)
83{
84	static char buffer[64];
85
86	sprintf(buffer, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
87	    id->time_low, id->time_mid, id->time_hi_and_version,
88	    id->clock_seq_hi_and_reserved, id->clock_seq_low, id->node[0],
89	    id->node[1], id->node[2], id->node[3], id->node[4], id->node[5]);
90	return (buffer);
91}
92
93static int
94show_value(int indent, const char *var, const char *fmt, ...)
95{
96	va_list ap;
97	int len;
98
99	len = indent;
100	while (indent--)
101		putchar(' ');
102	len += printf("<%s>", var);
103	va_start(ap, fmt);
104	len += vprintf(fmt, ap);
105	len += printf("</%s>\n", var);
106	return (len);
107}
108
109static size_t
110show_header(struct mca_record_header *rh)
111{
112
113	printf("  <header>\n");
114	show_value(4, "seqnr", "%lld", (long long)rh->rh_seqnr);
115	show_value(4, "revision", "%d.%d", BCD(rh->rh_major),
116	    BCD(rh->rh_minor));
117	show_value(4, "severity", "%s", severity(rh->rh_error));
118	show_value(4, "length", "%lld", (long long)rh->rh_length);
119	show_value(4, "date", "%d%02d/%02d/%02d",
120	    BCD(rh->rh_time[MCA_RH_TIME_CENT]),
121	    BCD(rh->rh_time[MCA_RH_TIME_YEAR]),
122	    BCD(rh->rh_time[MCA_RH_TIME_MON]),
123	    BCD(rh->rh_time[MCA_RH_TIME_MDAY]));
124	show_value(4, "time", "%02d:%02d:%02d",
125	    BCD(rh->rh_time[MCA_RH_TIME_HOUR]),
126	    BCD(rh->rh_time[MCA_RH_TIME_MIN]),
127	    BCD(rh->rh_time[MCA_RH_TIME_SEC]));
128	if (rh->rh_flags & MCA_RH_FLAGS_PLATFORM_ID)
129		show_value(4, "platform", "%s", uuid(&rh->rh_platform));
130	printf("  </header>\n");
131	return (rh->rh_length);
132}
133
134static void
135show_cpu_mod(const char *what, int idx, struct mca_cpu_mod *cpu_mod)
136{
137	printf("      <%s-%d>\n", what, idx);
138	if (cpu_mod->cpu_mod_flags & MCA_CPU_MOD_FLAGS_INFO)
139		show_value(8, "info", "0x%016llx",
140		    (long long)cpu_mod->cpu_mod_info);
141	if (cpu_mod->cpu_mod_flags & MCA_CPU_MOD_FLAGS_REQID)
142		show_value(8, "requester", "0x%016llx",
143		    (long long)cpu_mod->cpu_mod_reqid);
144	if (cpu_mod->cpu_mod_flags & MCA_CPU_MOD_FLAGS_RSPID)
145		show_value(8, "responder", "0x%016llx",
146		    (long long)cpu_mod->cpu_mod_rspid);
147	if (cpu_mod->cpu_mod_flags & MCA_CPU_MOD_FLAGS_TGTID)
148		show_value(8, "target", "0x%016llx",
149		    (long long)cpu_mod->cpu_mod_tgtid);
150	if (cpu_mod->cpu_mod_flags & MCA_CPU_MOD_FLAGS_IP)
151		show_value(8, "ip", "0x%016llx",
152		    (long long)cpu_mod->cpu_mod_ip);
153	printf("      </%s-%d>\n", what, idx);
154}
155
156static void
157show_cpu(struct mca_cpu_record *cpu)
158{
159	char var[16];
160	struct mca_cpu_mod *mod;
161	struct mca_cpu_cpuid *cpuid;
162	struct mca_cpu_psi *psi;
163	int i, n;
164
165	printf("    <cpu>\n");
166
167	if (cpu->cpu_flags & MCA_CPU_FLAGS_ERRMAP)
168		show_value(6, "errmap", "0x%016llx",
169		    (long long)cpu->cpu_errmap);
170	if (cpu->cpu_flags & MCA_CPU_FLAGS_STATE)
171		show_value(6, "state", "0x%016llx",
172		    (long long)cpu->cpu_state);
173	if (cpu->cpu_flags & MCA_CPU_FLAGS_CR_LID)
174		show_value(6, "cr_lid", "0x%016llx",
175		    (long long)cpu->cpu_cr_lid);
176
177	mod = (struct mca_cpu_mod*)(cpu + 1);
178	n = MCA_CPU_FLAGS_CACHE(cpu->cpu_flags);
179	for (i = 0; i < n; i++)
180		show_cpu_mod("cache", i, mod++);
181	n = MCA_CPU_FLAGS_TLB(cpu->cpu_flags);
182	for (i = 0; i < n; i++)
183		show_cpu_mod("tlb", i, mod++);
184	n = MCA_CPU_FLAGS_BUS(cpu->cpu_flags);
185	for (i = 0; i < n; i++)
186		show_cpu_mod("bus", i, mod++);
187	n = MCA_CPU_FLAGS_REG(cpu->cpu_flags);
188	for (i = 0; i < n; i++)
189		show_cpu_mod("reg", i, mod++);
190	n = MCA_CPU_FLAGS_MS(cpu->cpu_flags);
191	for (i = 0; i < n; i++)
192		show_cpu_mod("ms", i, mod++);
193
194	cpuid = (struct mca_cpu_cpuid*)mod;
195	for (i = 0; i < 6; i++) {
196		sprintf(var, "cpuid-%d", i);
197		show_value(6, var, "0x%016llx", (long long)cpuid->cpuid[i]);
198	}
199
200	psi = (struct mca_cpu_psi*)(cpuid + 1);
201	/* TODO: Dump PSI */
202
203	printf("    </cpu>\n");
204}
205
206static void
207show_memory(struct mca_mem_record *mem)
208{
209	printf("    <memory>\n");
210
211	if (mem->mem_flags & MCA_MEM_FLAGS_STATUS)
212		show_value(6, "status", "0x%016llx",
213		    (long long)mem->mem_status);
214	if (mem->mem_flags & MCA_MEM_FLAGS_ADDR)
215		show_value(6, "address", "0x%016llx",
216		    (long long)mem->mem_addr);
217	if (mem->mem_flags & MCA_MEM_FLAGS_ADDRMASK)
218		show_value(6, "mask", "0x%016llx",
219		    (long long)mem->mem_addrmask);
220	if (mem->mem_flags & MCA_MEM_FLAGS_NODE)
221		show_value(6, "node", "0x%04x", mem->mem_node);
222	if (mem->mem_flags & MCA_MEM_FLAGS_CARD)
223		show_value(6, "card", "0x%04x", mem->mem_card);
224	if (mem->mem_flags & MCA_MEM_FLAGS_MODULE)
225		show_value(6, "module", "0x%04x", mem->mem_module);
226	if (mem->mem_flags & MCA_MEM_FLAGS_BANK)
227		show_value(6, "bank", "0x%04x", mem->mem_bank);
228	if (mem->mem_flags & MCA_MEM_FLAGS_DEVICE)
229		show_value(6, "device", "0x%04x", mem->mem_device);
230	if (mem->mem_flags & MCA_MEM_FLAGS_ROW)
231		show_value(6, "row", "0x%04x", mem->mem_row);
232	if (mem->mem_flags & MCA_MEM_FLAGS_COLUMN)
233		show_value(6, "column", "0x%04x", mem->mem_column);
234	if (mem->mem_flags & MCA_MEM_FLAGS_BITPOS)
235		show_value(6, "bit", "0x%04x", mem->mem_bitpos);
236	if (mem->mem_flags & MCA_MEM_FLAGS_REQID)
237		show_value(6, "requester", "0x%016llx",
238		    (long long)mem->mem_reqid);
239	if (mem->mem_flags & MCA_MEM_FLAGS_RSPID)
240		show_value(6, "responder", "0x%016llx",
241		    (long long)mem->mem_rspid);
242	if (mem->mem_flags & MCA_MEM_FLAGS_TGTID)
243		show_value(6, "target", "0x%016llx",
244		    (long long)mem->mem_tgtid);
245	if (mem->mem_flags & MCA_MEM_FLAGS_BUSDATA)
246		show_value(6, "status", "0x%016llx",
247		    (long long)mem->mem_busdata);
248	if (mem->mem_flags & MCA_MEM_FLAGS_OEM_ID)
249		show_value(6, "oem", "%s", uuid(&mem->mem_oem_id));
250	/* TODO: Dump OEM data */
251
252	printf("    </memory>\n");
253}
254
255static void
256show_sel(void)
257{
258	printf("    # SEL\n");
259}
260
261static void
262show_pci_bus(struct mca_pcibus_record *pcibus)
263{
264	printf("    <pci-bus>\n");
265
266	if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_STATUS)
267		show_value(6, "status", "0x%016llx",
268		    (long long)pcibus->pcibus_status);
269	if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_ERROR)
270		show_value(6, "error", "0x%04x", pcibus->pcibus_error);
271	if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_BUS)
272		show_value(6, "bus", "0x%04x", pcibus->pcibus_bus);
273	if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_ADDR)
274		show_value(6, "address", "0x%016llx",
275		    (long long)pcibus->pcibus_addr);
276	if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_DATA)
277		show_value(6, "data", "0x%016llx",
278		    (long long)pcibus->pcibus_data);
279	if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_CMD)
280		show_value(6, "cmd", "0x%016llx",
281		    (long long)pcibus->pcibus_cmd);
282	if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_REQID)
283		show_value(6, "requester", "0x%016llx",
284		    (long long)pcibus->pcibus_reqid);
285	if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_RSPID)
286		show_value(6, "responder", "0x%016llx",
287		    (long long)pcibus->pcibus_rspid);
288	if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_TGTID)
289		show_value(6, "target", "0x%016llx",
290		    (long long)pcibus->pcibus_tgtid);
291	if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_OEM_ID)
292		show_value(6, "oem", "%s", uuid(&pcibus->pcibus_oem_id));
293	/* TODO: Dump OEM data */
294
295	printf("    </pci-bus>\n");
296}
297
298static void
299show_smbios(void)
300{
301	printf("    # SMBIOS\n");
302}
303
304static void
305show_pci_dev(struct mca_pcidev_record *pcidev)
306{
307	printf("    <pci-dev>\n");
308
309	if (pcidev->pcidev_flags & MCA_PCIDEV_FLAGS_STATUS)
310		show_value(6, "status", "0x%016llx",
311		    (long long)pcidev->pcidev_status);
312	if (pcidev->pcidev_flags & MCA_PCIDEV_FLAGS_INFO) {
313		show_value(6, "vendor", "0x%04x",
314		    pcidev->pcidev_info.info_vendor);
315		show_value(6, "device", "0x%04x",
316		    pcidev->pcidev_info.info_device);
317		show_value(6, "class", "0x%06x",
318		    MCA_PCIDEV_INFO_CLASS(pcidev->pcidev_info.info_ccfn));
319		show_value(6, "function", "0x%02x",
320		    MCA_PCIDEV_INFO_FUNCTION(pcidev->pcidev_info.info_ccfn));
321		show_value(6, "slot", "0x%02x", pcidev->pcidev_info.info_slot);
322		show_value(6, "bus", "0x%04x", pcidev->pcidev_info.info_bus);
323		show_value(6, "segment", "0x%04x",
324		    pcidev->pcidev_info.info_segment);
325	}
326	/* TODO: dump registers */
327	/* TODO: Dump OEM data */
328
329	printf("    </pci-dev>\n");
330}
331
332static void
333show_generic(void)
334{
335	printf("    # GENERIC\n");
336}
337
338static size_t
339show_section(struct mca_section_header *sh)
340{
341	static struct uuid uuid_cpu = MCA_UUID_CPU;
342	static struct uuid uuid_memory = MCA_UUID_MEMORY;
343	static struct uuid uuid_sel = MCA_UUID_SEL;
344	static struct uuid uuid_pci_bus = MCA_UUID_PCI_BUS;
345	static struct uuid uuid_smbios = MCA_UUID_SMBIOS;
346	static struct uuid uuid_pci_dev = MCA_UUID_PCI_DEV;
347	static struct uuid uuid_generic = MCA_UUID_GENERIC;
348
349	printf("  <section>\n");
350	show_value(4, "uuid", "%s", uuid(&sh->sh_uuid));
351	show_value(4, "revision", "%d.%d", BCD(sh->sh_major),
352	    BCD(sh->sh_minor));
353
354	if (!memcmp(&sh->sh_uuid, &uuid_cpu, sizeof(uuid_cpu)))
355		show_cpu((void*)(sh + 1));
356	else if (!memcmp(&sh->sh_uuid, &uuid_memory, sizeof(uuid_memory)))
357		show_memory((void*)(sh + 1));
358	else if (!memcmp(&sh->sh_uuid, &uuid_sel, sizeof(uuid_sel)))
359		show_sel();
360	else if (!memcmp(&sh->sh_uuid, &uuid_pci_bus, sizeof(uuid_pci_bus)))
361		show_pci_bus((void*)(sh + 1));
362	else if (!memcmp(&sh->sh_uuid, &uuid_smbios, sizeof(uuid_smbios)))
363		show_smbios();
364	else if (!memcmp(&sh->sh_uuid, &uuid_pci_dev, sizeof(uuid_pci_dev)))
365		show_pci_dev((void*)(sh + 1));
366	else if (!memcmp(&sh->sh_uuid, &uuid_generic, sizeof(uuid_generic)))
367		show_generic();
368
369	printf("  </section>\n");
370	return (sh->sh_length);
371}
372
373static void
374show(char *data)
375{
376	size_t reclen, seclen;
377
378	printf("<record>\n");
379	reclen = show_header((void*)data) - sizeof(struct mca_record_header);
380	data += sizeof(struct mca_record_header);
381	while (reclen > sizeof(struct mca_section_header)) {
382		seclen = show_section((void*)data);
383		reclen -= seclen;
384		data += seclen;
385	}
386	printf("</record>\n");
387}
388
389static void
390showall(char *buf, size_t buflen)
391{
392	struct mca_record_header *rh;
393	size_t reclen;
394
395	do {
396		if (buflen < sizeof(struct mca_record_header))
397			return;
398
399		rh = (void*)buf;
400		reclen = rh->rh_length;
401		if (buflen < reclen)
402			return;
403
404		show(buf);
405
406		buf += reclen;
407		buflen -= reclen;
408	}
409	while (1);
410}
411
412static void
413dump(char *data)
414{
415	struct mca_record_header *rh;
416	const char *fn;
417	int fd;
418
419	rh = (void*)data;
420	fn = (file) ? file : default_dumpfile;
421	fd = open(fn, O_WRONLY|O_CREAT|O_APPEND, 0660);
422	if (fd == -1)
423		err(2, "open(%s)", fn);
424	if (write(fd, (void*)rh, rh->rh_length) == -1)
425		err(2, "write(%s)", fn);
426	close(fd);
427}
428
429static void
430usage(void)
431{
432
433	fprintf(stderr, "usage: mca [-df]\n");
434	exit (1);
435}
436
437int
438main(int argc, char **argv)
439{
440	char mib[32];
441	char *buf;
442	size_t len;
443	int ch, error, fd;
444	int count, first, last;
445
446	while ((ch = getopt(argc, argv, "df:")) != -1) {
447		switch(ch) {
448		case 'd':	/* dump */
449			fl_dump = 1;
450			break;
451		case 'f':
452			if (file)
453				free(file);		/* XXX complain! */
454			file = strdup(optarg);
455			break;
456		default:
457			usage();
458		}
459	}
460
461	argc -= optind;
462	argv += optind;
463
464	if (file == NULL || fl_dump) {
465		len = sizeof(count);
466		error = sysctlbyname(hw_mca_count, &count, &len, NULL, 0);
467		if (error)
468			err(1, hw_mca_count);
469
470		if (count == 0)
471			errx(0, "no error records found");
472
473		len = sizeof(first);
474		error = sysctlbyname(hw_mca_first, &first, &len, NULL, 0);
475		if (error)
476			err(1, hw_mca_first);
477
478		len = sizeof(last);
479		error = sysctlbyname(hw_mca_last, &last, &len, NULL, 0);
480		if (error)
481			err(1, hw_mca_last);
482
483		while (count && first <= last) {
484			sprintf(mib, hw_mca_recid, first);
485			len = 0;
486			error = sysctlbyname(mib, NULL, &len, NULL, 0);
487			if (error == ENOENT) {
488				first++;
489				continue;
490			}
491			if (error)
492				err(1, "%s(1)", mib);
493
494			buf = malloc(len);
495			if (buf == NULL)
496				err(1, "buffer");
497
498			error = sysctlbyname(mib, buf, &len, NULL, 0);
499			if (error)
500				err(1, "%s(2)", mib);
501
502			if (fl_dump)
503				dump(buf);
504			else
505				show(buf);
506
507			free(buf);
508			first++;
509			count--;
510		}
511	} else {
512		fd = open(file, O_RDONLY);
513		if (fd == -1)
514			err(1, "open(%s)", file);
515
516		len = lseek(fd, 0LL, SEEK_END);
517		buf = mmap(NULL, len, PROT_READ, 0U, fd, 0LL);
518		if (buf == MAP_FAILED)
519			err(1, "mmap(%s)", file);
520
521		showall(buf, len);
522
523		munmap(buf, len);
524		close(fd);
525	}
526
527	return (0);
528}
529