Logo Search packages:      
Sourcecode: salinfo version File versions  Download package

mca.c

/*
 * File:    mca.c
 * Purpose: Generic MCA handling layer
 *
 * Updated for latest kernel
 * Copyright (C) 2003 Hewlett-Packard Co
 *    David Mosberger-Tang <davidm@hpl.hp.com>
 *
 * Copyright (C) 2002 Dell Computer Corporation
 * Copyright (C) Matt Domsch (Matt_Domsch@dell.com)
 *
 * Copyright (C) 2002 Intel
 * Copyright (C) Jenna Hall (jenna.s.hall@intel.com)
 *
 * Copyright (C) 2001 Intel
 * Copyright (C) Fred Lewis (frederick.v.lewis@intel.com)
 *
 * Copyright (C) 2000 Intel
 * Copyright (C) Chuck Fleckenstein (cfleck@co.intel.com)
 *
 * Copyright (C) 1999 Silicon Graphics, Inc.
 * Copyright (C) Vijay Chander(vijay@engr.sgi.com)
 *
 * 03/04/15 D. Mosberger Added INIT backtrace support.
 * 02/03/25 M. Domsch   GUID cleanups
 *
 * 02/01/04 J. Hall     Aligned MCA stack to 16 bytes, added platform vs. CPU
 *                error flag, set SAL default return values, changed
 *                error record structure to linked list, added init call
 *                to sal_get_state_info_size().
 *
 * 01/01/03 F. Lewis    Added setup of CMCI and CPEI IRQs, logging of corrected
 *                      platform errors, completed code for logging of
 *                      corrected & uncorrected machine check errors, and
 *                      updated for conformance with Nov. 2000 revision of the
 *                      SAL 3.0 spec.
 * 00/03/29 C. Fleckenstein  Fixed PAL/SAL update issues, began MCA bug fixes, logging issues,
 *                           added min save state dump, added INIT handler.
 * 2003-11-05 Strip down to the bare minimum required to decode SAL records.
 *          Add lots more decoding.
 *          Keith Owens <kaos@sgi.com>
 * 2003-11-16 Break out oem data decoder so each platform can handle the
 *          oem data as it likes.
 *          Keith Owens <kaos@sgi.com>
 * 2005-12-14 Fix use after free bug in read_salinfo_decode_oem().
 *          Correct the printing of the severity code and validation bits.
 *          Keith Owens <kaos@sgi.com>
 */

#include <errno.h>
#include <stdarg.h>
#include <signal.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/poll.h>

#include "mca.h"
#include "sal.h"

#define ARRAY_SIZE(x) (sizeof((x))/sizeof((x)[0]))

int debug;

struct decode_bits {
      int start;
      int length;
      const char *name;
      const char *y;
      const char *n;
};

struct reg_fmt {
      char * const name;
      int offset;
};

static int indent;

static sal_log_record_header_t *decode_oem_record_start;
static int decode_oem_fd_data;
static int decode_oem_use_sal;
static int decode_oem_cpu;
static int *decode_oem_oemdata_fd;

/* Equivalent of printf(), with automatic indentation at the start of each line */
static int iprintf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
static int iprintf_count;

static int
iprintf(const char *fmt, ...)
{
      char buf[1000000];      /* the decoded oem data from the kernel is one big print */
      char c, *p, *p1;
      va_list args;
      int len;
      static int startofline = 1;
      va_start(args, fmt);
      len = vsnprintf(buf, sizeof(buf), fmt, args);
      va_end(args);
      p = buf;
      do {
            if (startofline) {
                  int i;
                  for (i = 0; i < indent; ++i)
                        fputs("  ", stdout);
                  startofline = 0;
                  if (++iprintf_count > 1000000 /* arbitrary */) {
                        printf("\n\nError: iprintf line count exceeded, aborting output\n\n");
                        fflush(stdout);
                        exit(1);
                  }
            }
            p1 = strchr(p, '\n');
            if (p1) {
                  ++p1;
                  c = *p1;
                  *p1 = '\0';
            } else
                  c = '\0';
            fputs(p, stdout);
            if (p1) {
                  startofline = 1;
                  p = p1;
            }
            *p = c;
      } while (*p);
      return len;
}

/* Standard format for string plus value */
static int
prval(const char *string, u64 val)
{
      return iprintf("%-25s: 0x%016lx\n", string, val);
}


/* Print a formatted GUID. */
static void
prt_guid (efi_guid_t *p_guid)
{
      char out[40];
      iprintf("GUID = %s\n", efi_guid_unparse(p_guid, out));
}

static void
hexdump (const unsigned char *p, unsigned long n_ch)
{
      int i, j;
      if (!p)
            return;
      for (i = 0; i < n_ch;) {
            iprintf("%p ", (void *)p);
            for (j = 0; (j < 16) && (i < n_ch); i++, j++, p++) {
                  iprintf("%02x ", *p);
            }
            iprintf("\n");
      }
}


static void
prt_record_header (sal_log_record_header_t *rh)
{
      iprintf("SAL RECORD HEADER:  Record buffer = %p,  header size = %ld\n",
             (void *)rh, sizeof(sal_log_record_header_t));
      ++indent;
      hexdump((unsigned char *)rh, sizeof(sal_log_record_header_t));
      iprintf("Total record length = %d\n", rh->len);
      prt_guid(&rh->platform_guid);
      --indent;
      iprintf("End of SAL RECORD HEADER\n");
}

static void
prt_section_header (sal_log_section_hdr_t *sh)
{
      iprintf("SAL SECTION HEADER:  Record buffer = %p,  header size = %ld\n",
             (void *)sh, sizeof(sal_log_section_hdr_t));
      ++indent;
      hexdump((unsigned char *)sh, sizeof(sal_log_section_hdr_t));
      iprintf("Length of section & header = %d\n", sh->len);
      prt_guid(&sh->guid);
      --indent;
      iprintf("End of SAL SECTION HEADER\n");
}

static const char protocol1[] = "==== salinfo_decode_oem start ====\n";
static const char protocol2[] = "==== salinfo_decode_oem record ====\n";
static const char protocol3[] = "==== salinfo_decode_oem end ====\n";

static int
write_salinfo_decode_oem(const char *buffer, int len)
{
      int ret = 0, offset = 0;
      sig_t oldsig;
      if (decode_oem_oemdata_fd[1] < 0)
            return 1;
      oldsig = signal(SIGPIPE, SIG_IGN);
      while (len) {
            ret = write(decode_oem_oemdata_fd[1], buffer+offset, len);
            if (ret < 0) {
                  decode_oem_oemdata_fd[1] = -1;
                  if (errno != EPIPE)
                        iprintf("%s failed: %m\n", __FUNCTION__);
                  ret = 1;
                  break;
            }
            offset += ret;
            len -= ret;
            ret = 0;
      }
      signal(SIGPIPE, oldsig);
      return ret;
}

/* Read the response from salinfo_decode_oem.  To avoid deadlocks when one or
 * the other program is not obeying the protocol, only wait for 30 seconds for
 * the response.
 */
