cpu_interface.c 9.62 KB
/*
 * Copyright (C) 1998 by the Board of Trustees
 *    of Leland Stanford Junior University.
 * Copyright (C) 1998 Digital Equipment Corporation
 *
 * This file is part of the SimOS distribution.
 * See LICENSE file for terms of the license.
 *
 */

/*
 * Copyright (C) 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. 
 *
 */

/*****************************************************************
 * cpu_interface.c
 *****************************************************************/

#include <sys/signal.h>
#include "syslimits.h"
#include "simtypes.h"
#include "cpu_interface.h"
#include "checkpoint.h"


#include "alpha.h"
#include "gamma.h"
#include "tcl_init.h" 
#include "machine_params.h"
#include "sim_error.h"
#include "gdb_interface.h"
#include "alpha_gdb.h"
#include "ev5.h"
#include "addr_layout.h"

static CPUStatus GammaCPUStatus(int cpunum);
static void   GammaHandleDebugSignal(int cpuid, int sigusr); /* Defined in debug.c */
static VA     GammaCurrentPC(int);
extern void   GammaExitSimulator( CPUType toSim );
static int    GammaCurrentCpuNum(void);
static Inst   GammaCurrentInstruction(int);
static Result GammaGetReg(int cpunum, int regnum, Reg *val);
static void   GammaClearLockFlag(int cpunum);
static bool   GammaGetLockFlag(int cpunum);
static PA GammaGetLockAddr(int cpu);
static Result GammaGetMem(int cpunum, VA vAddr, uint nbytes, char *buf);
static Result GammaTranslateVirtualNoSE(int cpunum, VA vAddr, PA *paddr);
static void   GammaInstallMemAnnotation(VA);
static void GammaDMAInval(int machine, PA* k0list);
static void GammaLaunchSlaveCPU(int cpuNum);
static void GammaDoCheckpoint(int cpuNum);


bool tookCheckpoint = FALSE;

char *
GammaPAToHostAddr(int cpu, PA pAddr, VA vAddr) 
{
#ifndef SOLO
   return (PHYS_TO_MEMADDR(M_FROM_CPU(cpu),pAddr) +
           (IS_REMAPPED_PADDR(pAddr,cpu) ? remapVec->NodeAddr[cpu] : 0));
#endif
#ifdef SOLO
   return (char *)(vAddr);
#endif 
}

extern void GammaFinishMemRequests(void);



/*****************************************************************
 * CPUVectorInit
 *****************************************************************/
void 
GammaCPUVectorInit(void)
{
   CPUVec.Handle_Debug_Signal  = GammaHandleDebugSignal;
   CPUVec.GetRegister          = GammaGetReg;
   CPUVec.PutRegister          = GammaPutReg;
   CPUVec.ClearLockFlag        = GammaClearLockFlag;
   CPUVec.GetLockFlag          = GammaGetLockFlag;
   CPUVec.GetLockAddr          = GammaGetLockAddr;
   CPUVec.GetMemory            = GammaGetMem;
   CPUVec.PutMemory            = GammaPutMem;
   CPUVec.TranslateVirtualNoSE = GammaTranslateVirtualNoSE;
   CPUVec.InstallMemAnnotation = GammaInstallMemAnnotation;
   CPUVec.Unstall              = GammaUnstall;
   CPUVec.CPUstatus            = GammaCPUStatus;
   CPUVec.CycleCount           = GammaCycleCount;
   CPUVec.DMAInval             = GammaDMAInval;
   CPUVec.ExitSimulator        = GammaExitSimulator;
   CPUVec.CurrentCpuNum        = GammaCurrentCpuNum;
   CPUVec.CurrentPC            = GammaCurrentPC;
   CPUVec.CurrentInstruction   = GammaCurrentInstruction;
   CPUVec.InstrCount           = GammaInstructionCount;
   CPUVec.DoCheckpoint         = GammaDoCheckpoint;
#if 0
   CPUVec.TakeBusError         = GammaTakeBusError;
#endif
   CPUVec.useMemRef            = FALSE;
   CPUVec.singleEventQueue     = TRUE;
   CPUVec.PAToHostAddr         = GammaPAToHostAddr;
   CPUVec.LaunchSlaveCPU       = GammaLaunchSlaveCPU;
}



/*
 */


int
GammaCurrentCpuNum(void)
{
   return curPE->myNum;
}



VA
GammaCurrentPC(int cpu)
{
   return PE[cpu]->PC;
}

Inst GammaCurrentInstruction(int cpu)
{
   return  PE[cpu]->instr.word;
}


Result GammaGetReg(int cpu, int regnum, Reg *val)
{
   AlphaState *P = PE[cpu];
   if (regnum >=0 && regnum < 32) {
      *val = P->reg[regnum];
      return SUCCESS;
   }
   if (regnum >= 32 && regnum < 64) {
      Reg *regP = (Reg *)&P->fp[regnum-32];
      *val = *regP;
      return SUCCESS;
   }
   /* shadow registers */
   if (regnum >= SHADOW_REGNUM && regnum < (SHADOW_REGNUM+8)) {
     int shadowset = 8;
     Reg *regP;
     if (EV5_ShadowRegistersUsed(P, P->PC)) {
       /* return the saved register value */
       shadowset = 0;
     } else {
       /* return the shadow register value */
       shadowset = 8;
     }
     regP = (Reg *)&P->shadowRegs[regnum-67+shadowset];
     *val = *regP;
     return SUCCESS;
   }

   switch (regnum) {
   case INSTR_REGNUM: *val = P->instr.word; break;
   case PC_REGNUM: *val = P->PC; break;
   case FPCR_REGNUM: *val = P->fpcr; break;
   default: 
      CPUError("GammaGetReg: gdb register %d not defined \n",regnum);
   }
   return SUCCESS;
}


