mca.c revision 267654
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: releng/9.3/sbin/mca/mca.c 237047 2012-06-14 06:26:58Z eadler $"); 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