/*************************************************************************
*
*             Copyright Mentor Graphics Corporation 2013
*                         All Rights Reserved.
*
* THIS WORK CONTAINS TRADE SECRET AND PROPRIETARY INFORMATION WHICH IS
* THE PROPERTY OF MENTOR GRAPHICS CORPORATION OR ITS LICENSORS AND IS
* SUBJECT TO LICENSE TERMS.
*
**************************************************************************

**************************************************************************
*
* FILE NAME
*
*       arm_proc_exception.c
*
* COMPONENT
*
*       PROC - Nucleus Processes
*
* DESCRIPTION
*
*       This file contains architecture specific exception handling
*
* DATA STRUCTURES
*
*       None
*
* FUNCTIONS
*
*       PROC_AR_Exception
*
* DEPENDENCIES
*
*       nucleus.h
*       nu_kernel.h
*       proc_extern.h
*       proc_core.h
*       arch_proc_exception.h
*
*************************************************************************/
#include "nucleus.h"
#include "kernel/nu_kernel.h"
#include "kernel/proc_extern.h"
#include "os/kernel/process/core/proc_core.h"
#include "process/core/arch_proc_exception.h"

VOID TCCT_Schedule(VOID);
VOID ERC_System_Error(INT error_code);

/* Currently nested exceptions are not supported, this data structure
   will contain the architecture specific information for all
   exceptions that occur */
static NU_ARCH_EXCEPTION proc_ar_exception_cb;

/************************************************************************
*
* FUNCTION
*
*       PROC_AR_Exception
*
* DESCRIPTION
*
*       This exception handler retrieves the address/point of exception
*       and the type of exception that occurred and calls the general
*       handler.
*
* INPUTS
*
*       exception_num
*       stack_frame
*
* OUTPUTS
*
*       None
*
************************************************************************/
VOID PROC_AR_Exception(INT exception_num, VOID *stack_frame)
{
    ESAL_AR_STK_MIN *stk_frame;
    UNSIGNED         exception_addr = 0;
    UNSIGNED         exception_type = 0;
    UNSIGNED         generic_type = EXIT_INVALID_MEM;
    UNSIGNED         exit_type;

#ifdef CFG_NU_OS_ARCH_ARM_PROCESS_MEM_MGMT_ENABLE
    /* Set manager domain access */
    ESAL_AR_SET_DOMAIN(ESAL_AR_DACR_MANAGER);
#endif

    /* Get the Stack frame */
    stk_frame = (ESAL_AR_STK_MIN *)stack_frame;

    /* Check for data abort exception. */
    if (exception_num == ESAL_AR_DATA_EXCEPT_VECTOR_ID)
    {
        /* Adjust data abort return address */
        stk_frame->rtn_address = stk_frame -> rtn_address - 8;

        /* Read the address of the exception from the FAR  */
        ESAL_TS_RTE_CP_READ(ESAL_TS_RTE_CP15, 0, &exception_addr,  ESAL_TS_RTE_C6, ESAL_TS_RTE_C0, 0);

        /* Read the type of exception from the DFSR on the ARM core. */
        ESAL_TS_RTE_CP_READ(ESAL_TS_RTE_CP15, 0, &exception_type,  ESAL_TS_RTE_C5, ESAL_TS_RTE_C0, 0);

        /* Clear the unwanted bits */
        exception_type &= PROC_FSR_STATUS_MASK;
    }
    /* Check for prefetch abort exception. */
    else if (exception_num == ESAL_AR_PREFETCH_EXCEPT_VECTOR_ID)
    {
        /* Adjust prefetch abort return address */
        stk_frame->rtn_address = stk_frame -> rtn_address - 4;

        /* Read the type of exception from the IFSR on the ARM core. */
        ESAL_TS_RTE_CP_READ(ESAL_TS_RTE_CP15, 0, &exception_type,  ESAL_TS_RTE_C5, ESAL_TS_RTE_C0, 1);

        /* Clear the unwanted bits */
        exception_type &= PROC_FSR_STATUS_MASK;

        /* Use this bit to distinguish from the data abort */
        exception_type |= PROC_PREFETCH_ABORT;

        /* Return address is the point of exception */
        exception_addr = (UNSIGNED)stk_frame -> rtn_address;
    }
    else if (exception_num == ESAL_AR_UNDEF_EXCEPT_VECTOR_ID)
    {
        /* Adjust undefined instruction return address */
        stk_frame->rtn_address = stk_frame -> rtn_address - 4;

        /* Set the exception type to undefined */
        exception_type = PROC_UNDEF_ABORT;

        /* Return address is the point of exception */
        exception_addr = (UNSIGNED)stk_frame -> rtn_address;

        /* Set to illegal instruction exception */
        generic_type = EXIT_ILLEGAL_INST;
    }

    /* Setup the additional information control block */
    proc_ar_exception_cb.stack_min = stk_frame;
    proc_ar_exception_cb.type = exception_type;

    /* Call the handler */
    exit_type = PROC_Exception((VOID *)exception_addr, generic_type, (VOID *)(stk_frame -> rtn_address), (VOID*)&proc_ar_exception_cb);

    if (exit_type == NU_PROC_SCHEDULE)
    {
        /* Interrupts will already be locked out in the exception,
           maintain that state and set the mode back to system, this
           will modify the current stack */
        ESAL_TS_RTE_CPSR_C_WRITE(ESAL_AR_INT_CPSR_SYS_DISABLED);

        /* Switch to the system stack before returning to the scheduler */
        ESAL_GE_STK_SYSTEM_SP_SET();

        /* Restart the scheduler */
        TCCT_Schedule();
    }
    else if (exit_type == NU_PROC_RESUME_TASK)
    {
#ifdef CFG_NU_OS_ARCH_ARM_PROCESS_MEM_MGMT_ENABLE
        /* Set client domain access */
        ESAL_AR_SET_DOMAIN(ESAL_AR_DACR_CLIENT);
#endif

        /* Fall through and return to point of exception,
           if exception was not handled correctly exception
           will occur again */
    }
    else
    {
        /* Unknown return type */
        ERC_System_Error(NU_UNHANDLED_EXCEPTION);
    }
}
