#define  GDB_STUB_ENABLE_THREAD_SUPPORT 1
/*******************************************************************************

                     THIS SOFTWARE IS NOT COPYRIGHTED

    The following software is offered for use in the public domain.
    There is no warranty with regard to this software or its performance
    and the user must accept the software "AS IS" with all faults.

    THE CONTRIBUTORS DISCLAIM ANY WARRANTIES, EXPRESS OR IMPLIED, WITH
    REGARD TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

********************************************************************************
*
*   r46kstub.c -- target debugging stub for the IDT R4600 Orion processor
*
*   This module is based on the stub for the Hitachi SH processor written by
*   Ben Lee and Steve Chamberlain and supplied with gdb 4.16.  The latter
*   in turn "is originally based on an m68k software stub written by Glenn
*   Engel at HP, but has changed quite a bit."  The changes for the R4600
*   were written by C. M. Heard at VVNET.  They were based in part on the
*   Algorithmics R4000 version of Phil Bunce's PMON program.
*
*  Remote communication protocol:
*
*  A debug packet whose contents are <data>
*  is encapsulated for transmission in the form:
*
*       $ <data> # CSUM1 CSUM2
*
*       <data> must be ASCII alphanumeric and cannot include characters
*       '$' or '#'.  If <data> starts with two characters followed by
*       ':', then the existing stubs interpret this as a sequence number.
*
*       CSUM1 and CSUM2 are ascii hex representation of an 8-bit
*       checksum of <data>, the most significant nibble is sent first.
*       the hex digits 0-9,a-f are used.
*
*  Receiver responds with:
*
*       +    if CSUM is correct
*       -    if CSUM is incorrect
*
*  <data> is as follows.  All values are encoded in ascii hex digits.
*
*       Request         Packet
*
*       read registers  g
*       reply           XX....X         Each byte of register data
*                                       is described by two hex digits.
*                                       Registers are in the internal order
*                                       for GDB, and the bytes in a register
*                                       are in the same order the machine uses.
*                       or ENN          for an error.
*
*       write regs      GXX..XX         Each byte of register data
*                                       is described by two hex digits.
*       reply           OK              for success
*                       ENN             for an error
*
*       write reg       Pn...=r...      Write register n... with value r....
*       reply           OK              for success
*                       ENN             for an error
*
*       read mem        mAA..AA,LLLL    AA..AA is address, LLLL is length.
*       reply           XX..XX          XX..XX is mem contents
*                                       Can be fewer bytes than requested
*                                       if able to read only part of the data.
*                       or ENN          NN is errno
*
*       write mem       MAA..AA,LLLL:XX..XX
*                                       AA..AA is address,
*                                       LLLL is number of bytes,
*                                       XX..XX is data
*       reply           OK              for success
*                       ENN             for an error (this includes the case
*                                       where only part of the data was
*                                       written).
*
*       cont            cAA..AA         AA..AA is address to resume
*                                       If AA..AA is omitted,
*                                       resume at same address.
*
*       step            sAA..AA         AA..AA is address to resume
*                                       If AA..AA is omitted,
*                                       resume at same address.
*
*       There is no immediate reply to step or cont.
*       The reply comes when the machine stops.
*       It is           SAA             AA is the "signal number"
*
*       last signal     ?               Reply with the reason for stopping.
*                                       This is the same reply as is generated
*                                       for step or cont: SAA where AA is the
*                                       signal number.
*
*       detach          D               Host is detaching.  Reply OK and
*                                       end remote debugging session.
*
*       reserved        <other>         On other requests, the stub should
*                                       ignore the request and send an empty
*                                       response ($#<checksum>).  This way
*                                       we can extend the protocol and GDB
*                                       can tell whether the stub it is
*                                       talking to uses the old or the new.
*
*       Responses can be run-length encoded to save space.  A '*' means that
*       the next character is an ASCII encoding giving a repeat count which
*       stands for that many repetitions of the character preceding the '*'.
*       The encoding is n+29, yielding a printable character when n >=3
*       (which is where rle starts to win).  Don't use n > 99 since gdb
*       masks each character is receives with 0x7f in order to strip off
*       the parity bit.
*
*       As an example, "0* " means the same thing as "0000".
*
*******************************************************************************/


#include <string.h>
#include <signal.h>
#include "mips_opcode.h"
/* #include "memlimits.h" */
#include <rtems.h>
#include <rtems/bspIo.h>
#include "gdb_if.h"

/* Change it to something meaningful when debugging */
#undef ASSERT
#define ASSERT(x) if(!(x)) printk("ASSERT: stub: %d\n", __LINE__)

/***************/
/* Exception Codes */
#define	EXC_INT		0		/* External interrupt */
#define	EXC_MOD		1		/* TLB modification exception */
#define	EXC_TLBL	2		/* TLB miss (Load or Ifetch) */
#define	EXC_TLBS	3		/* TLB miss (Store) */
#define	EXC_ADEL	4		/* Address error (Load or Ifetch) */
#define	EXC_ADES	5		/* Address error (Store) */
#define	EXC_IBE		6		/* Bus error (Ifetch) */
#define	EXC_DBE		7		/* Bus error (data load or store) */
#define	EXC_SYS		8		/* System call */
#define	EXC_BP		9		/* Break point */
#define	EXC_RI		10		/* Reserved instruction */
#define	EXC_CPU		11		/* Coprocessor unusable */
#define	EXC_OVF		12		/* Arithmetic overflow */
#define	EXC_TRAP	13		/* Trap exception */
#define	EXC_FPE		15		/* Floating Point Exception */

