/****************************************************************************
*
*                            Open Watcom Project
*
*    Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
*
*  ========================================================================
*
*    This file contains Original Code and/or Modifications of Original
*    Code as defined in and that are subject to the Sybase Open Watcom
*    Public License version 1.0 (the 'License'). You may not use this file
*    except in compliance with the License. BY USING THIS FILE YOU AGREE TO
*    ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
*    provided with the Original Code and Modifications, and is also
*    available at www.sybase.com/developer/opensource.
*
*    The Original Code and all software distributed under the License are
*    distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
*    EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
*    ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
*    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
*    NON-INFRINGEMENT. Please see the License for the specific language
*    governing rights and limitations under the License.
*
*  ========================================================================
*
* Description:  Linux debugger trap file MIPS specific functions.
*
****************************************************************************/


#include <stddef.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include "trpimp.h"
#include "trperr.h"
#include "mad.h"
#include "madregs.h"
#include "exeelf.h"
#include "linuxcomm.h"

/* Implementation notes:
 *
 * - This code is little endian, for 32-bit MIPS systems. It could be extended
 *   to support 64-bit machines relatively easily, as well as big endian
 *   systems.
 *
 */

/* Macros to get at GP/FP registers based on their index; useful in loops */
#define TRANS_GPREG_32( mr, idx ) (*((unsigned_32 *)(&(mr->r0.u._32[I64LO32])) + (2 * idx)))
#define TRANS_FPREG_LO( mr, idx ) (*((unsigned_32 *)(&(mr->f0.u64.u._32[I64LO32])) + (2 * idx)))
#define TRANS_FPREG_HI( mr, idx ) (*((unsigned_32 *)(&(mr->f0.u64.u._32[I64HI32])) + (2 * idx)))

static void ReadCPU( struct mips_mad_registers *r )
{
    int         i;

    memset( r, 0, sizeof( *r ) );
    /* Read GPRs */
    for( i = 0; i < 32; ++i ) {
        TRANS_GPREG_32( r, i ) = ptrace( PTRACE_PEEKUSER, pid, (void *)i, NULL );
    }
    /* Read FPRs */
    for( i = 0; i < 16; ++i ) {
        TRANS_FPREG_LO( r, i ) = ptrace( PTRACE_PEEKUSER, pid, (void *)(FPR_BASE + i * 2), NULL );
        TRANS_FPREG_HI( r, i ) = ptrace( PTRACE_PEEKUSER, pid, (void *)(FPR_BASE + i * 2 + 1), NULL );
    }
    /* Read special registers */
    r->pc.u._32[I64LO32]  = ptrace( PTRACE_PEEKUSER, pid, (void *)PC, NULL );
    r->lo                 = ptrace( PTRACE_PEEKUSER, pid, (void *)MMLO, NULL );
    r->hi                 = ptrace( PTRACE_PEEKUSER, pid, (void *)MMHI, NULL );
    r->fpcsr              = ptrace( PTRACE_PEEKUSER, pid, (void *)FPC_CSR, NULL );
    r->fpivr              = ptrace( PTRACE_PEEKUSER, pid, (void *)FPC_EIR, NULL );

    last_eip = r->pc.u._32[I64LO32];
}

unsigned ReqRead_cpu( void )
{
    return( 0 );
}

unsigned ReqRead_fpu( void )
{
    return( 0 );
}

unsigned ReqRead_regs( void )
{
    mad_registers   *mr;

    mr = GetOutPtr( 0 );
    ReadCPU( &mr->mips );
    return( sizeof( mr->mips ) );
}

static void WriteCPU( struct mips_mad_registers *r )
{
    int         i;

    /* Write special registers */
    ptrace( PTRACE_POKEUSER, pid, (void *)PC, (void *)(r->pc.u._32[I64LO32]) );
    ptrace( PTRACE_POKEUSER, pid, (void *)MMLO, (void *)r->lo );
    ptrace( PTRACE_POKEUSER, pid, (void *)MMHI, (void *)r->hi );
    /* Write GPRs */
    for( i = 0; i < 32; ++i ) {
        ptrace( PTRACE_POKEUSER, pid, (void *)i, (void *)TRANS_GPREG_32( r, i ) );
    }
    /* Write FPRs */
    for( i = 0; i < 16; ++i ) {
        ptrace( PTRACE_POKEUSER, pid, (void *)(FPR_BASE + i * 2), (void *)TRANS_FPREG_LO( r, i ) );
        ptrace( PTRACE_POKEUSER, pid, (void *)(FPR_BASE + i * 2 + 1), (void *)TRANS_FPREG_HI( r, i ) );
    }
}

