/* Copyright 2016 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include <stdint.h>
#include <stdio.h>
#include "ec_panicinfo.h"

static void print_panic_reg(int regnum, const uint32_t *regs, int index)
{
	static const char * const regname[] = {
		"r0 ", "r1 ", "r2 ", "r3 ", "r4 ",
		"r5 ", "r6 ", "r7 ", "r8 ", "r9 ",
		"r10", "r11", "r12", "sp ", "lr ",
		"pc "};

	printf("%s:", regname[regnum]);
	if (regs)
		printf("%08x", regs[index]);
	else
		printf("        ");
	printf((regnum & 3) == 3 ? "\n" : " ");
}

static int parse_panic_info_cm(const struct panic_data *pdata)
{
	const uint32_t *lregs = pdata->cm.regs;
	const uint32_t *sregs = NULL;
	enum {
		ORIG_UNKNOWN = 0,
		ORIG_PROCESS,
		ORIG_HANDLER
	} origin = ORIG_UNKNOWN;
	int i;
	const char *panic_origins[3] = {"", "PROCESS", "HANDLER"};

	printf("Saved panic data:%s\n",
	       (pdata->flags & PANIC_DATA_FLAG_OLD_HOSTCMD ? "" : " (NEW)"));

	if (pdata->struct_version == 2)
		origin = ((lregs[11] & 0xf) == 1 || (lregs[11] & 0xf) == 9) ?
			 ORIG_HANDLER : ORIG_PROCESS;

	/*
	 * In pdata struct, 'regs', which is allocated before 'frame', has
	 * one less elements in version 1. Therefore, if the data is from
	 * version 1, shift 'sregs' by one element to align with 'frame' in
	 * version 1.
	 */
	if (pdata->flags & PANIC_DATA_FLAG_FRAME_VALID)
		sregs = pdata->cm.frame - (pdata->struct_version == 1 ? 1 : 0);

	printf("=== %s EXCEPTION: %02x ====== xPSR: %08x ===\n",
	       panic_origins[origin],
	       lregs[1] & 0xff, sregs ? sregs[7] : -1);
	for (i = 0; i < 4; ++i)
		print_panic_reg(i, sregs, i);
	for (i = 4; i < 10; ++i)
		print_panic_reg(i, lregs, i - 1);
	print_panic_reg(10, lregs, 9);
	print_panic_reg(11, lregs, 10);
	print_panic_reg(12, sregs, 4);
	print_panic_reg(13, lregs, origin == ORIG_HANDLER ? 2 : 0);
	print_panic_reg(14, sregs, 5);
	print_panic_reg(15, sregs, 6);

	return 0;
}

static int parse_panic_info_nds32(const struct panic_data *pdata)
{
	const uint32_t *regs = pdata->nds_n8.regs;
	uint32_t itype = pdata->nds_n8.itype;
	uint32_t ipc = pdata->nds_n8.ipc;
	uint32_t ipsw = pdata->nds_n8.ipsw;

	printf("Saved panic data:%s\n",
	       (pdata->flags & PANIC_DATA_FLAG_OLD_HOSTCMD ? "" : " (NEW)"));

	printf("=== EXCEP: ITYPE=%x ===\n", itype);
	printf("R0  %08x R1  %08x R2  %08x R3  %08x\n",
		     regs[0], regs[1], regs[2], regs[3]);
	printf("R4  %08x R5  %08x R6  %08x R7  %08x\n",
		     regs[4], regs[5], regs[6], regs[7]);
	printf("R8  %08x R9  %08x R10 %08x R15 %08x\n",
		     regs[8], regs[9], regs[10], regs[11]);
	printf("FP  %08x GP  %08x LP  %08x SP  %08x\n",
		     regs[12], regs[13], regs[14], regs[15]);
	printf("IPC %08x IPSW   %05x\n", ipc, ipsw);
	printf("SWID of ITYPE: %x\n", ((itype >> 16) & 0x7fff));

	return 0;
}

int parse_panic_info(const struct panic_data *pdata)
{
	/*
	 * We only understand panic data with version <= 2. Warn the user
	 * of higher versions.
	 */
	if (pdata->struct_version > 2)
		fprintf(stderr,
			"Unknown panic data version (%d). "
			"Following data may be incorrect!\n",
			pdata->struct_version);

	/* Validate magic number */
	if (pdata->magic != PANIC_DATA_MAGIC)
		fprintf(stderr,
			"Incorrect panic magic (%d). "
			"Following data may be incorrect!\n",
			pdata->magic);

	switch (pdata->arch) {
	case PANIC_ARCH_CORTEX_M:
		return parse_panic_info_cm(pdata);
	case PANIC_ARCH_NDS32_N8:
		return parse_panic_info_nds32(pdata);
	default:
		fprintf(stderr, "Unknown architecture (%d).\n", pdata->arch);
		break;
	}
	return -1;
}