/* FPU Control/Status register fields */
#define	CSR_FS		0x01000000	/* Set to flush denormals to zero */
#define	CSR_C		0x00800000	/* Condition bit (set by FP compare) */

#define	CSR_CMASK	(0x3f<<12)
#define	CSR_CE		0x00020000
#define	CSR_CV		0x00010000
#define	CSR_CZ		0x00008000
#define	CSR_CO		0x00004000
#define	CSR_CU		0x00002000
#define	CSR_CI		0x00001000

#define	CSR_EMASK	(0x1f<<7)
#define	CSR_EV		0x00000800
#define	CSR_EZ		0x00000400
#define	CSR_EO		0x00000200
#define	CSR_EU		0x00000100
#define	CSR_EI		0x00000080

#define	CSR_FMASK	(0x1f<<2)
#define	CSR_FV		0x00000040
#define	CSR_FZ		0x00000020
#define	CSR_FO		0x00000010
#define	CSR_FU		0x00000008
#define	CSR_FI		0x00000004

#define	CSR_RMODE_MASK	(0x3<<0)
#define	CSR_RM		0x00000003
#define	CSR_RP		0x00000002
#define	CSR_RZ		0x00000001
#define	CSR_RN		0x00000000

/***************/

/*
 * Saved register information.  Must be prepared by the exception
 * preprocessor before handle_exception is invoked.
 */
#if (__mips == 3)
typedef long long mips_register_t;
#define R_SZ 8
#elif (__mips == 1)
typedef unsigned int mips_register_t;
#define R_SZ 4
#else
#error "unknown MIPS ISA"
#endif
static mips_register_t *registers;

#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT)
static char do_threads;      /* != 0 means we are supporting threads */
#endif

/*
 * The following external functions provide character input and output.
 */
extern char getDebugChar (void);
extern void putDebugChar (char);

/*
 * Exception handler
 */
void handle_exception (rtems_vector_number vector, CPU_Interrupt_frame *frame);

/*
 * Prototype needed by this code and to keep it self contained.
 */
void rtems_interrupt_catch( rtems_isr_entry, int, rtems_isr_entry *);

/*
 * The following definitions are used for the gdb stub memory map
 */
struct memseg
{
      unsigned begin, end, opts;
};

static int is_readable(unsigned,unsigned);
static int is_writeable(unsigned,unsigned);
static int is_steppable(unsigned);

/*
 * BUFMAX defines the maximum number of characters in the inbound & outbound
 * packet buffers.  At least 4+(sizeof registers)*2 bytes will be needed for
 * register packets.  Memory dump packets can profitably use even more.
 */
#define BUFMAX 1500

static char inBuffer[BUFMAX];
static char outBuffer[BUFMAX];

/* Structure to keep info on a z-breaks */
#define BREAKNUM 32

struct z0break
{
  /* List support */
  struct z0break *next;
  struct z0break *prev;

  /* Location, preserved data */

  /* the address pointer, really, really must be a pointer to
  ** a 32 bit quantity (likely 64 on the R4k), so the full instruction is read &
  ** written.  Making it a char * as on the i386 will cause
  ** the zbreaks to mess up the breakpoint instructions
  */
  unsigned char *address;
  unsigned instr;
};

static struct z0break z0break_arr[BREAKNUM];
static struct z0break *z0break_avail = NULL;
static struct z0break *z0break_list  = NULL;


/*
 * Convert an int to hex.
 */
const char gdb_hexchars[] = "0123456789abcdef";

#define highhex(x) gdb_hexchars [(x >> 4) & 0xf]
#define lowhex(x) gdb_hexchars [x & 0xf]

/*
 * Convert length bytes of data starting at addr into hex, placing the
 * result in buf.  Return a pointer to the last (null) char in buf.
 */
static char *
mem2hex (void *_addr, int length, char *buf)
{
  unsigned int addr = (unsigned int) _addr;

  if (((addr & 0x7) == 0) && ((length & 0x7) == 0))      /* dword aligned */
    {
      long long *source = (long long *) (addr);
      long long *limit  = (long long *) (addr + length);

      while (source < limit)
        {
          int i;
          long long k = *source++;

          for (i = 15; i >= 0; i--)
            *buf++ = gdb_hexchars [(k >> (i*4)) & 0xf];
        }
    }
  else if (((addr & 0x3) == 0) && ((length & 0x3) == 0)) /* word aligned */
    {
      int *source = (int *) (addr);
      int *limit  = (int *) (addr + length);

      while (source < limit)
        {
          int i;
          int k = *source++;

          for (i = 7; i >= 0; i--)
            *buf++ = gdb_hexchars [(k >> (i*4)) & 0xf];
        }
    }
  else if (((addr & 0x1) == 0) && ((length & 0x1) == 0)) /* halfword aligned */
    {
      short *source = (short *) (addr);
      short *limit  = (short *) (addr + length);

      while (source < limit)
        {
          int i;
          short k = *source++;

          for (i = 3; i >= 0; i--)
            *buf++ = gdb_hexchars [(k >> (i*4)) & 0xf];
        }
    }
  else                                                   /* byte aligned */
    {
      char *source = (char *) (addr);
      char *limit  = (char *) (addr + length);

      while (source < limit)
        {
          int i;
          char k = *source++;

          for (i = 1; i >= 0; i--)
            *buf++ = gdb_hexchars [(k >> (i*4)) & 0xf];
        }
    }

  *buf = '\0';
  return (buf);
}


