#include <stdio.h>
#include <stdlib.h>
#include <string.h>


static void decode_ll(int code)
{
	printf ("\tMemory/IO : ");
	switch (code) {
		case 0:	printf ("Memory access");	break;
		case 1:	printf ("Reserved");	break;
		case 2:	printf ("I/O");	break;
		case 3:	printf ("Other");	break;
		default:
				printf ("Invalid");	break;
	}
	printf ("\n");
}


static void decode_transaction(int code)
{
	printf ("\tTransaction type : ");
	switch (code) {
		case 0:	printf ("Instruction");	break;
		case 1:	printf ("Data");	break;
		case 2:	printf ("Generic");	break;
		default:
				printf ("Invalid");	break;
	}
	printf ("\n");
}


static void decode_request(int code)
{
	printf ("\tRequest: ");
	switch (code) {
		case 0:	printf ("Generic error");	break;
		case 1:	printf ("Generic read");	break;
		case 2:	printf ("Generic write");	break;
		case 3:	printf ("Data read");		break;
		case 4:	printf ("Data write");		break;
		case 5:	printf ("Instruction fetch");	break;
		case 6:	printf ("Prefetch");	break;
		case 7:	printf ("Eviction");	break;
		case 8:	printf ("Snoop");		break;
		default:
				printf ("Invalid");	break;
	}
	printf ("\n");
}


static void parse_bank(int bank, unsigned long long value, unsigned long long addr)
{
	int low, high;
	int errorcode;
	int ms_errorcode;

	errorcode = value & 0xffff;
	ms_errorcode = (value>>17) & 0x7fff;	/* bits 17:31*/

	low = value & 0xffff;
	high = value >> 32;

	/* Could be simple MCA code */
	switch (errorcode) {
		case 0:	printf ("No error reported\n");
				return;
		case 1:	printf ("Not a known MCA error class\n");
				return;
		case 2:	printf ("Internal microcode ROM has parity error\n");
				return;
		case 3:	printf ("External error caused by another CPU\n");
				return;
		case 4:	printf ("Functional redundancy check failed\n");
				return;
	}
	if ((errorcode & 0xfc00) == 0x0400) {
		printf ("Internal error is not a known MCA error class\n");
		return;
	}

	printf ("parsebank(%d): %llx @ %llx\n", bank, value, addr);
	if ((high & (1<<31)) == 0) {
		printf ("Bank contents invalid, not parsing\n");
		return;
	}

	if ((low & (1<<16)) == 1)
		printf ("\tIn");
	else
		printf ("\tEx");
	printf ("ternal tag parity error\n");

	if (!(high & (1<<13)))
		printf ("\tUncorrectable ECC error\n");
	if (!(high & (1<<14)))
		printf ("\tCorrectable ECC error\n");

	if (!(high & (1<<25)))
		printf ("\tCPU state corrupt. Restart not possible\n");
	if (!(high & (1<<26)))
		printf ("\tAddress in addr register valid\n");
	if (!(high & (1<<27)))
		printf ("\tMISC register information valid\n");
		// TODO: Parse MISC register.
	if (!(high & (1<<28)))
		printf ("\tError enabled in control register\n");
	if (!(high & (1<<29)))
		printf ("\tError not corrected.\n");
	if (!(high & (1<<30)))
		printf ("\tError overflow\n");

	/* Decode error code. */
	if ((errorcode & (0xff00)) == 0) {
		printf ("\tTLB Error\n");
		decode_transaction ((errorcode>>2 & (1<<0 | 1<<1)));
		decode_ll (errorcode & (1<<0 | 1<<1));
	}

	if ((errorcode & (0xff00)) == 0x0100) {
		printf ("\tMemory heirarchy error\n");
		decode_request ((errorcode & 0xf)>>4);
		decode_transaction ((errorcode>>2 & (1<<0 | 1<<1)));
		decode_ll (errorcode & (1<<0 | 1<<1));
		return;
	}

	if ((errorcode & (0xf800)) == 0x0800) {
		printf ("\tBus and interconnect error\n");
		printf ("\tParticipation: ");
		switch ((errorcode & 0x0600) >>9) {
			case 0:	printf ("Local processor originated request");	break;
			case 1:	printf ("Local processor responded to request");	break;
			case 2:	printf ("Local processor observed error as third party");	break;
			case 3:	printf ("Generic");	break;
			default:
					printf ("Invalid");	break;
		}
		printf ("\n");

		printf ("\tTimeout: ");
		switch (errorcode & 0x0100) {
			case 0:	printf ("Request did not timeout");	break;
			case 1:	printf ("Request timed out");	break;
		}
		printf ("\n");

		decode_request ((errorcode & 0xf)>>4);
		decode_transaction ((errorcode>>2 & (1<<0 | 1<<1)));
		decode_ll (errorcode & (1<<0 | 1<<1));
		return;
	}

}


void parse_status(unsigned long long status)
{
	printf ("Status: (%lld) ", status);
	if (status & 1<<2)
		printf ("Machine Check in progress.\n");

	if (status & 1<<1)
		printf ("Error IP valid\n");

	if (status & 1<<0)
		printf ("Restart IP valid.\n");
	else
		printf ("Restart IP invalid.\n");
}


int main(void)
{
/* Sample output..
Sep  4 21:43:41 hamlet kernel: CPU 0: Machine Check Exception: 0000000000000004
Sep  4 21:43:41 hamlet kernel: Bank 1: f600200000000152 at 7600200000000152
Sep  4 21:43:41 hamlet kernel: Bank 2: d40040000000017a at 540040000000017a
Sep  4 21:43:41 hamlet kernel: Kernel panic: CPU context corrupt
*/

	parse_status (0x0000000000000004);
//	parse_bank(1, 0xf600200000000152, 0x7600200000000152);
//	parse_bank(2, 0xd40040000000017a, 0x540040000000017a);
	//parse_bank(4, 0xb200000000040151, 0x0);
	parse_bank(4, 0xb601a00022000800, 0x0);
	return 0;
}
