//==========================================================================
//
//      except.cxx
//
//      POSIX exception translation
//
//==========================================================================
//####COPYRIGHTBEGIN####
//                                                                          
// -------------------------------------------                              
// The contents of this file are subject to the Red Hat eCos Public License 
// Version 1.1 (the "License"); you may not use this file except in         
// compliance with the License.  You may obtain a copy of the License at    
// http://www.redhat.com/                                                   
//                                                                          
// Software distributed under the License is distributed on an "AS IS"      
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the 
// License for the specific language governing rights and limitations under 
// the License.                                                             
//                                                                          
// The Original Code is eCos - Embedded Configurable Operating System,      
// released September 30, 1998.                                             
//                                                                          
// The Initial Developer of the Original Code is Red Hat.                   
// Portions created by Red Hat are                                          
// Copyright (C) 1998, 1999, 2000 Red Hat, Inc.                             
// All Rights Reserved.                                                     
// -------------------------------------------                              
//                                                                          
//####COPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):           nickg
// Contributors:        nickg
// Date:                2000-03-27
// Purpose:             POSIX exception translation
// Description:         This file contains code to translate eCos hardware
//                      exceptions into POSIX signals.
//              
//              
//
//####DESCRIPTIONEND####
//
//==========================================================================

#include <pkgconf/hal.h>
#include <pkgconf/kernel.h>
#include <pkgconf/posix.h>

#include <cyg/kernel/ktypes.h>          // base kernel types
#include <cyg/infra/cyg_trac.h>         // tracing macros
#include <cyg/infra/cyg_ass.h>          // assertion macros

#include <cyg/infra/diag.h>

#include "pprivate.h"                   // POSIX private header

#include <signal.h>                     // our header

#include <cyg/kernel/thread.inl>

//==========================================================================
// Translation table from eCos exceptions to POSIX signals.