static int
read_salinfo_decode_oem(void)
{
      int nbytes = 0, size = 0, alloc = 16*1024, ret;
      char *buffer = NULL, *p1 = NULL, *p3 = NULL;
      sig_t oldsigpipe = signal(SIGPIPE, SIG_IGN);
      struct pollfd pfd = { .fd = decode_oem_oemdata_fd[0], .events = POLLIN };
      do {
            ret = 1;
            buffer = realloc(buffer, alloc);
            if (!buffer) {
                  fprintf(stderr, "%s: Can't alloc %d bytes\n", __FUNCTION__, alloc);
                  break;
            }

            ret = poll(&pfd, 1, 30*1000);
            if (ret == 0) {
                  iprintf("oem decode timed out\n");
                  break;
            } else if (ret < 0) {
                  if (errno == EINTR)
                        continue;
                  decode_oem_oemdata_fd[1] = -1;
                  if (errno != EPIPE)
                        iprintf("%s failed: %m\n", __FUNCTION__);
                  break;
            }
            nbytes = read(decode_oem_oemdata_fd[0], buffer + size, alloc - size);
            if (nbytes < 0) {
                  decode_oem_oemdata_fd[1] = -1;
                  if (errno != EPIPE)
                        iprintf("%s failed: %m\n", __FUNCTION__);
                  break;
            }
            ret = 0;
            if ((p1 = strstr(buffer, protocol1)))
                  p1 += sizeof(protocol1) - 1;
            if ((p3 = strstr(buffer, protocol3)))
                  break;
            if (nbytes == alloc - size)
                  alloc *= 2;
            size += nbytes;
      } while (nbytes);

      signal(SIGPIPE, oldsigpipe);
      if (p1 && p3 && p3 > p1) {
            nbytes = p3 - p1;
            *p3 = '\0';
      }
      else
            nbytes = 0;
      if (nbytes == 0)
            ret = 1;
      if (ret == 0)
            iprintf("%s", p1);
      free(buffer);
      return ret;
}

#define key_value(k, v)                               \
      (snprintf(buf, sizeof(buf), "%s=%ld\n", k, (v)+0L),   \
       write_salinfo_decode_oem(buf, strlen(buf)))

/* Attempt to decode oem data via a separate program which may or may not be
 * connected on the pipes listed in decode_oem_oemdata_fd.  If anything goes
 * wrong with the oem decoding then return 1 so the caller will decode the
 * oem data in hex.
 */
static int
platform_decode_oemdata (const sal_log_section_hdr_t *header)
{
      char buf[200];
      if (write_salinfo_decode_oem(protocol1, sizeof(protocol1)-1) ||
          key_value("fd_data", decode_oem_fd_data) ||
          key_value("use_sal", decode_oem_use_sal) ||
          key_value("cpu", decode_oem_cpu) ||
          key_value("raw_length", decode_oem_record_start->len) ||
          key_value("oem_section_offset", (char *)header - (char *)decode_oem_record_start) ||
          write_salinfo_decode_oem(protocol2, sizeof(protocol2)-1) ||
          write_salinfo_decode_oem((char *)decode_oem_record_start, decode_oem_record_start->len) ||
          write_salinfo_decode_oem("\n", 1) ||
          write_salinfo_decode_oem(protocol3, sizeof(protocol3)-1))
            return 1;
      if (read_salinfo_decode_oem())
            return 1;
      return 0;
}

/* Print OEM specific data.  decode_oem_oemdata_fd is a pair of file
 * descriptors which may or may not be valid for talking to the platform
 * specific salinfo_decode_oem program.  Try that first, if no luck then
 * just print in hex.
 */
static void
prt_oem_data (const sal_log_section_hdr_t *header, const u8 *p_data)
{
      int oem_data_len, i, j;

      if ((oem_data_len = header->len - ((char *)p_data - (char *)header)) <= 0)
            return;

      iprintf("OEM Specific Data\n");
      ++indent;

      if (decode_oem_oemdata_fd[1] >= 0 &&
          platform_decode_oemdata(header) == 0) {
            /* oem data has been decoded */
      } else {
            for (i = 0; i < oem_data_len; i += 16) {
                  iprintf("%s0x%04x ", i ? "\n" : "", i);
                  for (j = 0; i < oem_data_len && j < 16; j++, p_data++)
                        iprintf(" %02x", *p_data);
            }
            iprintf("\n");
      }
      --indent;
}

/* Log info from the SAL error record header */
static void
rec_header_print (sal_log_record_header_t *lh)
{
      iprintf("Err Record ID: %ld    SAL Rev: %2x.%02x\n", lh->id,
                  lh->revision.major, lh->revision.minor);
      iprintf("Time: %02x%02x-%02x-%02x %02x:%02x:%02x    Severity %d Validation bits 0x%02x\n",
                  lh->timestamp.slh_century, lh->timestamp.slh_year,
                  lh->timestamp.slh_month, lh->timestamp.slh_day,
                  lh->timestamp.slh_hour, lh->timestamp.slh_minute,
                  lh->timestamp.slh_second, lh->severity, lh->validation_bits);
}

/* Display the common fields from a mod_error_info entry */
static void
mod_error_info_common_print (sal_log_mod_error_info_t *info, void *common_check_info)
{
      /* All mod_error_info check fields have a common set of bits from is
       * (bit 54) through pi (bit 63).  Treat them all as if they were
       * cache_check.
       */
      pal_cache_check_info_t *check_info = common_check_info;
      if (check_info) {
            if (check_info->iv && check_info->is)
                  iprintf("instruction set ia32\n");
            if (check_info->pv)
                  iprintf("privilege level %d\n", check_info->pl);
            if (check_info->mcc)
                  iprintf("machine check corrected\n");
      }
      if (info->valid.target_identifier)
            prval("target identifier", info->target_identifier);
      if (info->valid.requestor_identifier)
            prval("requestor identifier", info->requestor_identifier);
      if (info->valid.responder_identifier)
            prval("responder identifier", info->responder_identifier);
      if (info->valid.precise_ip)
            prval("precise ip", info->precise_ip);
}

/* Display the machine check information related to cache error(s) */
static void
cache_check_info_print (int i, sal_log_mod_error_info_t *cache_check_info)
{
      pal_cache_check_info_t *info = (pal_cache_check_info_t *)&cache_check_info->check_info;
      static const char * const op[] = {
            "Unknown/unclassified",             /*  0 */
            "Load",
            "Store",
            "Instruction fetch or prefetch",
            "Data fetch",
            "Snoop",                      /*  5 */
            "Cast out",
            "Move in",
      };
      static const char * const mesi[] = {
            "Invalid",
            "Shared",
            "Exclusive",
            "Modified",
      };

      if (!cache_check_info->valid.check_info) {
            iprintf("%s: invalid cache_check_info[%d]\n", __FUNCTION__, i);
            mod_error_info_common_print(cache_check_info, NULL);
            return;
      }

      iprintf("Cache check info[%d]\n", i);
      ++indent;
      iprintf("Operation: %d (%s)",
            info->op, info->op < ARRAY_SIZE(op) ? op[info->op] : "reserved");
      iprintf(", Level: L%d", info->level);
       /* if both dl and tl are set, is a MESI error */
      if (info->dl && info->tl)
            iprintf(", MESI Error:");
      else if (info->dl)
            iprintf(", Line: Data");
      else if (info->tl)
            iprintf(", Line: Tag");
      if (info->dc)
            iprintf(", Cache: Data");
      if (info->ic)
            iprintf(", Cache: Instruction");
      if (info->mv)
            iprintf(", Mesi: %d (%s)",
                  info->mesi, info->mesi < ARRAY_SIZE(mesi) ? mesi[info->mesi] : "reserved");
      if (info->wiv)
            iprintf(", Way: %d, Index: %d", info->way, info->index);
      iprintf("\n");
      mod_error_info_common_print(cache_check_info, info);
      --indent;
}

/* Display the machine check information related to tlb error(s) */
static void
tlb_check_info_print (int i, sal_log_mod_error_info_t *tlb_check_info)

