faults.c 6.46 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. 
 *
 */


/****************************************************
 *
 * Tcl-based fault injection support
 *
 * Author:  John Chapin   1/30/96
 *
 ****************************************************/

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>

#include "../../cpus/shared/addr_layout.h"
#include "simmisc.h"
#include "sim_error.h"
#include "tcl_init.h"
#include "cpu_interface.h"

/**** Tcl dispatch table *****/

/* current interface
 * fault cpu 1 iparity          # next l1 icache access generates parity error
 * fault cpu 1 dparity          # next l1 dcache access generates parity error
 * fault cpu 1 ecc              # next l2 access generates ECC cache error
 * fault cpu 1 halt             # cease executing instructions
 * fault cpu 1 powerfail        # halt + stop responding to MAGIC interventions
 * fault magic 1 iparity        # next PP icache access generates parity error
 * fault magic 1 dparity        # next PP dcache access generates parity error
 * fault magic 1 halt           # PP ceases executing instructions
 * fault magic 1 powerfail      # halt + LLP failure on attached router
 * fault memory 0x350b00 27 ecc # access to range [addr,numlines] gives ECC
 *
 * fault inst smash 0x82005000 48  # fault instruction at addr with random seed
 * fault inst test 0x82005000 48   # test if can do fault
 */



static int cmdCpu(Tcl_Interp *interp, int argc, char *argv[]);
static int cmdMagic(Tcl_Interp *interp, int argc, char *argv[]);
static int cmdMemory(Tcl_Interp *interp, int argc, char *argv[]);
static int cmdInst(Tcl_Interp *interp, int argc, char *argv[]);

static tclcmd faultCmds[] = {
{   "cpu",         4, cmdCpu,       " cpu NodeNum faultType"},
{   "magic",       4, cmdMagic,     " magic NodeNum faultType"},
{   "memory",      5, cmdMemory,    " memory Addr Numlines faultType"},
{   "inst",        6, cmdInst,      " inst <test|smash> Addr randomSeed funcname"},
{    NULL,         0, NULL,           NULL}
};

static Tcl_HashTable faultTypeHT;

/* the order of entries in faultTypeEnum and faultTypeNames must match! */
static char* faultTypeNames[] = {
   "iparity", 
   "dparity", 
   "ecc",
   "halt", 
   "powerfail",
   NULL
};


void FaultInit(Tcl_Interp* interp)
{
   int i;
   Tcl_CreateCommand(interp, 
                     "fault", 
                     DispatchCmd, 
                     (ClientData)faultCmds,
                     NULL);

   Tcl_InitHashTable(&faultTypeHT, TCL_STRING_KEYS);
   i = 0;
   while (faultTypeNames[i]) {
      int new = 0;
      Tcl_HashEntry *entry = Tcl_CreateHashEntry(&faultTypeHT, 
                                                 faultTypeNames[i],
                                                 &new);
      ASSERT(new);
      Tcl_SetHashValue(entry, UINT_TO_PTRSIZE(i));
      i++;
   }

   {
      extern void FlashFaultInit(Tcl_Interp*);
      FlashFaultInit(interp);
   }
}

static int 
getNodeFaultType(Tcl_Interp* interp, char *nodeName, char* typeName,
                 int* nodenum, faultTypeEnum* faultType)
{
   int code;
   Tcl_HashEntry* entry;

   if ((code = Tcl_GetInt(interp, nodeName, nodenum)) != TCL_OK) {
      Tcl_AppendResult(interp, "bad cpunum \"", nodeName, "\"", NULL);
      return code;
   }

   if ((entry = Tcl_FindHashEntry(&faultTypeHT, typeName)) == NULL) {
      Tcl_AppendResult(interp, "bad fault type \"", typeName, "\"", NULL);
      return TCL_ERROR;
   }

   *faultType = (faultTypeEnum) Tcl_GetHashValue(entry);
   return TCL_OK;
}



int cmdCpu(Tcl_Interp *interp, int argc, char *argv[])
{
   int           nodeNum;
   faultTypeEnum faultType;
   int           code;


   code = getNodeFaultType(interp, argv[2], argv[3], &nodeNum, &faultType);
   if (code != TCL_OK) return code;

   switch (faultType) {
   case SIMFAULT_HALT:
      CPUVec.FaultInject(nodeNum, faultType);
      return TCL_OK;
   default:
      Tcl_AppendResult(interp, "fault cpu ", faultTypeNames[faultType],
                       " unimplemented", NULL);
      return TCL_ERROR;
   }
}

int cmdMagic(Tcl_Interp *interp, int argc, char *argv[])
{
   Tcl_AppendResult(interp, "fault magic unimplemented", NULL);
   return TCL_ERROR;
}

int cmdMemory(Tcl_Interp *interp, int argc, char *argv[])
{
#ifdef USE_FLASHLITE
  extern int insertECC(unsigned int addr, int lines);
  int code;
  int addrNum, linesNum;

  char *addrName  = argv[2];
  char *linesName = argv[3];
  char *faultName = argv[4];

  if ((code = Tcl_GetInt(interp, addrName, &addrNum)) != TCL_OK) {
    Tcl_AppendResult(interp, "bad address \"", addrName, "\"", NULL);
    return code;
  }
  if ((code = Tcl_GetInt(interp, linesName, &linesNum)) != TCL_OK) {
    Tcl_AppendResult(interp, "bad number of lines \"", linesName, "\"", NULL);
    return code;
  }
  if (strcmp(faultName, "ecc")) {
    Tcl_AppendResult(interp, "bad fault type \"", linesName, "\"", NULL);
    return TCL_ERROR;
  }

  if (insertECC(addrNum, linesNum)) {
    Tcl_AppendResult(interp, "cannot ECC address \"", addrName, "\"", NULL);
    return TCL_ERROR;
  }
  return TCL_OK;

#else
   Tcl_AppendResult(interp, "fault memory unimplemented", NULL);
   return TCL_ERROR;
#endif
}


int cmdInst(Tcl_Interp *interp, int argc, char *argv[])
{
  int addrNum, randomNum, code, doSmash;
  char errBuf[256];
  extern int smashinst(int dosmash, int cpunum, VA addr, int rnd, char* errbuf);
  int r;

  char *testName = argv[2];
  char *addrName  = argv[3];
  char *randomName = argv[4];

  if (argc < 6) {
    Tcl_AppendResult(interp, "need 5 args to fault inst\n", NULL);
    return TCL_ERROR;
  }
  strncpy(errBuf, argv[5], 255);

  if (!strcmp(testName, "test")) {
     doSmash = 0;
  } else if (!strcmp(testName, "smash")) {
     doSmash = 1;
  } else {
    Tcl_AppendResult(interp, "not test or smash: \"", testName, "\"", NULL);
    return TCL_ERROR;
  }
     
  if ((code = Tcl_GetInt(interp, addrName, &addrNum)) != TCL_OK) {
    Tcl_AppendResult(interp, "bad address \"", addrName, "\"", NULL);
    return code;
  }
  if ((code = Tcl_GetInt(interp, randomName, &randomNum)) != TCL_OK) {
    Tcl_AppendResult(interp, "bad random seed \"", randomName, "\"", NULL);
    return code;
  }

  r = smashinst(doSmash, CPUVec.CurrentCpuNum(), addrNum, randomNum, errBuf);
  Tcl_AppendResult(interp, errBuf, NULL);

  if ((r == SUCCESS) || (!strcmp(errBuf, "RETRY"))) 
     return TCL_OK;
  else 
     return TCL_ERROR;
}