static const struct
{
    cyg_code    exception;
    int         signal;
} exception_signal_mapping[] =
{
#ifdef CYGNUM_HAL_EXCEPTION_DATA_ACCESS
    {CYGNUM_HAL_EXCEPTION_DATA_ACCESS, SIGBUS},
#endif
#ifdef CYGNUM_HAL_EXCEPTION_DATA_WRITE
    {CYGNUM_HAL_EXCEPTION_DATA_WRITE, SIGBUS},
#endif
#ifdef CYGNUM_HAL_EXCEPTION_CODE_ACCESS
    {CYGNUM_HAL_EXCEPTION_CODE_ACCESS, SIGBUS},
#endif    
#ifdef CYGNUM_HAL_EXCEPTION_CODE_WRITE
    {CYGNUM_HAL_EXCEPTION_CODE_WRITE, SIGBUS},
#endif    
#ifdef CYGNUM_HAL_EXCEPTION_CODE_EXECUTE
    {CYGNUM_HAL_EXCEPTION_CODE_EXECUTE, SIGBUS},
#endif    
#ifdef CYGNUM_HAL_EXCEPTION_IO_ACCESS
    {CYGNUM_HAL_EXCEPTION_IO_ACCESS, SIGBUS},
#endif    
#ifdef CYGNUM_HAL_EXCEPTION_IO_WRITE
    {CYGNUM_HAL_EXCEPTION_IO_ACCESS, SIGBUS},
#endif    
#ifdef CYGNUM_HAL_EXCEPTION_DATA_TLBMISS_ACCESS
    {CYGNUM_HAL_EXCEPTION_DATA_TLBMISS_ACCESS, SIGSEGV},
#endif    
#ifdef CYGNUM_HAL_EXCEPTION_DATA_TLBMISS_WRITE
    {CYGNUM_HAL_EXCEPTION_DATA_TLBMISS_WRITE, SIGSEGV},
#endif    
#ifdef CYGNUM_HAL_EXCEPTION_CODE_TLBMISS_ACCESS
    {CYGNUM_HAL_EXCEPTION_CODE_TLBMISS_ACCESS, SIGSEGV},
#endif    
#ifdef CYGNUM_HAL_EXCEPTION_CODE_TLBMISS_WRITE
    {CYGNUM_HAL_EXCEPTION_CODE_TLBMISS_WRITE, SIGSEGV},
#endif    
#ifdef CYGNUM_HAL_EXCEPTION_DATA_TLBERROR_ACCESS
    {CYGNUM_HAL_EXCEPTION_DATA_TLBERROR_ACCESS, SIGSEGV},
#endif    
#ifdef CYGNUM_HAL_EXCEPTION_DATA_TLBERROR_WRITE
    {CYGNUM_HAL_EXCEPTION_DATA_TLBERROR_WRITE, SIGSEGV},
#endif    
#ifdef CYGNUM_HAL_EXCEPTION_CODE_TLBERROR_ACCESS
    {CYGNUM_HAL_EXCEPTION_CODE_TLBERROR_ACCESS, SIGSEGV},
#endif    
#ifdef CYGNUM_HAL_EXCEPTION_CODE_TLBERROR_WRITE
    {CYGNUM_HAL_EXCEPTION_CODE_TLBERROR_WRITE, SIGSEGV},
#endif    
#ifdef CYGNUM_HAL_EXCEPTION_DATA_UNALIGNED_ACCESS
    {CYGNUM_HAL_EXCEPTION_DATA_UNALIGNED_ACCESS, SIGBUS},
#endif    
#ifdef CYGNUM_HAL_EXCEPTION_DATA_UNALIGNED_WRITE
    {CYGNUM_HAL_EXCEPTION_DATA_UNALIGNED_WRITE, SIGBUS},
#endif    
#ifdef CYGNUM_HAL_EXCEPTION_IO_UNALIGNED_ACCESS
    {CYGNUM_HAL_EXCEPTION_IO_UNALIGNED_ACCESS, SIGBUS},
#endif    
#ifdef CYGNUM_HAL_EXCEPTION_IO_UNALIGNED_WRITE
    {CYGNUM_HAL_EXCEPTION_IO_UNALIGNED_WRITE, SIGBUS},
#endif    
#ifdef CYGNUM_HAL_EXCEPTION_ILLEGAL_INSTRUCTION
    {CYGNUM_HAL_EXCEPTION_ILLEGAL_INSTRUCTION, SIGILL},
#endif    
#ifdef CYGNUM_HAL_EXCEPTION_INTERRUPT
    {CYGNUM_HAL_EXCEPTION_INTERRUPT, SIGINT},
#endif    
#ifdef CYGNUM_HAL_EXCEPTION_TRAP
    {CYGNUM_HAL_EXCEPTION_TRAP, SIGTRAP},
#endif    
#ifdef CYGNUM_HAL_EXCEPTION_DIV_BY_ZERO
    {CYGNUM_HAL_EXCEPTION_DIV_BY_ZERO, SIGFPE},
#endif    
#ifdef CYGNUM_HAL_EXCEPTION_OVERFLOW
    {CYGNUM_HAL_EXCEPTION_OVERFLOW, SIGFPE},
#endif    
#ifdef CYGNUM_HAL_EXCEPTION_BOUNDS
    {CYGNUM_HAL_EXCEPTION_BOUNDS, SIGSEGV},
#endif
#ifdef CYGNUM_HAL_EXCEPTION_SINGLE_STEP
    {CYGNUM_HAL_EXCEPTION_SINGLE_STEP, SIGTRAP},
#endif
#ifdef CYGNUM_HAL_EXCEPTION_INSTRUCTION_BP
    {CYGNUM_HAL_EXCEPTION_INSTRUCTION_BP, SIGTRAP},
#endif
#ifdef CYGNUM_HAL_EXCEPTION_PERIPHERAL_BP
    {CYGNUM_HAL_EXCEPTION_PERIPHERAL_BP, SIGTRAP},
#endif
#ifdef CYGNUM_HAL_EXCEPTION_DATA_BP
    {CYGNUM_HAL_EXCEPTION_DATA_BP, SIGTRAP},
#endif
#ifdef CYGNUM_HAL_EXCEPTION_DEVELOPMENT_BP
    {CYGNUM_HAL_EXCEPTION_DEVELOPMENT_BP, SIGTRAP},
#endif
#ifdef CYGNUM_HAL_EXCEPTION_STACK_OVERFLOW
    {CYGNUM_HAL_EXCEPTION_STACK_OVERFLOW, SIGSEGV},
#endif
#ifdef CYGNUM_HAL_EXCEPTION_STACK_FAULT
    {CYGNUM_HAL_EXCEPTION_STACK_FAULT, SIGSEGV},
#endif
#ifdef CYGNUM_HAL_EXCEPTION_PARITY
    {CYGNUM_HAL_EXCEPTION_PARITY, SIGBUS},
#endif
#ifdef CYGNUM_HAL_EXCEPTION_FPU
    {CYGNUM_HAL_EXCEPTION_FPU, SIGFPE},
#endif
#ifdef CYGNUM_HAL_EXCEPTION_FPU_NOT_AVAIL
    {CYGNUM_HAL_EXCEPTION_FPU_NOT_AVAIL, SIGFPE},
#endif
#ifdef CYGNUM_HAL_EXCEPTION_FPU_OVERFLOW
    {CYGNUM_HAL_EXCEPTION_FPU_OVERFLOW, SIGFPE},
#endif
#ifdef CYGNUM_HAL_EXCEPTION_FPU_UNDERFLOW
    {CYGNUM_HAL_EXCEPTION_FPU_UNDERFLOW, SIGFPE},
#endif
#ifdef CYGNUM_HAL_EXCEPTION_FPU_DIV_BY_ZERO
    {CYGNUM_HAL_EXCEPTION_FPU_DIV_BY_ZERO, SIGFPE},
#endif
#ifdef CYGNUM_HAL_EXCEPTION_SYSTEM_CALL
    {CYGNUM_HAL_EXCEPTION_SYSTEM_CALL, SIGSYS},
#endif
    {0, 0} // dummy value to ensure compiler is happy
};

