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