{
      pal_tlb_check_info_t *info = (pal_tlb_check_info_t *)&tlb_check_info->check_info;
      static const char * const op[] = {
            "Unknown/unclassified",             /*  0 */
            "Load",
            "Store",
            "Instruction fetch or prefetch",
            "Data fetch",
            "TLB shoot down",             /*  5 */
            "TLB probe instruction",
            "Move in (VHPT fill)",
            "Purge",
      };

      if (!tlb_check_info->valid.check_info) {
            iprintf("%s: invalid tlb_check_info[%d]\n", __FUNCTION__, i);
            mod_error_info_common_print(tlb_check_info, NULL);
            return;                 /* If check info data not valid, skip it */
      }

      iprintf("TLB Check Info [%d]\n", i);
      ++indent;
      if (info->trv)
            iprintf("Slot: %d", info->tr_slot);
      iprintf(", Level: L%d", info->level);
      if (info->dtr)
            iprintf(", Data Translation Register");
      if (info->itr)
            iprintf(", Instruction Translation Register");
      if (info->dtc)
            iprintf(", Data Translation Cache");
      if (info->itc)
            iprintf(", Instruction Translation Cache");
      iprintf(", Operation: %d (%s)",
            info->op, info->op < ARRAY_SIZE(op) ? op[info->op] : "reserved");
      iprintf("\n");
      mod_error_info_common_print(tlb_check_info, info);
      --indent;
}

/* Display the machine check information related to bus error(s) */
static void
bus_check_info_print (int i, sal_log_mod_error_info_t *bus_check_info)
{
      pal_bus_check_info_t *info = (pal_bus_check_info_t *)&bus_check_info->check_info;
      static const char * const type[] = {
            "Unknown/unclassified",             /*  0 */
            "Partial read",
            "Partial write",
            "Full line read",
            "Full line write",
            "Implicit or explicit write-back",  /*  5 */
            "Snoop probe",
            "ptc.g",
            "Write coalescing",
            "I/O space read",
            "I/O space write",                  /* 10 */
            "IPI",
            "Interrupt acknowledge",
      };
      /* FIXME: Are these SGI specific or generic bsi values? */
      static const char * const bsi[] = {
            "Unknown/unclassified",             /*  0 */
            "Berr",
            "BINIT",
            "Hard fail",
      };

      if (!bus_check_info->valid.check_info) {
            iprintf("%s: invalid bus_check_info[%d]\n", __FUNCTION__, i);
            mod_error_info_common_print(bus_check_info, NULL);
            return;                 /* If check info data not valid, skip it */
      }

      iprintf("BUS Check Info [%d]\n", i);
      ++indent;
      iprintf("Transaction size: %d", info->size);
      if (info->ib)
            iprintf(", Internal Bus Error:");
      if (info->eb)
            iprintf(", External Bus Error:");
      if (info->cc)
            iprintf(", Cache to cache transfer");
      iprintf(", Type: %d (%s)",
            info->type, info->type < ARRAY_SIZE(type) ? type[info->type] : "reserved");
      iprintf(", Severity: %d, Hierarchy: %d", info->sev, info->hier);
      iprintf(", Status information: %d (%s)",
            info->bsi, info->bsi < ARRAY_SIZE(bsi) ? bsi[info->bsi] : "processor specific");
      iprintf("\n");
      mod_error_info_common_print(bus_check_info, info);
      --indent;
}

/* Display the machine check information related to reg_file error(s) */
static void
reg_file_check_info_print (int i, sal_log_mod_error_info_t *reg_file_check_info)
{
      pal_reg_file_check_info_t *info = (pal_reg_file_check_info_t *)&reg_file_check_info->check_info;
      static const char * const id[] = {
            "Unknown/unclassified",             /*  0 */
            "General register (bank 1)",
            "General register (bank 0)",
            "Floating point register",
            "Branch register",
            "Predicate register",               /*  5 */
            "Application register",
            "Control register",
            "Region register",
            "Protection key register",
            "Data breakpoint register",         /* 10 */
            "Instruction breakpoint register",
            "Performance monitor control register",
            "Performance monitor data register",
      };
      static const char * const op[] = {
            "Unknown",
            "Read",
            "Write",
      };

      if (!reg_file_check_info->valid.check_info) {
            iprintf("%s: invalid reg_file_check_info[%d]\n", __FUNCTION__, i);
            mod_error_info_common_print(reg_file_check_info, NULL);
            return;                 /* If check info data not valid, skip it */
      }

      iprintf("reg_file Check Info [%d]\n", i);
      ++indent;
      iprintf("ID %d (%s) Operation: %d (%s), Reg_num: %d\n",
            info->id, info->id < ARRAY_SIZE(id) ? id[info->id] : "reserved",
            info->op, info->op < ARRAY_SIZE(op) ? op[info->op] : "processor specific",
            info->reg_num);
      iprintf("\n");
      mod_error_info_common_print(reg_file_check_info, info);
      --indent;
}

/* Display the machine check information related to ms error(s).
 * For some reason, SAL calls this 'ms' (micro structure?), PAL calls it 'uarch'
 * (micro-architectural).
 */
static void
ms_check_info_print (int i, sal_log_mod_error_info_t *ms_check_info)
{
      pal_uarch_check_info_t *info = (pal_uarch_check_info_t *)&ms_check_info->check_info;
      static const char * const array_id[] = {
            "Unknown/unclassified",
      };
      static const char * const op[] = {
            "Unknown",
            "Read/load",
            "Write/store",
      };
      /* SGI specific mappings of sid/op.
       * FIXME: need some generic way to handle implementation specific
       * definitions.
       * sid*_op values start at index ARRAY_SIZE(op).
       */
      static const char * const sid0_op[] = {
            "Illegal ISA transfer",
      };
      static const char * const sid1_op[] = {
            "Internal proc timer expired, BINIT signaled",
            "Internal proc timer reached half-way point, MCA signaled",
      };
      static const char * const sid8_op[] = {
            "Over-temp detected, cpu power reduced",
            "Proc temp returned to normal, normal pwr restored",
      };
      const char * const *sid_op;
      int size;

      if (!ms_check_info->valid.check_info) {
            iprintf("%s: invalid uarch_check_info[%d]\n", __FUNCTION__, i);
            mod_error_info_common_print(ms_check_info, NULL);
            return;                 /* If check info data not valid, skip it */
      }

      iprintf("Uarch Check Info [%d]\n", i);
      ++indent;
      iprintf("Sid %d, Level L%d", info->sid, info->level);
      switch(info->sid) {
      case 0:
            sid_op = sid0_op;
            size = ARRAY_SIZE(sid0_op);
            break;
      case 1:
            sid_op = sid1_op;
            size = ARRAY_SIZE(sid1_op);
            break;
      case 8:
            sid_op = sid8_op;
            size = ARRAY_SIZE(sid8_op);
            break;
      default:
            sid_op = NULL;
            size = 0;
      }
      iprintf(", Array ID: %d (%s)",
            info->array_id, info->array_id < ARRAY_SIZE(array_id) ? array_id[info->array_id] : "implementation specific");
      iprintf(", Operation: %d (%s)",
            info->op,
            info->op < ARRAY_SIZE(op) ? op[info->op] :
                  info->op - ARRAY_SIZE(op) < size ?
                        sid_op[info->op - ARRAY_SIZE(op)] :
                        "implementation specific"
            );
      if (info->wv)
            iprintf(", Way: %d", info->way);
      if (info->xv)
            iprintf(", Index: %d", info->index);
      iprintf("\n");
      mod_error_info_common_print(ms_check_info, info);
      --indent;
}