Result GammaPutReg(int cpu, int regnum, Reg val)
{
   AlphaState *P = PE[cpu];
   if (regnum >=0 && regnum < 32) {
      P->reg[regnum] = val;
      return SUCCESS;
   }
   if (regnum >= 32 && regnum < 64) {
      P->fp[regnum-32] = val;
      return SUCCESS;
   }

   switch (regnum) {
   case PC_REGNUM: P->PC = val; break;
   case FPCR_REGNUM: P->fpcr = val; break;
   default: 
      CPUError("GammaGetReg: gdb register %d not defined \n",regnum);
   }
   return SUCCESS;
}


static void GammaClearLockFlag(int cpu)
{
   AlphaState *P = PE[cpu];
   P->lock_flag = 0;
}

static bool GammaGetLockFlag(int cpu)
{
   AlphaState *P = PE[cpu];
   return P->lock_flag;
}

static PA GammaGetLockAddr(int cpu)
{
   AlphaState *P = PE[cpu];
   return P->locked_addr;
}

Result GammaGetMem(int cpunum, VA vAddr, uint nbytes, char *buf)
{
   int i;
   PA pA=0;
   buf[0] = 0;
   for (i=0;i<nbytes;i++) {
      if (EV5_DTranslateVirtual(PE[cpunum],vAddr+i,0,0,&pA)==MMU_SUCCESS) {
	 buf[i] = *(char *)PHYS_TO_MEMADDR(0,pA);
      } else {
	 return FAILURE;
      }
   }
   return SUCCESS;
}

Result GammaPutMem(int cpunum, VA vAddr, uint nbytes, char *buf)
{

   int i;
   PA pA=0;
   CPUWarning("Put mem size %d addr 0x%lx buf 0x%x (flush cache) \n",nbytes,vAddr,*(uint32*)buf);
   for (i=0;i<nbytes;i++) {
      if (EV5_DTranslateVirtual(PE[cpunum],vAddr+i,0,0,&pA)==MMU_SUCCESS) {
	 *(char*)PHYS_TO_MEMADDR(0,pA) =  buf[i];
      } else {
	 return FAILURE;
      }
   }
   return SUCCESS;
}

Result GammaTranslateVirtualNoSE(int cpunum, VA vAddr, PA *pAddr)
{
   if (EV5_DTranslateVirtual(PE[cpunum],vAddr,0,0,pAddr)==MMU_SUCCESS) {
      return SUCCESS;
   } else { 
      return FAILURE;
   }
}




/*****************************************************************
 * GammaDebug
 *
 * 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 
GammaDebug(int cpunum, int is_break_instruction)
{
   Simdebug_result res;

   res = Simdebug_run(is_break_instruction ? SIGTRAP : SIGUSR2, cpunum);
   switch (res) {
   case SD_CONTINUE:
      gamma_break_nexti = GAMMA_NOBREAK;
      gamma_debug_mode = 0;
      break;
   case SD_NEXTI_ANYCPU:
      gamma_break_nexti = GAMMA_BREAKANYCPU;
      gamma_debug_mode = 1;
      break;
   default:
      gamma_break_nexti = res;
      gamma_debug_mode = 1;
      break;
   }
   
   return 1;
}

/*****************************************************************
 * HandleDebugSignal
 *****************************************************************/
static void 
GammaHandleDebugSignal(int cpuid, int sigusr)
{
   gamma_debug_mode = 1;
   if (sigusr) {
      gamma_sigusr = 1;
   } else { 
      if (cpuid == -1) {
         gamma_break_nexti = GAMMA_BREAKANYCPU;
      } else {
         gamma_break_nexti = cpuid;
      }
   }
}

static CPUStatus GammaCPUStatus(int cpunum)
{
  return PE[cpunum]->cpuState;
}

static void GammaInstallMemAnnotation(VA vA)
{ ;
}


void GammaDMAInval(int machine, PA* k0list)
{
   ;
}

/*****************************************************************
 * HandleDebugSignal
 *****************************************************************/

void GammaLaunchSlaveCPU(int cpuNum)
{
   CPUWarning("GAMMA: launching cpu %d \n",cpuNum);
   PE[cpuNum]->cpuState = cpu_running;
}


static void GammaDoCheckpoint(int cpuNum)
{
   int i;
   VA oldPC[SIM_MAXCPUS];
   
   for(i=0;i<TOTAL_CPUS;i++) {
      oldPC[i] = PE[i]->PC;
   }
   if (AnnFMLookup(PE[cpuNum]->PC,ANNFM_PC_TYPE)) {
      /* most likely a post-pc annotation --> increment unless branch */
      uint32 inst =  CPUVec.CurrentInstruction(cpuNum);
      union alpha_instruction ainst = (union alpha_instruction) inst;
      switch (ainst.common.opcode) {
      case op_call_pal:
      case op_br:
      case op_bsr:
      case op_blbc:
      case op_beq:
      case op_blt:
      case op_ble:
      case op_blbs:
      case op_bne:
      case op_bge:
      case op_bgt:
      case op_fbeq:
      case op_fblt:
      case op_fble:
      case op_fbne:
      case op_fbge:
      case op_fbgt:
      case op_jsr:
         break;
      default:
         PE[cpuNum]->PC += 4;
      }
   }
   Simcpt_Checkpoint(CPT_SAVE,"");
   for(i=0;i<TOTAL_CPUS;i++) {
      PE[i]->PC = oldPC[i];
   }
}