/*
 * Convert a hex character to an int.
 */
static int
hex (char ch)
{
  if ((ch >= 'a') && (ch <= 'f'))
    return (ch - 'a' + 10);
  if ((ch >= '0') && (ch <= '9'))
    return (ch - '0');
  if ((ch >= 'A') && (ch <= 'F'))
    return (ch - 'A' + 10);
  return (-1);
}

/*
 * Convert a string from hex to int until a non-hex digit
 * is found.  Return the number of characters processed.
 */
static int
hexToInt (char **ptr, int *intValue)
{
  int numChars = 0;
  int hexValue;

  *intValue = 0;

  while (**ptr)
    {
      hexValue = hex (**ptr);
      if (hexValue >= 0)
        {
          *intValue = (*intValue << 4) | hexValue;
          numChars++;
        }
      else
        break;

      (*ptr)++;
    }

  return (numChars);
}

/*
 * Convert a string from hex to long long until a non-hex
 * digit is found.  Return the number of characters processed.
 */
static int
hexToLongLong (char **ptr, long long *intValue)
{
  int numChars = 0;
  int hexValue;

  *intValue = 0;

  while (**ptr)
    {
      hexValue = hex (**ptr);
      if (hexValue >= 0)
        {
          *intValue = (*intValue << 4) | hexValue;
          numChars++;
        }
      else
        break;

      (*ptr)++;
    }

  return (numChars);
}

/*
 * Convert the hex array buf into binary, placing the result at the
 * specified address.  If the conversion fails at any point (i.e.,
 * if fewer bytes are written than indicated by the size parameter)
 * then return 0;  otherwise return 1.
 */
static int
hex2mem (char *buf, void *_addr, int length)
{
  unsigned int addr = (unsigned int) _addr;
  if (((addr & 0x7) == 0) && ((length & 0x7) == 0))      /* dword aligned */
    {
      long long *target = (long long *) (addr);
      long long *limit  = (long long *) (addr + length);

      while (target < limit)
        {
          int i, j;
          long long k = 0;

          for (i = 0; i < 16; i++)
            if ((j = hex(*buf++)) < 0)
              return 0;
            else
              k = (k << 4) + j;
          *target++ = k;
        }
    }
  else if (((addr & 0x3) == 0) && ((length & 0x3) == 0)) /* word aligned */
    {
      int *target = (int *) (addr);
      int *limit  = (int *) (addr + length);

      while (target < limit)
        {
          int i, j;
          int k = 0;

          for (i = 0; i < 8; i++)
            if ((j = hex(*buf++)) < 0)
              return 0;
            else
              k = (k << 4) + j;
          *target++ = k;
        }
    }
  else if (((addr & 0x1) == 0) && ((length & 0x1) == 0)) /* halfword aligned */
    {
      short *target = (short *) (addr);
      short *limit  = (short *) (addr + length);

      while (target < limit)
        {
          int i, j;
          short k = 0;

          for (i = 0; i < 4; i++)
            if ((j = hex(*buf++)) < 0)
              return 0;
            else
              k = (k << 4) + j;
          *target++ = k;
        }
    }
  else                                                   /* byte aligned */
    {
      char *target = (char *) (addr);
      char *limit  = (char *) (addr + length);

      while (target < limit)
        {
          int i, j;
          char k = 0;

          for (i = 0; i < 2; i++)
            if ((j = hex(*buf++)) < 0)
              return 0;
            else
              k = (k << 4) + j;
          *target++ = k;
        }
    }

  return 1;
}

/* Convert the binary stream in BUF to memory.

   Gdb will escape $, #, and the escape char (0x7d).
   COUNT is the total number of bytes to write into
   memory. */
static unsigned char *
bin2mem (
  char *buf,
  unsigned char *mem,
  int   count
)
{
  int i;

  for (i = 0; i < count; i++) {
      /* Check for any escaped characters. Be paranoid and
         only unescape chars that should be escaped. */
      if (*buf == 0x7d) {
          switch (*(buf+1)) {
            case 0x3:  /* # */
            case 0x4:  /* $ */
            case 0x5d: /* escape char */
              buf++;
              *buf |= 0x20;
              break;
            default:
              /* nothing */
              break;
            }
        }

      *mem++ = *buf++;
    }

  return mem;
}


/*
 * Scan the input stream for a sequence for the form $<data>#<checksum>.
 */
static void
getpacket (char *buffer)
{
  unsigned char checksum;
  unsigned char xmitcsum;
  int i;
  int count;
  char ch;
  do
    {
      /* wait around for the start character, ignore all other characters */
      while ((ch = getDebugChar ()) != '$');
      checksum = 0;
      xmitcsum = -1;

      count = 0;

      /* now, read until a # or end of buffer is found */
      while ( (count < BUFMAX-1) && ((ch = getDebugChar ()) != '#') )
          checksum += (buffer[count++] = ch);

      /* make sure that the buffer is null-terminated */
      buffer[count] = '\0';

      if (ch == '#')
        {
          xmitcsum = hex (getDebugChar ()) << 4;
          xmitcsum += hex (getDebugChar ());
          if (checksum != xmitcsum)
            putDebugChar ('-'); /* failed checksum */
          else
            {
              putDebugChar ('+');       /* successful transfer */
              /* if a sequence char is present, reply the sequence ID */
              if (buffer[2] == ':')
                {
                  putDebugChar (buffer[0]);
                  putDebugChar (buffer[1]);
                  /* remove sequence chars from buffer */
                  for (i = 3; i <= count; i++)
                    buffer[i - 3] = buffer[i];
                }
            }
        }
    }
  while (checksum != xmitcsum);
}

