/**
 * @file   drExcHandler.c
 * @brief  Implements exception handler of the driver.
 *
 * Responsible for handling exceptions cause by IPC and DCI handler threads.
 * Restarts the thread that causes specific exception. runs with higher priority
 * than IPC and DCI handler threads
 *
 * Copyright (C) 2012 Giesecke & Devrient GmbH
 */

#include "drStd.h"
#include "DrApi/DrApiThread.h"

#include "drCommon.h"
#include "drUtils.h"
#include "dbg.h"

#if TBASE_API_LEVEL >= 8
extern uint32_t *drIpchStack;
#else
/* IPC handler stack */
EXTERNAL_STACK(drIpchStack)
#endif

/* IPC handler entry function */
extern _NORETURN void drIpchLoop(void);

/**
 * Cleanup function
 */
static void doCleanup(void)
{
	/* Nothing to do */
}

/**
 * Exception handler loop.
 */
_NORETURN void drExchLoop(void)
{
	threadno_t faultedThread;
	threadid_t ipcPartner;
	uint32_t mr0, mr1, mr2;
	addr_t ip = NULL;
	addr_t sp = NULL;

	for (;;) {
	/* wait  for exception */
	if (E_OK != drApiIpcWaitForMessage(&ipcPartner, &mr0, &mr1, &mr2)) {
		/* Unable to receive IPC message */
		sec_err("drExchLoop(): drApiIpcWaitForMessage failed");
		continue;
	}

	/* mr0 holds threadid value of thread that caused the exception */
	faultedThread = GET_THREADNO(mr0);

	/* Process exception */
	switch (mr1) {
	case TRAP_SEGMENTATION:
		sec_err("drExchLoop(): TRAP Segmentation exception occurs");
		/* Update ip and sp accordingly and restart the thread */
		switch (faultedThread) {
		case DRIVER_THREAD_NO_IPCH:
			sec_err("drExchLoop(): IPCH thread caused exception");
			ip = FUNC_PTR(drIpchLoop);
#if TBASE_API_LEVEL >= 8
			sp = drIpchStack;
#else
			sp = getStackTop(drIpchStack);
#endif
			break;
		default:
			sec_err("drExchLoop(): Unknown thread. This should never happen");
			break;
		}

		if ((ip != NULL) && (sp != NULL)) {
			/* Set sp and ip accordingly
					and resume execution if DCIH thread */
			if (E_OK != drUtilsRestartThread(faultedThread, ip, sp))
				sec_err("drExchLoop(): restarting thread failed");
		}
		break;
	case TRAP_ALIGNMENT:
		sec_err("drExchLoop(): TRAP Alignment exception occurs");
		/* Update ip and sp accordingly and restart the thread */
		switch (faultedThread) {
		case DRIVER_THREAD_NO_IPCH:
			sec_err("drExchLoop(): IPCH thread caused exception");
			ip = FUNC_PTR(drIpchLoop);
#if TBASE_API_LEVEL >= 8
			sp = drIpchStack;
#else
			sp = getStackTop(drIpchStack);
#endif
			break;
		default:
			sec_err("drExchLoop(): Unknown thread. This should never happen");
			break;
		}

		if ((ip != NULL) && (sp != NULL)) {
			/* Set sp and ip accordingly and resume execution if DCIH thread */
			if (E_OK != drUtilsRestartThread(faultedThread, ip, sp))
				sec_err("drExchLoop(): restarting thread failed");
		}
		break;
	case TRAP_UNDEF_INSTR:
		sec_err("drExchLoop(): TRAP Undefined Instruction exception occurs");
		/* Update ip and sp accordingly and restart the thread */
		switch (faultedThread) {
		case DRIVER_THREAD_NO_IPCH:
			sec_err("drExchLoop(): IPCH thread caused exception");
			ip = FUNC_PTR(drIpchLoop);
#if TBASE_API_LEVEL >= 8
			sp = drIpchStack;
#else
			sp = getStackTop(drIpchStack);
#endif
			break;
		default:
			sec_err("drExchLoop(): Unknown thread. This should never happen");
			break;
		}

		if ((ip != NULL) && (sp != NULL)) {
			/* Set sp and ip accordingly and resume execution if DCIH thread */
			if (E_OK != drUtilsRestartThread(faultedThread, ip, sp))
				sec_err("drExchLoop(): restarting thread failed");
		}
		break;
	default:
		/**
		 * TODO-2012-09-26-gurel: Update this section accordingly
		 *
		 * Unknown exception occured. Do cleanup in case
		 */
		doCleanup();
		break;
	}
	}
}
