debug.c 11.5 KB
/*
 * Copyright (C) 1996-1998 by the Board of Trustees
 *    of Leland Stanford Junior University.
 * 
 * This file is part of the SimOS distribution. 
 * See LICENSE file for terms of the license. 
 *
 */

/*****************************************************************
 * debug.c
 * 
 * Support for running gdb on programs that are executing under
 * mipsy. Right now this will only work under simos, but it would be
 * nice to have it work for Solo Mipsy as well. 
 * $Author: blythe $
 * $File$
 *****************************************************************/
#include <sys/signal.h>
#include "mipsy.h"
#include "cp0.h"
#include "fpu.h"
#include "cpu_state.h"
#include "sim_error.h"
#include "memref.h"
#include "cpu_interface.h"
#include "gdb_interface.h"
#include "mips_gdb.h"

#ifdef USE_FLASHLITE
#include "mipsy_interface.h"
#include "flash_interface.h"
#endif

/* Local Variables */
static Reg cached_pc;
static uint cached_inst;

/* Global Variables */
int mipsy_sigusr = 0;
int mipsy_break_nexti = MIPSY_NOBREAK;
int mipsy_debug_mode = 0;

/*****************************************************************
 * mipsy_getreg
 *****************************************************************/
Result
MipsyGetReg(int cpunum, int regnum, Reg *val)
{
   CPUState* P;

#ifdef MIPSY_MXS
   if (PE[cpunum].inMXS) {
      CopyFromMXS(PE + cpunum);
   }
#endif     

   P = &PE[cpunum];
   
   if(regnum < 0) {
      return FAILURE;
   }
   
   if (regnum <= RA_REGNUM) {
      *val = P->R[regnum];
      return SUCCESS;
   }
   
   if ((regnum >= FP0_REGNUM) && (regnum <= FP31_REGNUM)) {
      *val = IntReg(regnum-FP0_REGNUM);
      return SUCCESS;
   }
   
   switch (regnum) {
   case PC_REGNUM:    *val = P->PC;               break;
   case CAUSE_REGNUM: *val = P->CP0[C0_CAUSE];    break;
   case BAD_REGNUM:   *val = P->CP0[C0_BADVADDR]; break;
   case HI_REGNUM:    *val = P->HI;               break;
   case LO_REGNUM:    *val = P->LO;               break;
   case FCRCS_REGNUM: *val = P->FCR[31];          break;
   case FCRIR_REGNUM: *val = P->FCR[30];          break;
   case INX_REGNUM:   *val = P->CP0[C0_INX];      break;
   case RAND_REGNUM:  *val = P->CP0[C0_RAND];     break;
   case TLBLO_REGNUM: *val = P->CP0[C0_TLBLO_0];   break;
   case CTXT_REGNUM:  *val = P->CP0[C0_CTXT];     break;
   case TLBHI_REGNUM: *val = P->CP0[C0_TLBHI];    break;
   case SR_REGNUM:    *val = P->CP0[C0_SR];       break;
   case EPC_REGNUM:   *val = P->CP0[C0_EPC];      break;
   case ERROR_EPC_REGNUM: *val = P->CP0[C0_ERROR_EPC]; break;
   case COUNT_REGNUM:    *val = P->CP0[C0_COUNT];       break;
   case COMPARE_REGNUM:  *val = P->CP0[C0_COMPARE];       break;
   case TLBLO1_REGNUM:   *val = P->CP0[C0_TLBLO_1];       break;
   case PGMASK_REGNUM:   *val = P->CP0[C0_PGMASK];       break;
   case NPC_REGNUM:   *val = P->nPC;              break;
   default:           return FAILURE;
   }
   
   return SUCCESS;
}

/*****************************************************************
 * MipsyPutReg
 *****************************************************************/