//==========================================================================
// POSIX exception handler

static void cyg_posix_exception_handler(
    CYG_ADDRWORD        data,                   // user supplied data == signal number
    cyg_code            exception_number,       // exception being raised
    CYG_ADDRWORD        exception_info          // any exception specific info
    )
{
    int signo = 0;

    pthread_info *self = pthread_self_info();

    if( self == NULL )
    {
        // Not a POSIX thread, just return
        return;
    }
    
#ifdef CYGSEM_KERNEL_EXCEPTIONS_DECODE

    signo = data;

#else

    for( int i = 0; exception_signal_mapping[i].signal != 0; i++ )    
    {
        if( exception_signal_mapping[i].exception == exception_number )
        {
            signo = exception_signal_mapping[i].signal;
            break;
        }
    }

#endif

    if( sigismember( &self->sigmask, signo ) )
    {
        // The signal is masked in the current thread. POSIX says that
        // the behaviour is undefined here. We choose to ignore it.

        return;
    }

    // The kernel exception handler may have disabled interrupts, so
    // we (re-)enable them here. From this point on we are running in
    // a context that is effectively just pushed onto the stack of the
    // current thread. If we return we will unwind and resume
    // execution from the excepting code. We can also, in theory,
    // longjump out of the signal handler, and although that is
    // deprecated, we make sure in cyg_deliver_signals() that it is
    // possible to do it.
    
    HAL_ENABLE_INTERRUPTS();
    
    struct sigevent sev;

    sev.sigev_notify           = SIGEV_SIGNAL;
    sev.sigev_signo            = signo;
    sev.sigev_value.sival_ptr  = (void *)exception_info;

    // Generate the signal
    cyg_sigqueue( &sev, SI_EXCEPT );

    // And try to deliver it
    cyg_deliver_signals();
}

//==========================================================================
// Install all the exception handlers

static void install_handlers( Cyg_Thread *thread)
{
#ifdef CYGSEM_KERNEL_EXCEPTIONS_DECODE

    // With decoded exceptions, we must install a separate exception
    // handler for each supported exception.

    for( int i = 0; exception_signal_mapping[i].signal != 0; i++ )
    {
        thread->register_exception( exception_signal_mapping[i].exception,
                                    cyg_posix_exception_handler,
                                    exception_signal_mapping[i].signal,,
                                    NULL,
                                    NULL);
    }
    
#else

    // Otherwise there is just one exception handler for all exceptions.
    
    thread->register_exception( CYGNUM_HAL_EXCEPTION_MIN,
                                cyg_posix_exception_handler,
                                0,
                                NULL,
                                NULL);
    
#endif    
    
}

//==========================================================================
// Initialization

externC void cyg_posix_exception_start()
{
#ifdef CYGSEM_KERNEL_EXCEPTIONS_GLOBAL

    // With global exceptions, we only need to install a single static
    // set of exception handlers. Note that by this point in system
    // initialization the idle thread should be installed as the
    // current thread, so we pass a pointer to that to
    // install_handlers(). The identity of the thread passed is
    // actually irrelevant in this case and is just used as a handle
    // into the thread class.

    install_handlers( Cyg_Thread::self() );
    
#endif    
}

//==========================================================================
// Per thread exception initialization and destruction

externC void cyg_pthread_exception_init(pthread_info *thread)
{
#ifndef CYGSEM_KERNEL_EXCEPTIONS_GLOBAL

    // With non-global exceptions we must install a new set of handlers
    // for each thread.

    install_handlers( thread->thread );
    
#endif
}

externC void cyg_pthread_exception_destroy(pthread_info *thread)
{
    // Nothing to do at present.
}

// -------------------------------------------------------------------------
// EOF except.cxx