/*
 * Get a positive/negative acknowledgment for a transmitted packet.
 */
static char
getAck (void)
{
  char c;

  do
    {
      c = getDebugChar ();
    }
  while ((c != '+') && (c != '-'));

  return c;
}

/*
 * Send the packet in buffer and wait for a positive acknowledgement.
 */
static void
putpacket (char *buffer)
{
  int checksum;

  /* $<packet info>#<checksum> */
  do
    {
      char *src = buffer;
      putDebugChar ('$');
      checksum = 0;

      while (*src != '\0')
        {
          int runlen = 0;

          /* Do run length encoding */
          while ((src[runlen] == src[0]) && (runlen < 99))
            runlen++;
          if (runlen > 3)
            {
              int encode;
              /* Got a useful amount */
              putDebugChar (*src);
              checksum += *src;
              putDebugChar ('*');
              checksum += '*';
              checksum += (encode = (runlen - 4) + ' ');
              putDebugChar (encode);
              src += runlen;
            }
          else
            {
              putDebugChar (*src);
              checksum += *src;
              src++;
             }
        }

      putDebugChar ('#');
      putDebugChar (highhex (checksum));
      putDebugChar (lowhex (checksum));
    }
  while  (getAck () != '+');
}


/*
 * Saved instruction data for single step support
 */
static struct
  {
    unsigned *targetAddr;
    unsigned savedInstr;
  }
instrBuffer;

/*
 * If a step breakpoint was planted restore the saved instruction.
 */
static void
undoSStep (void)
{
  if (instrBuffer.targetAddr != NULL)
    {
      *instrBuffer.targetAddr = instrBuffer.savedInstr;
      instrBuffer.targetAddr = NULL;
    }
  instrBuffer.savedInstr = NOP_INSTR;
}

/*
 * If a single step is requested put a temporary breakpoint at the instruction
 * which logically follows the next one to be executed.  If the next instruction
 * is a branch instruction then skip the instruction in the delay slot.  NOTE:
 * ERET instructions are NOT handled, as it is impossible to single-step through
 * the exit code in an exception handler.  In addition, no attempt is made to
 * do anything about BC0T and BC0F, since a condition bit for coprocessor 0
 * is not defined on the R4600.  Finally, BC2T and BC2F are ignored since there
 * is no coprocessor 2 on a 4600.
 */
static void
doSStep (void)
{
   InstFmt inst;

   instrBuffer.targetAddr = (unsigned *)(registers[PC]+4);    /* set default */

   inst.word = *(unsigned *)registers[PC];     /* read the next instruction  */

   switch (inst.RType.op) {                    /* override default if branch */
      case OP_SPECIAL:
         switch (inst.RType.func) {
            case OP_JR:
            case OP_JALR:
               instrBuffer.targetAddr =
                  (unsigned *)registers[inst.RType.rs];
               break;
         };
         break;

      case OP_REGIMM:
         switch (inst.IType.rt) {
            case OP_BLTZ:
            case OP_BLTZL:
            case OP_BLTZAL:
            case OP_BLTZALL:
               if (registers[inst.IType.rs] < 0 )
                  instrBuffer.targetAddr =
                     (unsigned *)(((signed short)inst.IType.imm<<2)
                                  + (registers[PC]+4));
               else
                  instrBuffer.targetAddr = (unsigned*)(registers[PC]+8);
               break;
            case OP_BGEZ:
            case OP_BGEZL:
            case OP_BGEZAL:
            case OP_BGEZALL:
               if (registers[inst.IType.rs] >= 0 )
                  instrBuffer.targetAddr =
                     (unsigned *)(((signed short)inst.IType.imm<<2)
                                  + (registers[PC]+4));
               else
                  instrBuffer.targetAddr = (unsigned*)(registers[PC]+8);
               break;
         };
         break;

      case OP_J:
      case OP_JAL:
         instrBuffer.targetAddr =
            (unsigned *)((inst.JType.target<<2) + ((registers[PC]+4)&0xf0000000));
         break;

      case OP_BEQ:
      case OP_BEQL:
         if (registers[inst.IType.rs] == registers[inst.IType.rt])
            instrBuffer.targetAddr =
               (unsigned *)(((signed short)inst.IType.imm<<2) + (registers[PC]+4));
         else
            instrBuffer.targetAddr = (unsigned*)(registers[PC]+8);
         break;
      case OP_BNE:
      case OP_BNEL:
         if (registers[inst.IType.rs] != registers[inst.IType.rt])
            instrBuffer.targetAddr =
               (unsigned *)(((signed short)inst.IType.imm<<2) + (registers[PC]+4));
         else
            instrBuffer.targetAddr = (unsigned*)(registers[PC]+8);
         break;
      case OP_BLEZ:
      case OP_BLEZL:
         if (registers[inst.IType.rs] <= 0)
            instrBuffer.targetAddr =
               (unsigned *)(((signed short)inst.IType.imm<<2) + (registers[PC]+4));
         else
            instrBuffer.targetAddr = (unsigned*)(registers[PC]+8);
         break;
      case OP_BGTZ:
      case OP_BGTZL:
         if (registers[inst.IType.rs] > 0)
            instrBuffer.targetAddr =
               (unsigned *)(((signed short)inst.IType.imm<<2) + (registers[PC]+4));
         else
            instrBuffer.targetAddr = (unsigned*)(registers[PC]+8);
         break;

      case OP_COP1:
         if (inst.RType.rs == OP_BC)
            switch (inst.RType.rt) {
               case COPz_BCF:
               case COPz_BCFL:
                  if (registers[FCSR] & CSR_C)
                     instrBuffer.targetAddr = (unsigned*)(registers[PC]+8);
                  else
                     instrBuffer.targetAddr =
                        (unsigned *)(((signed short)inst.IType.imm<<2)
                                     + (registers[PC]+4));
                  break;
               case COPz_BCT:
               case COPz_BCTL:
                  if (registers[FCSR] & CSR_C)
                     instrBuffer.targetAddr =
                        (unsigned *)(((signed short)inst.IType.imm<<2)
                                     + (registers[PC]+4));
                  else
                     instrBuffer.targetAddr = (unsigned*)(registers[PC]+8);
                  break;
            };
         break;
   }

   if( is_steppable((unsigned)instrBuffer.targetAddr) && *(instrBuffer.targetAddr) != BREAK_INSTR )
   {
      instrBuffer.savedInstr = *instrBuffer.targetAddr;
      *instrBuffer.targetAddr = BREAK_INSTR;
   }
   else
   {
      instrBuffer.targetAddr = NULL;
      instrBuffer.savedInstr = NOP_INSTR;
   }
   return;
}