/* Format and log the platform memory device error record section data */
static void
mem_dev_err_info_print (sal_log_mem_dev_err_info_t *mdei)
{
      iprintf("Mem Error Detail\n");

      ++indent;
      if (mdei->valid.error_status)
            iprintf("Error Status: %#lx ", mdei->error_status);
      if (mdei->valid.physical_addr)
            iprintf("Physical Address: %#lx ", mdei->physical_addr);
      if (mdei->valid.addr_mask)
            iprintf("Address Mask: %#lx ", mdei->addr_mask);
      if (mdei->valid.node)
            iprintf("Node: %d ", mdei->node);
      if (mdei->valid.card)
            iprintf("Card: %d ", mdei->card);
      if (mdei->valid.module)
            iprintf("Module: %d ", mdei->module);
      if (mdei->valid.bank)
            iprintf("Bank: %d ", mdei->bank);
      if (mdei->valid.device)
            iprintf("Device: %d ", mdei->device);
      if (mdei->valid.row)
            iprintf("Row: %d ", mdei->row);
      if (mdei->valid.column)
            iprintf("Column: %d ", mdei->column);
      if (mdei->valid.bit_position)
            iprintf("Bit Position: %d ", mdei->bit_position);
      if (mdei->valid.target_id)
            iprintf("Target Address: %#lx ", mdei->target_id);
      if (mdei->valid.requestor_id)
            iprintf("Requestor Address: %#lx ", mdei->requestor_id);
      if (mdei->valid.responder_id)
            iprintf("Responder Address: %#lx ", mdei->responder_id);
      if (mdei->valid.bus_spec_data)
            iprintf("Bus Specific Data: %#lx ", mdei->bus_spec_data);
      iprintf("\n");

      if (mdei->valid.oem_id) {
            u8  *p_data = &(mdei->oem_id[0]);
            int i;

            iprintf("OEM Memory Controller ID: ");
            for (i = 0; i < 16; i++, p_data++)
                  iprintf("%02x ", *p_data);
            iprintf("\n");
      }

      if (mdei->valid.oem_data)
            platform_mem_dev_err_print(&mdei->header, &mdei->oem_data[0]);
      --indent;
}

/* Format and log the platform SEL device error record section data */
static void
sel_dev_err_info_print (sal_log_sel_dev_err_info_t *sdei)
{
      int     i;

      iprintf("SEL Device Error Detail\n");

      ++indent;
      if (sdei->valid.record_id)
            iprintf("Record ID: %#x ", sdei->record_id);
      if (sdei->valid.record_type)
            iprintf("Record Type: %#x ", sdei->record_type);
      iprintf("Time Stamp: ");
      for (i = 0; i < 4; i++)
            iprintf("%1d ", sdei->timestamp[i]);
      if (sdei->valid.generator_id)
            iprintf("Generator ID: %#x ", sdei->generator_id);
      if (sdei->valid.evm_rev)
            iprintf("Message Format Version: %#x ", sdei->evm_rev);
      if (sdei->valid.sensor_type)
            iprintf("Sensor Type: %#x ", sdei->sensor_type);
      if (sdei->valid.sensor_num)
            iprintf("Sensor Number: %#x ", sdei->sensor_num);
      if (sdei->valid.event_dir)
            iprintf("Event Direction Type: %#x ", sdei->event_dir);
      if (sdei->valid.event_data1)
            iprintf("Data1: %#x ", sdei->event_data1);
      if (sdei->valid.event_data2)
            iprintf("Data2: %#x ", sdei->event_data2);
      if (sdei->valid.event_data3)
            iprintf("Data3: %#x ", sdei->event_data3);
      iprintf("\n");
      --indent;

}

/* Format and log the platform PCI bus error record section data */
static void
pci_bus_err_info_print (sal_log_pci_bus_err_info_t *pbei)
{
      iprintf("PCI Bus Error Detail\n");

      ++indent;
      if (pbei->valid.err_status)
            iprintf("Error Status: %#lx ", pbei->err_status);
      if (pbei->valid.err_type)
            iprintf("Error Type: %#x ", pbei->err_type);
      if (pbei->valid.bus_id)
            iprintf("Bus ID: %#x ", pbei->bus_id);
      if (pbei->valid.bus_address)
            iprintf("Bus Address: %#lx ", pbei->bus_address);
      if (pbei->valid.bus_data)
            iprintf("Bus Data: %#lx ", pbei->bus_data);
      if (pbei->valid.bus_cmd)
            iprintf("Bus Command: %#lx ", pbei->bus_cmd);
      if (pbei->valid.requestor_id)
            iprintf("Requestor ID: %#lx ", pbei->requestor_id);
      if (pbei->valid.responder_id)
            iprintf("Responder ID: %#lx ", pbei->responder_id);
      if (pbei->valid.target_id)
            iprintf("Target ID: %#lx ", pbei->target_id);
      iprintf("\n");

      if (pbei->valid.oem_data)
            platform_pci_bus_err_print(&pbei->header, &pbei->oem_data[0]);
      --indent;
}

/* Format and log the platform SMBIOS device error record section data */
static void
smbios_dev_err_info_print (sal_log_smbios_dev_err_info_t *sdei)
{
      u8      i;

      iprintf("SMBIOS Device Error Detail\n");

      ++indent;
      if (sdei->valid.event_type)
            iprintf("Event Type: %#x ", sdei->event_type);
      if (sdei->valid.time_stamp) {
            iprintf("Time Stamp: ");
            for (i = 0; i < 6; i++)
                  iprintf("%d ", sdei->time_stamp[i]);
      }
      if ((sdei->valid.data) && (sdei->valid.length)) {
            iprintf("Data: ");
            for (i = 0; i < sdei->length; i++)
                  iprintf("%02x ", sdei->data[i]);
      }
      iprintf("\n");
      --indent;
}

/* Format and log the platform PCI component error record section data */
static void
pci_comp_err_info_print(sal_log_pci_comp_err_info_t *pcei)
{
      u32     n_mem_regs, n_io_regs;
      u64     i, n_pci_data;
      u64     *p_reg_data;
      u8      *p_oem_data;

      iprintf("PCI Component Error Detail\n");

      ++indent;
      if (pcei->valid.err_status)
            iprintf("Error Status: %#lx\n", pcei->err_status);
      if (pcei->valid.comp_info)
            iprintf("Component Info: Vendor Id = %#x, Device Id = %#x,"
                   " Class Code = 0x%02x%02x%02x, Seg/Bus/Dev/Func = %d/%d/%d/%d\n",
                   pcei->comp_info.vendor_id, pcei->comp_info.device_id,
                   pcei->comp_info.class_code[0], pcei->comp_info.class_code[1],
                   pcei->comp_info.class_code[2], pcei->comp_info.seg_num,
                   pcei->comp_info.bus_num, pcei->comp_info.dev_num,
                   pcei->comp_info.func_num);

      n_mem_regs = (pcei->valid.num_mem_regs) ? pcei->num_mem_regs : 0;
      n_io_regs =  (pcei->valid.num_io_regs)  ? pcei->num_io_regs  : 0;
      p_reg_data = &(pcei->reg_data_pairs[0]);
      p_oem_data = (u8 *)p_reg_data +
            (n_mem_regs + n_io_regs) * 2 * sizeof(u64);
      n_pci_data = p_oem_data - (u8 *)pcei;

      if (n_pci_data > pcei->header.len) {
            iprintf("Invalid PCI Component Error Record format: length = %d, "
                   " Size PCI Data = %ld, Num Mem-Map/IO-Map Regs = %d/%d\n",
                   pcei->header.len, n_pci_data, n_mem_regs, n_io_regs);
            goto out;
      }

      if (n_mem_regs) {
            iprintf("Memory Mapped Registers\n  Address \tValue\n");
            ++indent;
            for (i = 0; i < pcei->num_mem_regs; i++) {
                  iprintf("%#lx %#lx\n", p_reg_data[0], p_reg_data[1]);
                  p_reg_data += 2;
            }
            --indent;
      }
      if (n_io_regs) {
            iprintf("I/O Mapped Registers\n  Address \tValue\n");
            ++indent;
            for (i = 0; i < pcei->num_io_regs; i++) {
                  iprintf("%#lx %#lx\n", p_reg_data[0], p_reg_data[1]);
                  p_reg_data += 2;
            }
            --indent;
      }
      if (pcei->valid.oem_data)
            platform_pci_comp_err_print(&pcei->header, p_oem_data);
out:
      --indent;
}