Result 
MipsyPutReg(int cpunum, int regnum, Reg val)
{
   CPUState* P;

#ifdef MIPSY_MXS
   if (PE->inMXS) {
      CPUWarning("mipsy_putreg doesn't work in MXS mode\n");
   }
#endif     

   P = &PE[cpunum];

   if ((regnum < 0) || (regnum >= GDB_NUM_REGS)) {
      return FAILURE;
   }
   
   if (regnum <= RA_REGNUM) { 
      P->R[regnum] = val;
      return SUCCESS;
   }
   
   if ((regnum >= FP0_REGNUM) && (regnum <= FP31_REGNUM)) {
      IntReg(regnum-FP0_REGNUM) = val;
      return SUCCESS;
   }

   switch (regnum) {
   case PC_REGNUM:    
      /* 
       * This one is a little tricky. If this is set from a PC
       * annotation, then everything should work fine. By also setting
       * the P->PC, I cover the case where this is set from gdb.
       */
      P->PC                = val; 
      P->nPC               = val; 
      P->branchTarget      = 0;
      P->branchStatus      = BranchStatus_none;
      break;
   case CAUSE_REGNUM: P->CP0[C0_CAUSE]    = val; break;
   case BAD_REGNUM:   P->CP0[C0_BADVADDR] = val; break;
   case HI_REGNUM:    P->HI               = val; break;
   case LO_REGNUM:    P->LO               = val; break;
   case FCRCS_REGNUM: P->FCR[31]          = val; break;
   case FCRIR_REGNUM: P->FCR[30]          = val; break;
   case INX_REGNUM:   P->CP0[C0_INX]      = val; break;
   case RAND_REGNUM:  P->CP0[C0_RAND]     = val; break;
   case TLBLO_REGNUM: P->CP0[C0_TLBLO_0]  = val; break;
   case CTXT_REGNUM:  P->CP0[C0_CTXT]     = val; break;
   case TLBHI_REGNUM: P->CP0[C0_TLBHI]    = val; break;
   case SR_REGNUM:    P->CP0[C0_SR]       = val; break;
   case EPC_REGNUM:   P->CP0[C0_EPC]      = val; break;
   case ERROR_EPC_REGNUM: P->CP0[C0_ERROR_EPC] = val; break;
   case COUNT_REGNUM:    P->CP0[C0_COUNT]      = val; break;
   case COMPARE_REGNUM:  P->CP0[C0_COMPARE]    = val; break;
   case TLBLO1_REGNUM:   P->CP0[C0_TLBLO_1]    = val; break;
   case PGMASK_REGNUM:   P->CP0[C0_PGMASK]     = val; break;
   default:           return FAILURE;
   }
   
   return SUCCESS;
}

/*****************************************************************
 * MipsyGetMem
 *****************************************************************/
Result
MipsyGetMem(int cpuNum, VA vAddr, uint nbytes, char *buf)
{
   PA pAddr;
   CPUState *P = &PE[cpuNum];

#ifdef MIPSY_MXS
   if (PE[cpuNum].inMXS) {
      CopyFromMXS(P);
   }
#endif     

#ifdef USE_FLASHLITE      
#ifndef SOLO
   /* Check for SIMRAMALIAS or SIMPROMALIAS.
    * These are special-cased. At init time, the contents of those
    * areas are copied from the backdoor into PP-private memory.
    * We don't want to access the backdoor copies here, but rather
    * the copies each node sees.
    */
   if (GetFPROMAndFRAM(cpuNum, vAddr, nbytes, buf)) {
      return SUCCESS;
   }
#endif
#endif
#if 1
      /* handle cache attributes by rewriting vAddr */
      if (IS_KSEG0(vAddr)) {
	if ((P->CP0[C0_CONFIG] & 0x7) == CBIT_UNCACHED)
	    vAddr |= K1BASE;    /* uncached K0 -> uncached K1 space */
      }
#endif
   if (PAGE_NUMBER(vAddr+nbytes-1) == PAGE_NUMBER(vAddr)) {
      /* common case: entire range fits on one page */
      if (TranslateVirtualNoSideeffect(P, vAddr, &pAddr) != SUCCESS) {
         return FAILURE;     /* for now, give up if not in TLB */
      }

      MemRefDebugReadData(cpuNum, vAddr, pAddr, buf, nbytes);
      
   } else {
      PA pAddr2;
      int bytes_on_first_page = (1<<12) - PAGE_OFFSET(vAddr);
      VA vaddr2 = (vAddr) + bytes_on_first_page;

      ASSERT(nbytes <= (1 << NUM_OFFSET_BITS));

      if (TranslateVirtualNoSideeffect(P, vAddr, &pAddr) != SUCCESS) {
         return FAILURE;        
      }
      
      if (TranslateVirtualNoSideeffect(P, vaddr2, &pAddr2) != SUCCESS) {
         return FAILURE;        
      }

      MemRefDebugReadData(cpuNum, vAddr, pAddr, buf, bytes_on_first_page);
      MemRefDebugReadData(cpuNum, vaddr2, pAddr2, 
                          buf + bytes_on_first_page,
                          nbytes - bytes_on_first_page);
   }

   return SUCCESS;
}

/*****************************************************************
 * MipsyPutMem
 *****************************************************************/