/*
 * Translate the R4600 exception code into a Unix-compatible signal.
 */
static int
computeSignal (void)
{
  int exceptionCode = (registers[CAUSE] & CAUSE_EXCMASK) >> CAUSE_EXCSHIFT;

  switch (exceptionCode)
    {
    case EXC_INT:
      /* External interrupt */
      return SIGINT;

    case EXC_RI:
      /* Reserved instruction */
    case EXC_CPU:
      /* Coprocessor unusable */
      return SIGILL;

    case EXC_BP:
      /* Break point */
      return SIGTRAP;

    case EXC_OVF:
      /* Arithmetic overflow */
    case EXC_TRAP:
      /* Trap exception */
    case EXC_FPE:
      /* Floating Point Exception */
      return SIGFPE;

    case EXC_IBE:
      /* Bus error (Ifetch) */
    case EXC_DBE:
      /* Bus error (data load or store) */
      return SIGBUS;

    case EXC_MOD:
      /* TLB modification exception */
    case EXC_TLBL:
      /* TLB miss (Load or Ifetch) */
    case EXC_TLBS:
      /* TLB miss (Store) */
    case EXC_ADEL:
      /* Address error (Load or Ifetch) */
    case EXC_ADES:
      /* Address error (Store) */
      return SIGSEGV;

    case EXC_SYS:
      /* System call */
      return SIGSYS;

    default:
      return SIGTERM;
    }
}

/*
 *  This support function prepares and sends the message containing the
 *  basic information about this exception.
 */
static void gdb_stub_report_exception_info(
  rtems_vector_number vector,
  CPU_Interrupt_frame *frame,
  int                  thread
)
{
   char *optr;
   int sigval;

   optr = outBuffer;
   *optr++ = 'T';
   sigval = computeSignal ();
   *optr++ = highhex (sigval);
   *optr++ = lowhex (sigval);

   *optr++ = highhex(SP); /*gdb_hexchars[SP]; */
   *optr++ = lowhex(SP);
   *optr++ = ':';
   optr    = mem2hstr(optr, (unsigned char *)&frame->sp, R_SZ );
   *optr++ = ';';

   *optr++ = highhex(PC); /*gdb_hexchars[PC]; */
   *optr++ = lowhex(PC);
   *optr++ = ':';
   optr    = mem2hstr(optr, (unsigned char *)&frame->epc, R_SZ );
   *optr++ = ';';

#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT)
   if (do_threads)
   {
      *optr++ = 't';
      *optr++ = 'h';
      *optr++ = 'r';
      *optr++ = 'e';
      *optr++ = 'a';
      *optr++ = 'd';
      *optr++ = ':';
      optr   = thread2vhstr(optr, thread);
      *optr++ = ';';
   }
#endif
   *optr++ = '\0';
}



/*
 * Scratch frame used to retrieve contexts for different threads, so as
 * not to disrupt our current context on the stack
 */
CPU_Interrupt_frame current_thread_registers;

/*
 * This function handles all exceptions.  It only does two things:
 * it figures out why it was activated and tells gdb, and then it
 * reacts to gdb's requests.
 */