/* Format and log the platform specific error record section data */
static void
plat_specific_err_info_print (sal_log_plat_specific_err_info_t *psei)
{
      iprintf("Platform Specific Error Detail\n");

      ++indent;
      if (psei->valid.err_status)
            iprintf("Error Status: %#lx\n", psei->err_status);
      if (psei->valid.guid)
            prt_guid(&psei->guid);
      if (psei->valid.oem_data)
            platform_plat_specific_err_print(&psei->header, &psei->oem_data[0]);
      --indent;
}

/* Format and log the platform host controller error record section data */
static void
host_ctlr_err_info_print (sal_log_host_ctlr_err_info_t *hcei)
{
      iprintf("Host Controller Error Detail\n");

      ++indent;
      if (hcei->valid.err_status)
            iprintf("Error Status: %#lx ", hcei->err_status);
      if (hcei->valid.requestor_id)
            iprintf("Requestor ID: %#lx ", hcei->requestor_id);
      if (hcei->valid.responder_id)
            iprintf("Responder ID: %#lx ", hcei->responder_id);
      if (hcei->valid.target_id)
            iprintf("Target ID: %#lx ", hcei->target_id);
      if (hcei->valid.bus_spec_data)
            iprintf("Bus Specific Data: %#lx ", hcei->bus_spec_data);
      iprintf("\n");
      if (hcei->valid.oem_data)
            platform_host_ctlr_err_print(&hcei->header, &hcei->oem_data[0]);
      --indent;
}

/* Format and log the platform bus error record section data */
static void
plat_bus_err_info_print (sal_log_plat_bus_err_info_t *pbei)
{
      iprintf("Platform Bus Error Detail\n");

      ++indent;
      if (pbei->valid.err_status)
            iprintf("Error Status: %#lx ", pbei->err_status);
      if (pbei->valid.requestor_id)
            iprintf("Requestor ID: %#lx ", pbei->requestor_id);
      if (pbei->valid.responder_id)
            iprintf("Responder ID: %#lx ", pbei->responder_id);
      if (pbei->valid.target_id)
            iprintf("Target ID: %#lx ", pbei->target_id);
      if (pbei->valid.bus_spec_data)
            iprintf("Bus Specific Data: %#lx ", pbei->bus_spec_data);
      iprintf("\n");
      if (pbei->valid.oem_data)
            platform_plat_bus_err_print(&pbei->header, &pbei->oem_data[0]);
      --indent;
}

/* Summarize and decode the processor device error record */
static void
proc_summary (sal_log_processor_info_t *slpi)
{
      const char *label, *summary = NULL;
      pal_processor_state_info_t *sp = (pal_processor_state_info_t *)(&slpi->proc_state_parameter);
      sal_log_mod_error_info_t *bus_info = slpi->info + slpi->valid.num_cache_check + slpi->valid.num_tlb_check;
      pal_bus_check_info_t *bus_check_info = (pal_bus_check_info_t *)
            (slpi->valid.num_bus_check && bus_info->valid.check_info ? &bus_info->check_info : NULL);
      if (sp->cc)
            summary = "Cache Check";
      else if (sp->tc)
            summary = "TLB Check";
      else if (sp->bc)
            summary = "Bus Check";
      else if (sp->rc)
            summary = "Register Check";
      else if (sp->uc)
            summary = "Microarch Check";

      if (sp->in && !summary) {
            label = "PROCESSOR RECEIVED NMI";
            summary = "INIT received";
      } else {
            if (sp->cm) {
                  label = "CORRECTED PROCESSOR ERROR";
            } else if ((sp->bc && sp->co && sp->ci && !(sp->cc || sp->tc || sp->rc || sp->uc)) &&
                     ((bus_check_info && bus_check_info->eb && bus_check_info->bsi == 1))) {
                  label = "EXTERNAL BUS ERROR";
            } else {
                  label = "UNCORRECTED PROCESSOR ERROR";
            }
            if (!summary)
                  summary = "Unknown";
      }

      iprintf("%s: %s\n", label, summary);
}

/* Decode the processor LID field */

#define lid_to_slice(lid) (((lid) >> 28) & 0xf)
#define lid_to_nasid(lid) (((lid) >> 16) & 0xfff)

static void
proc_decode_lid (sal_log_processor_info_t *slpi)
{
      prval("processor lid", slpi->proc_cr_lid);
      ++indent;
      /* FIXME: SN specific.  Need a generic way to decode lid */
      iprintf("cpu: %c nasid: 0x%lx\n",
            (char)(lid_to_slice(slpi->proc_cr_lid) + 'A'),
            lid_to_nasid(slpi->proc_cr_lid));
      --indent;
}

/* Decode one level sensitive field from the error map */
static void
proc_decode_error_map1(u64 val, const char *id)
{
      int i;
      for (i = 0; i < 4; ++i) {
            if (val & (1UL << i))
                  iprintf("%s level %d error\n", id, i+1);
      }
}

/* Decode the processor error map */
static void
proc_decode_error_map (sal_log_processor_info_t *slpi)
{
      struct error_map {
            u64 cid                 : 4,
                tid                 : 4,
                eic                 : 4,
                edc                 : 4,
                eit                 : 4,
                edt                 : 4,
                ebh                 : 4,
                erf                 : 4,
                ems                 : 16,
                rsvd          : 16;
      } erm;
      memcpy(&erm, &slpi->proc_error_map, sizeof(erm));

      prval("processor error map", slpi->proc_error_map);
      ++indent;
      iprintf("processor code id: %d\n", erm.cid);
      iprintf("logical thread id: %d\n", erm.tid);
      proc_decode_error_map1(erm.eic, "instruction cache");
      proc_decode_error_map1(erm.edc, "data cache");
      proc_decode_error_map1(erm.eit, "instruction tlb");
      proc_decode_error_map1(erm.edt, "data tlb");
      proc_decode_error_map1(erm.ebh, "processor bus");
      if (erm.erf)
            prval("register file structures", erm.erf);
      if (erm.ems)
            prval("micro-architectural structures", erm.ems);
      --indent;
}

/* Decode a register according to an array of decode_bits.
 * "rv" fields must be zero.
 * Fields of more than one bit are printed in decimal, using just b->y.
 * Boolean fields print b->y or b->n (if both are defined).  If b->n is
 * not defined then print b->y followed by "enabled" or "disabled".
 * If suppress0 is set then 0 valued boolean fields are suppressed and true
 * boolean fields do not print '1'.
 */
static void
decode_reg(u64 reg, const struct decode_bits *b, int size, int suppress0)
{
      int i, out;
      for (i = 0; i < size; ++i, ++b) {
            int val = (reg >> b->start) & (~0UL >> (64 - b->length));
            if (strcmp(b->name, "rv") == 0) {
                  if (val) {
                        iprintf("rv  [%d", b->start);
                        if (b->length > 1)
                              iprintf(":%d", b->start + b->length - 1);
                        iprintf("] Error, reserved field contains non-zero value %#x\n", val);
                  }
                  continue;
            }
            if (suppress0 && !val && b->length == 1 && !b->n)
                  continue;
            out = iprintf("%-3s ", b->name);
            if (b->length > 1) {
                  out += iprintf("[%d:%d]=%d", b->start + b->length - 1, b->start, val);
                  iprintf("%*s%s\n", (14-out < 1) ? 1 : 14-out, " ", b->y);
                  continue;
            }
            out += iprintf("[%d]", b->start);
            if (!suppress0 || b->n || val)
                  out += iprintf("=%d", val);
            iprintf("%*s", (14-out < 1) ? 1 : 14-out, " ");
            if (b->y && b->n)
                  iprintf("%s\n", val ? b->y : b->n);
            else if (suppress0)
                  iprintf("%s\n", b->y);
            else
                  iprintf("%s %sabled\n", b->y, val ? "en" : "dis");
      }
}

