/*
 * NPLL - PowerPC Exception handling
 *
 * Copyright (C) 2025-2026 Techflash
 *
 * Based on code in BootMii ppcskel:
 * Copyright (C) 2008		Segher Boessenkool <segher@kernel.crashing.org>
 *
 * Original license disclaimer:
 * This code is licensed to you under the terms of the GNU GPL, version 2;
 * see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
*/

#include <stdio.h>
#include <string.h>
#include <npll/cache.h>
#include <npll/irq.h>
#include <npll/panic.h>
#include <npll/timer.h>
#include <npll/types.h>
#include <npll/utils.h>

extern char exception_2200_start, exception_2200_end;

static void dump_stack_trace(u32 *sp) {
	u32 prev_sp, lr;
	int depth;
	printf("Stack trace:\n");

	for (depth = 0; sp && depth < 32; depth++) {
		prev_sp = sp[0];
		lr = sp[1];

		printf("  #%d  SP=0x%08x  LR=0x%08x\r\n", depth, (u32)sp, lr);

		// sanity checks
		if (prev_sp <= (u32)sp || prev_sp == 0 || prev_sp == 0xffffffff)
			break;

		sp = (u32 *)prev_sp;
	}
}

struct exceptionFrame {
	u32 gpr[32];
	u32 cr;
	u32 xer;
	u32 lr;
	u32 ctr;
	u32 srr0;
	u32 srr1;
	u32 dar;
	u32 dsisr;
};

#define MAX_EXCEPTION_RECURSION 32

static struct exceptionFrame exceptionFrames[MAX_EXCEPTION_RECURSION];
static uint exceptionRecursionCount = 0;

void __attribute__((noreturn)) E_Handler(int exception) {
	u32 sp;
	struct exceptionFrame *frame;
	int i;

	frame = (struct exceptionFrame *)physToCached(0x2000);

	if (exception == 0x0500) {
		IRQ_Handle();
		__builtin_unreachable();
	}
	else if (exception == 0x0900) {
		if (exceptionRecursionCount >= MAX_EXCEPTION_RECURSION)
			panic("DEC exception recursion overflow");
		memcpy(&exceptionFrames[exceptionRecursionCount++], frame, sizeof(struct exceptionFrame));
		IRQ_Enable();
		T_DECHandler();
		IRQ_Disable();
		memcpy(frame, &exceptionFrames[--exceptionRecursionCount], sizeof(struct exceptionFrame));
		IRQ_Return();
		__builtin_unreachable();
	}

	printf("\r\nException %04x occurred!\r\n", exception);

	sp = frame->gpr[1];

	printf("\r\n R0..R7    R8..R15  R16..R23  R24..R31\r\n");
	for (i = 0; i < 8; i++)
		printf("%08x  %08x  %08x  %08x\r\n", frame->gpr[0 + i], frame->gpr[8 + i], frame->gpr[16 + i], frame->gpr[24 + i]);

	printf("\r\n CR/XER    LR/CTR  SRR0/SRR1 DAR/DSISR\r\n");
	printf("%08x  %08x  %08x  %08x\r\n", frame->cr, frame->lr, frame->srr0, frame->dar);
	printf("%08x  %08x  %08x  %08x\r\n", frame->xer, frame->ctr, frame->srr1, frame->dsisr);

	dump_stack_trace((u32 *)sp);

	panic("Got fatal exception");
}

void E_Init(void) {
	u32 vector, len_2200, *insn;
	TRACE();

	for (vector = 0x100; vector < 0x1800; vector += 0x10) {
		insn = physToCached(vector);

		insn[0] = 0xbc002000;			// stmw 0,0x2000(0)
		insn[1] = 0x38600000 | (u32)vector;	// li 3,vector
		insn[2] = 0x48002202;			// ba 0x2200
		insn[3] = 0;
	}
	dcache_flush_icache_invalidate(physToCached(0x100), 0x1f00);

	len_2200 = (u32)&exception_2200_end - (u32)&exception_2200_start;
	memcpy(physToCached(0x2200), &exception_2200_start, len_2200);
	dcache_flush_icache_invalidate(physToCached(0x2200), len_2200);
}