extern void clear_cache(void);
void handle_exception (rtems_vector_number vector, CPU_Interrupt_frame *frame)
{
   int          host_has_detached = 0;
   int          regno, addr, length;
   char         *ptr;
   int          current_thread;  /* current generic thread */
   int          thread;          /* stopped thread: context exception happened in */

   long long    regval;
   void         *regptr;
   int          binary;

   registers = (mips_register_t *)frame;

   thread = 0;
#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT)
   if (do_threads) {
      thread = rtems_gdb_stub_get_current_thread();
   }
#endif
   current_thread = thread;

   {
      /* reapply all breakpoints regardless of how we came in */
      struct z0break *z0, *zother;

      for (zother=z0break_list; zother!=NULL; zother=zother->next)
      {
         if( zother->instr == 0xffffffff )
         {
            /* grab the instruction */
            zother->instr = *(zother->address);
            /* and insert the breakpoint */
            *(zother->address) = BREAK_INSTR;
         }
      }

      /* see if we're coming from a breakpoint */
      if( *((unsigned *)frame->epc) == BREAK_INSTR )
      {
         /* see if its one of our zbreaks */
         for (z0=z0break_list; z0!=NULL; z0=z0->next)
         {
            if( (unsigned)z0->address == frame->epc)
               break;
         }
         if( z0 )
         {
            /* restore the original instruction */
            *(z0->address) = z0->instr;
            /* flag the breakpoint */
            z0->instr = 0xffffffff;

            /*
               now when we return, we'll execute the original code in
               the original state.  This leaves our breakpoint inactive
               since the break instruction isn't there, but we'll reapply
               it the next time we come in via step or breakpoint
            */
         }
         else
         {
            /* not a zbreak, see if its our trusty stepping code */

            /*
             * Restore the saved instruction at
             * the single-step target address.
             */
            undoSStep();
         }
      }
   }

   /* reply to host that an exception has occurred with some basic info */
   gdb_stub_report_exception_info(vector, frame, thread);
   putpacket (outBuffer);

   while (!(host_has_detached)) {
      outBuffer[0] = '\0';
      getpacket (inBuffer);
      binary = 0;

      switch (inBuffer[0]) {
         case '?':
            gdb_stub_report_exception_info(vector, frame, thread);
            break;

         case 'd': /* toggle debug flag */
            /* can print ill-formed commands in valid packets & checksum errors */
            break;

         case 'D':
            /* remote system is detaching - return OK and exit from debugger */
            strcpy (outBuffer, "OK");
            host_has_detached = 1;
            break;

         case 'g':               /* return the values of the CPU registers */
            regptr = registers;
#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT)
            if (do_threads && current_thread != thread )
               regptr = &current_thread_registers;
#endif
            mem2hex (regptr, NUM_REGS * (sizeof registers), outBuffer);
            break;

         case 'G':       /* set the values of the CPU registers - return OK */
            regptr = registers;
#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT)
            if (do_threads && current_thread != thread )
               regptr = &current_thread_registers;
#endif
            if (hex2mem (&inBuffer[1], regptr, NUM_REGS * (sizeof registers)))
               strcpy (outBuffer, "OK");
            else
               strcpy (outBuffer, "E00"); /* E00 = bad "set register" command */
            break;

         case 'P':
            /* Pn...=r...  Write register n... with value r... - return OK */
            ptr = &inBuffer[1];
            if (hexToInt(&ptr, &regno) &&
                *ptr++ == '=' &&
                hexToLongLong(&ptr, &regval))
            {
               registers[regno] = regval;
               strcpy (outBuffer, "OK");
            }
            else
               strcpy (outBuffer, "E00"); /* E00 = bad "set register" command */
            break;

         case 'm':
            /* mAA..AA,LLLL  Read LLLL bytes at address AA..AA */
            ptr = &inBuffer[1];
            if (hexToInt (&ptr, &addr)
                && *ptr++ == ','
                && hexToInt (&ptr, &length)
                && is_readable (addr, length)
                && (length < (BUFMAX - 4)/2))
               mem2hex ((void *)addr, length, outBuffer);
            else
               strcpy (outBuffer, "E01"); /* E01 = bad 'm' command */
            break;

         case 'X':  /* XAA..AA,LLLL:<binary data>#cs */
            binary = 1;
         case 'M':
            /* MAA..AA,LLLL:  Write LLLL bytes at address AA..AA - return OK */
            ptr = &inBuffer[1];
            if (hexToInt (&ptr, &addr)
                && *ptr++ == ','
                && hexToInt (&ptr, &length)
                && *ptr++ == ':'
                && is_writeable (addr, length) ) {
               if ( binary )
                  hex2mem (ptr, (void *)addr, length);
               else
                  bin2mem (ptr, (void *)addr, length);
               strcpy (outBuffer, "OK");
            }
            else
               strcpy (outBuffer, "E02"); /* E02 = bad 'M' command */
            break;

         case 'c':
            /* cAA..AA    Continue at address AA..AA(optional) */
         case 's':
            /* sAA..AA    Step one instruction from AA..AA(optional) */
         {
            /* try to read optional parameter, pc unchanged if no parm */
            ptr = &inBuffer[1];
            if (hexToInt (&ptr, &addr))
               registers[PC] = addr;

            if (inBuffer[0] == 's')
               doSStep ();
         }
         goto stubexit;

         case 'k':  /* remove all zbreaks if any */
        dumpzbreaks:
         {
            {
               /* Unlink the entire list */
               struct z0break *z0, *znxt;

               while( (z0= z0break_list) )
               {

                  /* put back the instruction */
                  if( z0->instr != 0xffffffff )
                     *(z0->address) = z0->instr;

                  /* pop off the top entry */
                  znxt = z0->next;
                  if( znxt ) znxt->prev = NULL;
                  z0break_list = znxt;

                  /* and put it on the free list */
                  z0->prev = NULL;
                  z0->next = z0break_avail;
                  z0break_avail = z0;
               }
            }

            strcpy(outBuffer, "OK");
         }
         break;

         case 'q':   /* queries */
#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT)
            rtems_gdb_process_query( inBuffer, outBuffer, do_threads, thread );
#endif
            break;

#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT)
         case 'T':
         {
            int testThread;

            if( vhstr2thread(&inBuffer[1], &testThread) == NULL )
            {
               strcpy(outBuffer, "E01");
               break;
            }

            if( rtems_gdb_index_to_stub_id(testThread) == NULL )
            {
               strcpy(outBuffer, "E02");
            }
            else
            {
               strcpy(outBuffer, "OK");
            }
         }
         break;
#endif

         case 'H':  /* set new thread */
#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT)
            if (inBuffer[1] != 'g') {
               break;
            }

            if (!do_threads) {
               break;
            }

            {
               int tmp, ret;

               /* Set new generic thread */
               if (vhstr2thread(&inBuffer[2], &tmp) == NULL) {
                  strcpy(outBuffer, "E01");
                  break;
               }

               /* 0 means `thread' */
               if (tmp == 0) {
                  tmp = thread;
               }

               if (tmp == current_thread) {
                  /* No changes */
                  strcpy(outBuffer, "OK");
                  break;
               }

               /* Save current thread registers if necessary */
               if (current_thread != thread) {
                  ret = rtems_gdb_stub_set_thread_regs(
                     current_thread,
                     (unsigned int *) (void *)&current_thread_registers);
                  ASSERT(ret);
               }

               /* Read new registers if necessary */
               if (tmp != thread) {
                  ret = rtems_gdb_stub_get_thread_regs(
                     tmp, (unsigned int *) (void *)&current_thread_registers);

                  if (!ret) {
                     /* Thread does not exist */
                     strcpy(outBuffer, "E02");
                     break;
                  }
               }

               current_thread = tmp;
               strcpy(outBuffer, "OK");
            }
#endif
            break;

         case 'Z':  /* Add breakpoint */
         {
            int ret, type, len;
            unsigned char *address;
            struct z0break *z0;

            ret = parse_zbreak(inBuffer, &type, &address, &len);
            if (!ret) {
               strcpy(outBuffer, "E01");
               break;
            }

            if (type != 0) {
               /* We support only software break points so far */
               strcpy(outBuffer, "E02");
               break;
            }

            if (len != R_SZ) {     /* was 1 */
               strcpy(outBuffer, "E03");
               break;
            }

            /* Let us check whether this break point already set */
            for (z0=z0break_list; z0!=NULL; z0=z0->next) {
               if (z0->address == address) {
                  break;
               }
            }

            if (z0 != NULL) {
               /* we already have a breakpoint for this address */
               strcpy(outBuffer, "E04");
               break;
            }

            /* Let us allocate new break point */
            if (z0break_avail == NULL) {
               strcpy(outBuffer, "E05");
               break;
            }

            /* Get entry */
            z0 = z0break_avail;
            z0break_avail = z0break_avail->next;

            /* Let us copy memory from address add stuff the break point in */
            /*
            *if (mem2hstr(z0->buf, address, 1) == NULL ||
              !hstr2mem(address, "cc" , 1)) {

               * Memory error *
               z0->next = z0break_avail;
               z0break_avail = z0;
               strcpy(outBuffer, "E05");
               break;
            }*/

            /* Fill it */
            z0->address = address;

            if( z0->address == (unsigned char *) frame->epc )
            {
               /* re-asserting the breakpoint that put us in here, so
               we'll add the breakpoint but leave the code in place
               since we'll be returning to it when the user continues */
               z0->instr = 0xffffffff;
            }
            else
            {
               /* grab the instruction */
               z0->instr = *(z0->address);
               /* and insert the break */
               *(z0->address) = BREAK_INSTR;
            }

            /* Add to the list */
            {
               struct z0break *znxt = z0break_list;

               z0->prev = NULL;
               z0->next = znxt;

               if( znxt ) znxt->prev = z0;
               z0break_list = z0;
            }

            strcpy(outBuffer, "OK");
         }
         break;

         case 'z': /* remove breakpoint */
            if (inBuffer[1] == 'z')
            {
               goto dumpzbreaks;

               /*
                * zz packet - remove all breaks *
                z0last = NULL;

                for (z0=z0break_list; z0!=NULL; z0=z0->next)
                {
                if(!hstr2mem(z0->address, z0->buf, R_SZ))
                {
                ret = 0;
                }
                z0last = z0;
                }

                * Free entries if any *
                if (z0last != NULL) {
                z0last->next  = z0break_avail;
                z0break_avail = z0break_list;
                z0break_list  = NULL;
                }

                if (ret) {
                strcpy(outBuffer, "OK");
                } else {
                strcpy(outBuffer, "E04");
                }
                break;
               */
            }
            else
            {
               int ret, type, len;
               unsigned char *address;
               struct z0break *z0;

               ret = parse_zbreak(inBuffer, &type, &address, &len);
               if (!ret) {
                  strcpy(outBuffer, "E01");
                  break;
               }

               if (type != 0) {
                  /* We support only software break points so far */
                  break;
               }

               if (len != R_SZ) {
                  strcpy(outBuffer, "E02");
                  break;
               }

               /* Let us check whether this break point set */
               for (z0=z0break_list; z0!=NULL; z0=z0->next) {
                  if (z0->address == address) {
                     break;
                  }
               }

               if (z0 == NULL) {
                  /* Unknown breakpoint */
                  strcpy(outBuffer, "E03");
                  break;
               }

               /*
               if (!hstr2mem(z0->address, z0->buf, R_SZ)) {
                  strcpy(outBuffer, "E04");
                  break;
                  }*/

               if( z0->instr != 0xffffffff )
               {
                  /* put the old instruction back  */
                  *(z0->address) = z0->instr;
               }

               /* Unlink entry */
               {
                  struct z0break *zprv = z0->prev, *znxt = z0->next;

                  if( zprv ) zprv->next = znxt;
                  if( znxt ) znxt->prev = zprv;

                  if( !zprv ) z0break_list = znxt;

                  znxt = z0break_avail;

                  z0break_avail = z0;
                  z0->prev = NULL;
                  z0->next = znxt;
               }

               strcpy(outBuffer, "OK");
            }
            break;

         default: /* do nothing */
            break;
      }

      /* reply to the request */
      putpacket (outBuffer);
   }

  stubexit:

   /*
    *  The original code did this in the assembly wrapper.  We should consider
    *  doing it here before we return.
    *
    *  On exit from the exception handler invalidate each line in the I-cache
    *  and write back each dirty line in the D-cache.  This needs to be done
    *  before the target program is resumed in order to ensure that software
    *  breakpoints and downloaded code will actually take effect.  This
    *  is because modifications to code in ram will affect the D-cache,
    *  but not necessarily the I-cache.
    */

   clear_cache();
}