/* Decode the processor state parameter field */
static void
proc_decode_state_parameter (sal_log_processor_info_t *slpi)
{
      static const struct decode_bits b[] = {
            {  0,  2, "rv" },
            {  2,  1, "rz", "rendezvous request successful", "rendezvous request unsuccessful" },
            {  3,  1, "ra", "rendezvous was attempted", "rendezvous was not attempted" },
            {  4,  1, "me", "multiple errors have occurred" },
            {  5,  1, "mn", "min state registered with PAL", "min state not registered with PAL" },
            {  6,  1, "sy", "storage integrity synchronized", "storage integrity not synchronized" },
            {  7,  1, "co", "continuable", "not continuable" },
            {  8,  1, "ci", "machine check is isolated", "machine check is not isolated" },
            {  9,  1, "us", "uncontained storage damage" },
            { 10,  1, "hd", "hardware degraded" },
            { 11,  1, "tl", "trap lost" },
            { 12,  1, "mi", "more info available" },
            { 13,  1, "pi", "ip logged is precise", "ip logged is not precise" },
            { 14,  1, "pm", "min state is precise", "min state is not precise" },
            { 15,  1, "dy", "processor dynamic state is valid", "processor dynamic state is not valid" },
            { 16,  1, "in", "interrupt caused by INIT" },
            { 17,  1, "rs", "rse is valid", "rse is not valid" },
            { 18,  1, "cm", "fault has been corrected", "fault has not been corrected" },
            { 19,  1, "ex", "machine check expected" },
            { 20,  1, "cr", "control registers are valid", "control registers are not valid" },
            { 21,  1, "pc", "performance counters are valid", "performance counters are not valid" },
            { 22,  1, "dr", "debug registers are valid", "debug registers are not valid" },
            { 23,  1, "tr", "translation registers are valid", "translation registers are not valid" },
            { 24,  1, "rr", "region registers are valid", "region registers are not valid" },
            { 25,  1, "ar", "application registers are valid", "application registers are not valid" },
            { 26,  1, "br", "branch registers are valid", "branch registers are not valid" },
            { 27,  1, "pr", "predicate registers are valid", "predicate registers are not valid" },
            { 28,  1, "fp", "floating point registers are valid", "floating point registers are not valid" },
            { 29,  1, "b1", "bank one general registers are valid", "bank one general registers are not valid" },
            { 30,  1, "b0", "bank zero general registers are valid", "bank zero general registers are not valid" },
            { 31,  1, "gr", "general registers are valid", "general registers are not valid" },
            { 59,  1, "cc", "cache check" },
            { 60,  1, "tc", "tlb check" },
            { 61,  1, "bc", "bus check" },
            { 62,  1, "rc", "register file check" },
            { 63,  1, "cc", "micro-archecture check" },
      };
      u64 psp = slpi->proc_state_parameter;
      const char *msg;

      prval("processor state parameter", psp);
      indent += 4;
      decode_reg(psp, b, ARRAY_SIZE(b), 1);
      indent -= 4;

      if (psp & (1UL << 18 /* psp.cm */)) {
            msg = "corrected";
      } else {
            switch ((psp >> 6) & 0xf /* psp.sy - psp.us */) {
            case 0x8:
                  msg = "error was not isolated, must reset system";
                  break;
            case 0xc:
                  msg = "error was isolated but not contained, must reset system";
                  break;
            case 0x4:   /* drop through */
            case 0x6:
                  msg = "error was isolated and contained, continuable if sw can recover";
                  break;
            case 0x7:
                  msg = "error was isolated, contained, and is continuable";
                  break;
            default:
                  msg = "unknown";
                  break;
            }
      }
      iprintf("PAL recovery status:\n");
      ++indent;
      iprintf("%s\n", msg);
      --indent;
}

static void
print_reg(const char *name, u64 val)
{
      iprintf("%-5s: 0x%016lx\n", name, val);
}

static void
print_2_reg(const char *name0, u64 val0, const char *name1, u64 val1)
{
      iprintf("%-5s: 0x%016lx  %-5s: 0x%016lx\n", name0, val0, name1, val1);
}

static void
decode_psr(u64 reg)
{
      static const struct decode_bits b[] = {
            {  0,  6, "", "User mask" },
            {  0,  1, "rv" },
            {  1,  1, "be", "big endian", "little endian" },
            {  2,  1, "up", "user performance monitor" },
            {  3,  1, "ac", "alignment check" },
            {  4,  1, "mfl", "lower (f2 .. f31) floating-point registers written", "lower (f2 .. f31) floating-point registers not written" },
            {  5,  1, "mfh", "upper (f32 .. f127) floating-point registers written", "upper (f32 .. f127) floating-point registers not written" },
            {  0, 24, "", "System mask" },
            {  6,  7, "rv" },
            { 13,  1, "ic", "interrupt collection" },
            { 14,  1, "i", "interrupts" },
            { 15,  1, "pk", "protection key" },
            { 16,  1, "rv" },
            { 17,  1, "dt", "data address translation" },
            { 18,  1, "dfl", "disabled floating-point low register set", "disabled floating-point low register not set" },
            { 19,  1, "dfh", "disabled floating-point high register set", "disabled floating-point high register not set" },
            { 20,  1, "sp", "secure performance monitor" },
            { 21,  1, "pp", "privileged performance monitor" },
            { 22,  1, "di", "disable instruction set transition set", "disable instruction set transition not set" },
            { 23,  1, "si", "secure interval timer" },
            { 24,  1, "db", "debug breakpoint fault" },
            { 25,  1, "lp", "lower privilege transfer trap" },
            { 26,  1, "tb", "taken branch trap" },
            { 27,  1, "rt", "register stack translation" },
            { 28,  4, "rv" },
            { 32,  2, "cpl", "current privilege level" },
            { 34,  1, "is", "IA32 instruction set", "IA64 instruction set" },
            { 35,  1, "mc", "machine check abort disabled", "machine check abort enabled" },
            { 36,  1, "it", "instruction address translation" },
            { 37,  1, "id", "instruction debug fault disabled", "instruction debug fault enabled" },
            { 38,  1, "da", "disable data access and dirty-bit faults", "enable data access and dirty-bit faults" },
            { 39,  1, "dd", "data debug fault disabled", "data debug fault enabled" },
            { 40,  1, "ss", "single step" },
            { 41,  2, "ri", "restart instruction" },
            { 43,  1, "ed", "exception deferral" },
            { 44,  1, "bn", "bank 1", "bank 0" },
            { 45,  1, "ia", "instruction access-bit faults disabled", "instruction access-bit faults enabled" },
            { 46, 18, "rv" },
      };
      decode_reg(reg, b, ARRAY_SIZE(b), 0);
}

static void
decode_isr(u64 reg)
{
      static const struct decode_bits b[] = {
            {  0, 16, "", "Code" },
            { 16,  8, "", "Vector" },
            { 24,  8, "rv" },
            { 32,  1, "x", "execute exception" },
            { 33,  1, "w", "write exception" },
            { 34,  1, "r", "read exception" },
            { 35,  1, "na", "non-access exception" },
            { 36,  1, "sp", "speculative load exception" },
            { 37,  1, "rs", "register stack interrupt" },
            { 38,  1, "ir", "incomplete register frame" },
            { 39,  1, "ni", "nested interruption" },
            { 40,  1, "so", "IA-32 supervisor override" },
            { 41,  2, "ei", "excepting instruction" },
            { 43,  1, "ed", "exception deferal" },
            { 44, 20, "rv" },
      };
      decode_reg(reg, b, ARRAY_SIZE(b), 1);
}