Result
MipsyPutMem(int cpuNum, VA vAddr, uint nbytes, char *buf)
{
   PA pAddr;
   CPUState *P = &PE[cpuNum];

#ifdef MIPSY_MXS
   if (PE[cpuNum].inMXS) {
      CopyFromMXS(P);
   }
#endif     

#ifdef USE_FLASHLITE      
#ifndef SOLO
   /* Check for SIMRAMALIAS or SIMPROMALIAS.
    * These are special-cased. At init time, the contents of those
    * areas are copied from the backdoor into PP-private memory.
    * We don't want to access the backdoor copies here, but rather
    * the copies each node sees.
    */
   if (PutFPROMAndFRAM(cpuNum, vAddr, nbytes, buf)) {
      return SUCCESS;
   }
#endif
#endif
#if 1
      /* handle cache attributes by rewriting vAddr */
      if (IS_KSEG0(vAddr)) {
	if ((P->CP0[C0_CONFIG] & 0x7) == CBIT_UNCACHED)
	    vAddr |= K1BASE;    /* uncached K0 -> uncached K1 space */
      }
#endif
   
   if (PAGE_NUMBER(vAddr+nbytes-1) == PAGE_NUMBER(vAddr)) {
      /* common case: entire range fits on one page */
      if (TranslateVirtualNoSideeffect(P, vAddr, &pAddr) != SUCCESS) {
         return FAILURE;     /* for now, give up if not in TLB */
      }

      MemRefDebugWriteData(cpuNum, vAddr, pAddr, buf, nbytes);
      
   } else {
      PA pAddr2;
      int bytes_on_first_page = (1<<12) - PAGE_OFFSET(vAddr);
      VA vaddr2 = (vAddr) + bytes_on_first_page;

      ASSERT(nbytes <= (1 << NUM_OFFSET_BITS));

      if (TranslateVirtualNoSideeffect(P, vAddr, &pAddr) != SUCCESS) {
         return FAILURE;        
      }
      
      if (TranslateVirtualNoSideeffect(P, vaddr2, &pAddr2) != SUCCESS) {
         return FAILURE;        
      }

      MemRefDebugWriteData(cpuNum, nbytes, pAddr, buf, bytes_on_first_page);
      MemRefDebugWriteData(cpuNum, vaddr2, pAddr2, 
                           buf + bytes_on_first_page,
                           nbytes - bytes_on_first_page);
   }

   return SUCCESS;
}


Result MipsyTranslateVirtualNoSE(int cpuNum, VA vAddr, PA *pAddr)
{
   return TranslateVirtualNoSideeffect(&PE[cpuNum], vAddr, pAddr) ;
}


/*****************************************************************
 * MipsyDebug
 *
 * Main entry point into the debugger from normal execution. 
 *
 * NOTE: the second argument is_break_instruction is set when
 * we call in due to a single-step trap (from cpu.c) even though
 * there is no break instruction at the PC.  We need to send
 * the SIGTRAP to gdb in this case.  If we send a SIGUSR2 it
 * stops single-stepping immediately even though we may not
 * have yet reached the next line boundary (for 'n' or 's').
 *
 * Return value 0 means nothing has changed, go through normal
 * instruction completion (pc = npc, npc += 4, annotations, etc).
 *
 * Return value 1 means PC has changed or instruction at PC has
 * been overwritten.  If you entered the debugger between instructions
 * abort anything you have read about the next instruction and restart.
 * If you entered the debugger from a breakpoint, reexecute the
 * instruction at that PC.
 *
 *****************************************************************/
int 
MipsyDebug(int cpunum, int is_break_instruction)
{
   Simdebug_result res;
   Inst new_inst=0;

   cached_pc = PE[cpunum].PC;

   if (MipsyGetMem(cpunum, cached_pc, 4, (char *)&cached_inst) != SUCCESS) {
      CPUWarning("Debug -- Current instruction at PC=0x%x not mapped!\n", 
                 cached_pc);
      cached_inst = 0; /* nop */
   }
   res = Simdebug_run(is_break_instruction ? SIGTRAP : SIGUSR2, cpunum);
   switch (res) {
   case SD_CONTINUE:
      mipsy_break_nexti = MIPSY_NOBREAK;
      mipsy_debug_mode = 0;
      break;
   case SD_NEXTI_ANYCPU:
      mipsy_break_nexti = MIPSY_BREAKANYCPU;
      mipsy_debug_mode = 1;
      break;
   default:
      mipsy_break_nexti = res;
      mipsy_debug_mode = 1;
      break;
   }
   
   if (PE[cpunum].PC != cached_pc) { 
      return 1;
   }
   
   if (MipsyGetMem(cpunum,cached_pc,4,(char*)&new_inst) != SUCCESS) {
      CPUWarning("Debug -- Current instruction at PC=0x%x not mapped!\n");
      return 1;
   }

   if( cached_inst != new_inst ) { 
      return 1;
   }

   return 0;
}

/*****************************************************************
 * HandleDebugSignal
 *****************************************************************/
void 
HandleDebugSignal(int cpuid, int sigusr)
{
   mipsy_debug_mode = 1;
   if (sigusr) {
      mipsy_sigusr = 1;
   } else { 
      if (cpuid == -1) {
         mipsy_break_nexti = MIPSY_BREAKANYCPU;
      } else {
         mipsy_break_nexti = cpuid;
      }
   }
}