/*
 * 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>
 */

#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) {
			p1 = strstr(buffer, protocol1);
			if (p1)
				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\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);
}

/* 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;
}