static void
decode_pr(u64 reg)
{
      int i, prev = -2;
      for (i = 0; i < 64; ++i) {
            if ((reg & (1UL << i)) && prev == -2) {
                  /* 0->1 transition */
                  iprintf("%sp%d", i ? ", " : "", i);
                  prev = i;
            }
            else if (!(reg & (1UL << i)) && prev != -2) {
                  /* 1->0 transition */
                  if (prev < i-1)
                        iprintf("-%d", i-1);
                  prev = -2;
            }
      }
      if (prev != -2) {
            /* trailing 1->0 transition */
            if (prev < i-1)
                  iprintf("-%d", i-1);
      }
      iprintf("\n");
}

#define CR_DCR          0
#define CR_ITM          1
#define CR_IVA          2
#define CR_PTA          8
#define CR_DUP_IPSR     16
#define CR_ISR          17
#define CR_DUP_IIP      19
#define CR_IFA          20
#define CR_ITIR         21
#define CR_IIPA         22
#define CR_DUP_IFS      23
#define CR_IIM          24
#define CR_IHA          25
#define CR_LID          64
#define CR_IVR          65
#define CR_TPR          66
#define CR_EOI          67
#define CR_IRR0         68
#define CR_IRR1         69
#define CR_IRR2         70
#define CR_IRR3         71
#define CR_ITV          72
#define CR_PMV          73
#define CR_CMCV         74
#define CR_LRR0         80
#define CR_LRR1         81

#define AR_RSC          16
#define AR_BSP          17
#define AR_BSPSTORE     18
#define AR_RNAT         19
#define AR_CCV          32
#define AR_UNAT         36
#define AR_FPSR         40
#define AR_PFS          64
#define AR_LC           65
#define AR_EC           66


static void
print_reg_array(const char *name, const u64 *val, int start, int len)
{
      int i;
      for(i = 0; i < len; i+=4, val+=4) {
            iprintf("%5s%-2d: 0x%016lx 0x%016lx 0x%016lx 0x%016lx\n",
                  name, start+i, *val, *(val+1), *(val+2), *(val+3));
      }
}


static void
print_reg_layout(const struct reg_fmt *fmt, const u64 *val)
{
      const char *fmt_str;
      int i;
      for (i = 0; fmt->name; ++fmt, ++i) {
            fmt_str = (i & 1) ? "  %-16s: 0x%016lx\n" : "%-16s: 0x%016lx";
            iprintf(fmt_str, fmt->name, *(u64 *)((char *)val + fmt->offset));
      }
      if (i & 1)
            iprintf("\n");
}

#define CROFF(x) (offsetof(sal_processor_static_info_t, cr[CR_ ## x]))
#define AROFF(x) (offsetof(sal_processor_static_info_t, ar[AR_ ## x]))

/* Display the processor min state */
static void
min_state_print (const sal_processor_static_info_t *slpi)
{
      u64 regs[16];
      static const struct reg_fmt reg_layout[] = {
            {"cr0  (dcr)",    CROFF(DCR)},
            {"cr1  (itm)",    CROFF(ITM)},
            {"cr2  (iva)",    CROFF(IVA)},
            {"cr8  (pta)",    CROFF(PTA)},
            {"cr16 (ipsr)",   CROFF(DUP_IPSR)},
            {"cr17 (isr)",    CROFF(ISR)},
            {"cr19 (iip)",    CROFF(DUP_IIP)},
            {"cr20 (ifa)",    CROFF(IFA)},
            {"cr21 (itir)",   CROFF(ITIR)},
            {"cr22 (iipa)",   CROFF(IIPA)},
            {"cr23 (ifs)",    CROFF(DUP_IFS)},
            {"cr24 (iim)",    CROFF(IIM)},
            {"cr25 (iha)",  CROFF(IHA)},
            {"cr64 (lid)",    CROFF(LID)},
            {"cr66 (tpr)",    CROFF(TPR)},
            {"cr68 (irr0)",   CROFF(IRR0)},
            {"cr69 (irr1)",   CROFF(IRR1)},
            {"cr70 (irr2)",   CROFF(IRR2)},
            {"cr71 (irr3)",   CROFF(IRR3)},
            {"cr72 (itv)",    CROFF(ITV)},
            {"cr73 (pmv)",    CROFF(PMV)},
            {"cr74 (cmcv)",   CROFF(CMCV)},
            {"cr80 (lrr0)",   CROFF(LRR0)},
            {"cr81 (lrr1)",   CROFF(LRR1)},
            {"ar16 (rsc)",  AROFF(RSC)},
            {"ar17 (bsp)",    AROFF(BSP)},
            {"ar18 (bspstore)", AROFF(BSPSTORE)},
            {"ar19 (rnat)",   AROFF(RNAT)},
            {"ar32 (ccv)",    AROFF(CCV)},
            {"ar36 (unat)",   AROFF(UNAT)},
            {"ar40 (fpsr)",   AROFF(FPSR)},
            {"ar64 (pfs)",    AROFF(PFS)},
            {"ar65 (lc)",     AROFF(LC)},
            {"ar66 (ec)",     AROFF(EC)},
            { NULL },
      };

      iprintf("Processor static data:\n");
      ++indent;
      if (slpi->min_state_area.pmsa_xip || slpi->min_state_area.pmsa_xpsr) {
            print_2_reg("xip", slpi->min_state_area.pmsa_xip, "xfs", slpi->min_state_area.pmsa_xfs);
            print_reg("xpsr", slpi->min_state_area.pmsa_xpsr);
            indent += 4;
            decode_psr(slpi->min_state_area.pmsa_xpsr);
            indent -= 4;
      }

      print_2_reg("iip", slpi->min_state_area.pmsa_iip, "iipa", slpi->cr[CR_IIPA]);

      print_reg("ipsr", slpi->min_state_area.pmsa_ipsr);
      indent += 4;
      decode_psr(slpi->min_state_area.pmsa_ipsr);
      indent -= 4;
      print_reg("isr", slpi->cr[CR_ISR]);
      indent += 4;
      decode_isr(slpi->cr[CR_ISR]);
      indent -= 4;
      print_reg("pr", slpi->min_state_area.pmsa_pr);
      indent += 4;
      decode_pr(slpi->min_state_area.pmsa_pr);
      indent -= 4;

      print_reg_layout(reg_layout, (u64 *)slpi);

      memcpy(regs+1, &(slpi->min_state_area.pmsa_gr[0]), 15*8);
      regs[0] = 0;
      print_reg_array("r", regs, 0, 16);

      print_reg_array("bk0 r", &(slpi->min_state_area.pmsa_bank0_gr[0]), 16, 16);
      print_reg_array("bk1 r", &(slpi->min_state_area.pmsa_bank1_gr[0]), 16, 16);

      memcpy(regs, slpi->br, 8*sizeof(regs[0]));
      regs[0] = slpi->min_state_area.pmsa_br0; /* get b0 from min state */
      print_reg_array("b", regs, 0, 8);
      print_reg_array("k", &(slpi->ar[0]), 0, 8);
      print_reg_array("rr", &(slpi->rr[0]), 0, 8);

      --indent;
}

