/**
 * @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 goes here>
 */

#include "drStd.h"
#include "DrApi/DrApiMm.h"
#include "DrApi/DrApiThread.h"
#include "DrApi/DrApiIpcMsg.h"
#include "DrApi/DrApiLogging.h"

#include "drCommon.h"
#include "drUtils.h"

#include "sec_fr_common.h"
#include "sec_fr_pal_common.h"

/* IPC handler stack */
#if (TBASE_API_LEVEL<=5)
EXTERNAL_STACK(drIpchStack)
#else
extern uint32_t *drIpchStack;
#endif

/* IPC handler entry function */
extern _NORETURN void drIpchLoop( void );

/**
 * Cleanup function
 */
static void doCleanup( void ) {
    /**
     * TODO: Add cleanup code here
     *
     */
}

/**
 * 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;

    PAL_LOGV("[Secure SEC_FR driver] drExchLoop(): Exception handler thread is running");

    for (;;) {
        // wait for exception
        if ( E_OK != drApiIpcWaitForMessage(
                    &ipcPartner,
                    &mr0,
                    &mr1,
                    &mr2) ) {
            /* Unable to receive IPC message */
            PAL_LOGE("[Secure SEC_FR driver] 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:
            PAL_LOGV("[Secure SEC_FR driver] drExchLoop(): TRAP_SEGMENTATION case");
            /* Update ip and sp accordingly and restart the thread */
            switch(faultedThread) {
            //--------------------------------------
            case DRIVER_THREAD_NO_IPCH:
                PAL_LOGV("[Secure SEC_FR driver] drExchLoop(): DRIVER_THREAD_NO_IPCH case");
                ip = FUNC_PTR(drIpchLoop);
#if (TBASE_API_LEVEL<=5)
                sp = getStackTop(drIpchStack);
#else
                sp = drIpchStack;
#endif
                break;
            //--------------------------------------
            default:
                PAL_LOGE("[Secure SEC_FR driver] drExchLoop(): Unknown thread. This should never happen");
                break;
            }

            if ((ip != NULL) && (sp != NULL)) {
                PAL_LOGV("[Secure SEC_FR driver] drExchLoop(): drUtilsRestartThread() is being called");
                /* Set sp and ip accordingly and resume execution if DCIH thread */
                if (E_OK != drUtilsRestartThread(
                            faultedThread,
                            ip,
                            sp)) {
                    PAL_LOGE("[Secure SEC_FR driver] drExchLoop(): restarting thread failed");
                }
            }

            break;
        //--------------------------------------
        case TRAP_ALIGNMENT:
        case TRAP_UNDEF_INSTR:
            /**
             * This should never happen. If it does, do the cleanup and exit gracefully
             */
            PAL_LOGV("[Secure SEC_FR driver] drExchLoop(): case TRAP_ALIGNMENT TRAP_UNDEF_INSTR");
            doCleanup();

            /* Stop IPC handler thread */
            if (E_OK != drApiStopThread(DRIVER_THREAD_NO_IPCH)) {
                PAL_LOGV("[Secure SEC_FR driver] drExchLoop(): Unable to stop IPC handler thread");
            }

            /* Stop main thread */
            if (E_OK != drApiStopThread(NILTHREAD)) {
                PAL_LOGV("[Secure SEC_FR driver] drExchLoop(): Unable to stop main thread");
            }

            /* Will not come to this point */
            break;
        //--------------------------------------
        default:

            PAL_LOGV("[Secure SEC_FR driver] drExchLoop():  switch(mr1) default case");
            /* TODO: Update this sestion accordingly
            *
            * Unknown exception occured.
            *  Do cleanup in case
             */
            doCleanup();
            break;
        }
    }
}