static int numsegs;
static struct memseg   memsegments[NUM_MEMSEGS];

int gdbstub_add_memsegment( unsigned base, unsigned end, int opts )
{
   if( numsegs == NUM_MEMSEGS ) return -1;

   memsegments[numsegs].begin = base;
   memsegments[numsegs].end   = end;
   memsegments[numsegs].opts   = opts;

   ++numsegs;
   return RTEMS_SUCCESSFUL;
}

static int is_readable(unsigned ptr, unsigned len)
{
   struct memseg *ms;
   int i;

   if( (ptr & 0x3) ) return -1;

   for(i=0; i<numsegs; i++)
   {
      ms= &memsegments[i];

      if( ms->begin <= ptr && ptr+len <= ms->end && (ms->opts & MEMOPT_READABLE) )
         return -1;
   }
   return 0;
}

static int is_writeable(unsigned ptr, unsigned len)
{
   struct memseg *ms;
   int i;

   if( (ptr & 0x3) ) return -1;

   for(i=0; i<numsegs; i++)
   {
      ms= &memsegments[i];

      if( ms->begin <= ptr && ptr+len <= ms->end && (ms->opts & MEMOPT_WRITEABLE) )
         return -1;
   }
   return 0;
}

static int is_steppable(unsigned ptr)
{
   struct memseg *ms;
   int i;

   if( (ptr & 0x3) ) return -1;

   for(i=0; i<numsegs; i++)
   {
      ms= &memsegments[i];

      if( ms->begin <= ptr && ptr <= ms->end && (ms->opts & MEMOPT_WRITEABLE) )
         return -1;
   }
   return 0;
}