/* Display the processor device error record */
static void
proc_dev_err_info_print (sal_log_processor_info_t *slpi)
{
      size_t  d_len = slpi->header.len - sizeof(sal_log_section_hdr_t);
      int                         i;
      sal_log_mod_error_info_t    *p_data;


      if (debug) {
            char    *p_data = (char *)&slpi->valid;
            iprintf("SAL_PROC_DEV_ERR SECTION DATA:  Data buffer = %p, "
                   "Data size = %ld\n", (void *)p_data, d_len);
            hexdump(p_data, d_len);
            iprintf("End of SAL_PROC_DEV_ERR SECTION DATA\n");
      }

      proc_summary(slpi);

      ++indent;
      if (slpi->valid.proc_cr_lid)
            proc_decode_lid(slpi);
      if (slpi->valid.proc_state_param)
            proc_decode_state_parameter(slpi);
      if (slpi->valid.proc_error_map)
            proc_decode_error_map(slpi);
      --indent;

      /*
       *  Note: March 2001 SAL spec states that if the number of elements in any
       *  of  the MOD_ERROR_INFO_STRUCT arrays is zero, the entire array is
       *  absent. Also, current implementations only allocate space for number of
       *  elements used.  So we walk the data pointer from here on.
       */
      p_data = &slpi->info[0];

#define check_info_debug(type) \
      if (debug && slpi->valid.num_ ## type ## _check) \
            iprintf("num_" # type "_check %d " # type "_check_info 0x%p\n", slpi->valid.num_ ## type ## _check, p_data);

      /* Print the cache check information if any*/
      check_info_debug(cache);
      for (i = 0 ; i < slpi->valid.num_cache_check; i++, p_data++)
            cache_check_info_print(i, p_data);

      /* Print the tlb check information if any*/
      check_info_debug(tlb);
      for (i = 0 ; i < slpi->valid.num_tlb_check; i++, p_data++)
            tlb_check_info_print(i, p_data);

      /* Print the bus check information if any*/
      check_info_debug(bus);
      for (i = 0 ; i < slpi->valid.num_bus_check; i++, p_data++)
            bus_check_info_print(i, p_data);

      /* Print the reg file check information if any*/
      check_info_debug(reg_file);
      for (i = 0 ; i < slpi->valid.num_reg_file_check; i++, p_data++)
            reg_file_check_info_print(i, p_data);

      /* Print the ms check information if any*/
      check_info_debug(ms);
      for (i = 0 ; i < slpi->valid.num_ms_check; i++, p_data++)
            ms_check_info_print(i, p_data);

      /* Print CPUID registers if any*/
      if (slpi->valid.cpuid_info) {
            u64     *p = (u64 *)p_data;
            if (debug)
                  iprintf("cpuid_info 0x%p\n", p_data);
            iprintf("CPUID Regs: %#lx %#lx %#lx %#lx\n", p[0], p[1], p[2], p[3]);
            p_data++;
      }

      /* Print processor static info if any */
      if (slpi->valid.psi_static_struct) {
            sal_processor_static_info_t *spsi = SAL_LPI_PSI_INFO(slpi);
            if (debug)
                  iprintf("psi_static_struct start 0x%p end 0x%p\n", spsi, spsi+1);
            if (spsi->valid.minstate) {
                  iprintf("\n");
                  min_state_print(spsi);
            }
      }
}

/* Format and Log the SAL Platform Error Record */
void
platform_info_print (sal_log_record_header_t *lh, int use_sal, int fd_data, int cpu, int *oemdata_fd)
{
      sal_log_section_hdr_t   *slsh;
      int               n_sects;
      int               ercd_pos;

      decode_oem_record_start = lh;
      decode_oem_fd_data = fd_data;
      decode_oem_use_sal = use_sal;
      decode_oem_cpu = cpu;
      decode_oem_oemdata_fd = oemdata_fd;

      iprintf_count = 0;

      if (debug > 1)
            prt_record_header(lh);

      if ((ercd_pos = sizeof(sal_log_record_header_t)) >= lh->len) {
            iprintf("%s: truncated SAL error record. len = %d\n",
                         __FUNCTION__, lh->len);
            return;
      }

      /* Print record header info */
      rec_header_print(lh);

      for (n_sects = 0; (ercd_pos < lh->len); n_sects++, ercd_pos += slsh->len) {
            /* point to next section header */
            slsh = (sal_log_section_hdr_t *)((char *)lh + ercd_pos);
            if (slsh->len < sizeof(*slsh) || slsh->len > 1000000 /* arbitrary */) {
                  iprintf("\n\nError: section header length (%d) is out of range\n\n", slsh->len);
                  prt_record_header(lh);
                  prt_section_header(slsh);
                  return;
            }

            if (debug > 1) {
                  prt_section_header(slsh);
                  if (efi_guidcmp(slsh->guid, SAL_PROC_DEV_ERR_SECT_GUID) != 0) {
                        size_t  d_len = slsh->len - sizeof(sal_log_section_hdr_t);
                        char    *p_data = (char *)&((sal_log_mem_dev_err_info_t *)slsh)->valid;
                        iprintf("Start of Platform Err Data Section:  Data buffer = %p, "
                               "Data size = %ld\n", (void *)p_data, d_len);
                        hexdump(p_data, d_len);
                        iprintf("End of Platform Err Data Section\n");
                  }
            }

            /*
             *  Now process the various record sections
             */
            if (efi_guidcmp(slsh->guid, SAL_PROC_DEV_ERR_SECT_GUID) == 0) {
                  iprintf("Processor Device Error Info Section\n");
                  ++indent;
                  proc_dev_err_info_print((sal_log_processor_info_t *)slsh);
                  --indent;
            } else if (efi_guidcmp(slsh->guid, SAL_PLAT_MEM_DEV_ERR_SECT_GUID) == 0) {
                  ++indent;
                  iprintf("Platform Memory Device Error Info Section\n");
                  mem_dev_err_info_print((sal_log_mem_dev_err_info_t *)slsh);
                  --indent;
            } else if (efi_guidcmp(slsh->guid, SAL_PLAT_SEL_DEV_ERR_SECT_GUID) == 0) {
                  ++indent;
                  iprintf("Platform SEL Device Error Info Section\n");
                  sel_dev_err_info_print((sal_log_sel_dev_err_info_t *)slsh);
                  --indent;
            } else if (efi_guidcmp(slsh->guid, SAL_PLAT_PCI_BUS_ERR_SECT_GUID) == 0) {
                  iprintf("Platform PCI Bus Error Info Section\n");
                  ++indent;
                  pci_bus_err_info_print((sal_log_pci_bus_err_info_t *)slsh);
                  --indent;
            } else if (efi_guidcmp(slsh->guid, SAL_PLAT_SMBIOS_DEV_ERR_SECT_GUID) == 0) {
                  iprintf("Platform SMBIOS Device Error Info Section\n");
                  ++indent;
                  smbios_dev_err_info_print((sal_log_smbios_dev_err_info_t *)slsh);
                  --indent;
            } else if (efi_guidcmp(slsh->guid, SAL_PLAT_PCI_COMP_ERR_SECT_GUID) == 0) {
                  iprintf("Platform PCI Component Error Info Section\n");
                  ++indent;
                  pci_comp_err_info_print((sal_log_pci_comp_err_info_t *)slsh);
                  --indent;
            } else if (efi_guidcmp(slsh->guid, SAL_PLAT_SPECIFIC_ERR_SECT_GUID) == 0) {
                  iprintf("Platform Specific Error Info Section\n");
                  ++indent;
                  plat_specific_err_info_print((sal_log_plat_specific_err_info_t *) slsh);
                  --indent;
            } else if (efi_guidcmp(slsh->guid, SAL_PLAT_HOST_CTLR_ERR_SECT_GUID) == 0) {
                  iprintf("Platform Host Controller Error Info Section\n");
                  ++indent;
                  host_ctlr_err_info_print((sal_log_host_ctlr_err_info_t *)slsh);
                  --indent;
            } else if (efi_guidcmp(slsh->guid, SAL_PLAT_BUS_ERR_SECT_GUID) == 0) {
                  iprintf("Platform Bus Error Info Section\n");
                  ++indent;
                  plat_bus_err_info_print((sal_log_plat_bus_err_info_t *)slsh);
                  --indent;
            } else {
                  char out[40];
                  iprintf("%s: unsupported record section %s\n", __FUNCTION__,
                           efi_guid_unparse(&slsh->guid, out));
                  continue;
            }
      }

      if (debug > 1)
            iprintf("%s: found %d sections in SAL error record. len = %d\n",
                         __FUNCTION__, n_sects, lh->len);
      if (!n_sects)
            iprintf("No Platform Error Info Sections found\n");
      return;
}

Generated by  Doxygen 1.6.0   Back to index