unsigned ReqWrite_cpu( void )
{
    return( 0 );
}

unsigned ReqWrite_fpu()
{
    return( 0 );
}

unsigned ReqWrite_regs( void )
{
    mad_registers   *mr;

    mr = GetInPtr( sizeof( write_regs_req ) );
    WriteCPU( &mr->mips );
    return( 0 );
}

unsigned ReqSet_watch( void )
{
    set_watch_req   *acc;
    set_watch_ret   *ret;

    acc = GetInPtr( 0 );
    ret = GetOutPtr( 0 );
    ret->multiplier = 100000;
    ret->err = 1;
    return( sizeof( *ret ) );
}

unsigned ReqClear_watch( void )
{
    clear_watch_req *acc;

    acc = GetInPtr( 0 );
    return( 0 );
}

/* We do not support I/O port access on MIPS, although if we really
 * wanted to, we could access memory mapped ISA/PCI I/O ports on systems
 * where those are provided. Would require root privileges.
 */
unsigned ReqRead_io( void )
{
    read_io_req *acc;
    void        *ret;

    acc = GetInPtr( 0 );
    ret = GetOutPtr( 0 );
    return( 0 );
}

unsigned ReqWrite_io( void )
{
    write_io_req    *acc;
    write_io_ret    *ret;
    void            *data;

    acc = GetInPtr( 0 );
    data = GetInPtr( sizeof( *acc ) );
    ret = GetOutPtr( 0 );
    ret->len = 0;
    return( sizeof( *ret ) );
}

unsigned ReqGet_sys_config( void )
{
    get_sys_config_ret  *ret;

    ret = GetOutPtr( 0 );
    ret->sys.os = OS_LINUX;

    // TODO: Detect OS version (kernel version?)!
    ret->sys.osmajor = 1;
    ret->sys.osminor = 0;

    ret->sys.cpu = MIPS_R3000;
    ret->sys.fpu = 0;
    ret->sys.huge_shift = 3;
    ret->sys.mad = MAD_MIPS;
    CONV_LE_16( ret->sys.mad );
    return( sizeof( *ret ) );
}

unsigned ReqMachine_data( void )
{
    machine_data_req    *acc;
    machine_data_ret    *ret;
    unsigned_8          *data;

    acc = GetInPtr( 0 );
    ret = GetOutPtr( 0 );
    data = GetOutPtr( sizeof( *ret ) );
    ret->cache_start = 0;
    ret->cache_end = ~(addr_off)0;
    *data = 0;
    return( sizeof( *ret ) + sizeof( *data ) );
}

const char *const ExceptionMsgs[33] = {
    "",
    TRP_QNX_hangup,
    TRP_QNX_user_interrupt,
    TRP_QNX_quit,
    TRP_EXC_illegal_instruction,
    TRP_QNX_trap,
    TRP_QNX_abort,
    TRP_EXC_access_violation "(SIGEMT)",
    TRP_QNX_floating_point_error,
    TRP_QNX_process_killed,
    TRP_QNX_bus_error,
    TRP_EXC_access_violation "(SIGSEGV)",
    TRP_QNX_sys,
    TRP_QNX_broken_pipe,
    TRP_QNX_alarm,
    TRP_QNX_process_termination,
    TRP_QNX_user_signal_1,
    TRP_QNX_user_signal_2,
    TRP_QNX_child_stopped,
    TRP_QNX_power_fail,
    TRP_QNX_winch,
    TRP_QNX_urgent,
    TRP_QNX_poll,
    TRP_QNX_process_stopped,
    "", /* SIGTSTP */
    TRP_QNX_process_continued,
    "", /* SIGTTIN */
    "", /* SIGTTOU */
    "", /* SIGVTALRM */
    "", /* SIGPROF */
    "", /* SIGXCPU */
    "", /* SIGXFSZ */
    ""
};