static char initialized = 0;   /* 0 means we are not initialized */

void mips_gdb_stub_install(int enableThreads)
{
   /*
     These are the RTEMS-defined vectors for all the MIPS exceptions
   */
   int exceptionVector[]= { MIPS_EXCEPTION_MOD, \
                            MIPS_EXCEPTION_TLBL, \
                            MIPS_EXCEPTION_TLBS, \
                            MIPS_EXCEPTION_ADEL, \
                            MIPS_EXCEPTION_ADES, \
                            MIPS_EXCEPTION_IBE, \
                            MIPS_EXCEPTION_DBE, \
                            MIPS_EXCEPTION_SYSCALL, \
                            MIPS_EXCEPTION_BREAK, \
                            MIPS_EXCEPTION_RI, \
                            MIPS_EXCEPTION_CPU, \
                            MIPS_EXCEPTION_OVERFLOW, \
                            MIPS_EXCEPTION_TRAP, \
                            MIPS_EXCEPTION_VCEI, \
                            MIPS_EXCEPTION_FPE, \
                            MIPS_EXCEPTION_C2E, \
                            MIPS_EXCEPTION_WATCH, \
                            MIPS_EXCEPTION_VCED, \
                            -1 };
   int  i;
   rtems_isr_entry old;

   if (initialized)
   {
      ASSERT(0);
      return;
   }

   memset( memsegments,0,sizeof(struct memseg)*NUM_MEMSEGS );
   numsegs = 0;

#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT)
   if( enableThreads )
      do_threads = 1;
   else
      do_threads = 0;
#endif

   {
      struct z0break *z0;

      z0break_avail = NULL;
      z0break_list  = NULL;

      /* z0breaks list init, now we'll do it so it makes sense... */
      for (i=0; i<BREAKNUM; i++)
      {
         memset( (z0= &z0break_arr[i]), 0, sizeof(struct z0break));

         z0->next = z0break_avail;
         z0break_avail = z0;
      }
   }

   for(i=0; exceptionVector[i] > -1; i++)
   {
      rtems_interrupt_catch( (rtems_isr_entry) handle_exception, exceptionVector[i], &old );
   }

   initialized = 1;

   /* get the attention of gdb */
   /* mips_break(1);  disabled so user code can choose to invoke it or not */
}
