registry.c 9.23 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. 
 *
 */

/*****************************************************************
 * $File$
 * 
 * The registry should hold three types of "special" addresses:
 *   Backdoor functions
 *   Backdoor variables
 *   I/O device ranges
 * 
 * $Author: blythe $
 * $Date: 2002/07/19 07:43:30 $
 *****************************************************************/

#include <stdlib.h>
#include "cpu_interface.h"
#include "simtypes.h"
#include "simutil.h"
#include "list.h"
#include "sim_error.h"
#include "simutil.h"
#include "registry.h"
#include "sim.h"
#include "syslimits.h"

#define MAX_FUNCS         1023  /* max. func. entries */
#define MAX_ENTRIES_CPU   32    /* max. range entries per CPU */
#define MAX_ENTRIES       SIM_MAXCPUS*MAX_ENTRIES_CPU

/* Just for registering backdoor functions */
typedef struct FuncEntry FuncEntry;
struct FuncEntry {
   bool present;
   uint64 tag;
   void (*func)(void);
};
static FuncEntry funcRegistry[MAX_FUNCS];


/* For registering backdoor and I/O data ranges */
typedef struct entry Entry;
struct entry {
   VA start;
   VA end;
   uint flag;
   void *data; /* This is either a function or a data pointer */
   char *name; /* Name, only used for debugging */
};

static Entry  registry[MAX_ENTRIES]; /* range registry */
static int    numEntries = 0;        /* number of entries */
static int    complete   = 0;        /* still adding entries if == 0 */
static Entry* lastEntry;             /* pointer to last entry accessed */

static VA minAddress;
static VA maxAddress;
static uint simFuncCount;


/*****************************************************************
 * Registry initialization
 *****************************************************************/
void 
RegistryInit(void)
{
   int i;

   minAddress = (VA)-1LL;
   maxAddress = 0;

   for (i=0; i < MAX_FUNCS; i++) {
      funcRegistry[i].func = NULL;
      funcRegistry[i].present = FALSE;
   }

   lastEntry = registry;
   
   numEntries = 0;
}


/*****************************************************************
 * This procedure just allows you to register functions with the
 * registry with no regard as to what address they are placed at. 
 * They will end up being dereferenced in the kernel, caught by 
 * the simulators, and then the corresponding function will be
 * executed in the backdoor. All functions will be called with
 * normal parameter lists, so I kept the arg type as void.
 *****************************************************************/

void 
RegistryAddSimFunction(void *ptr, void *func)
{
   uint hash;

   simFuncCount++;
   ASSERT(simFuncCount < MAX_FUNCS);
   hash = PTR_TO_UINT64(ptr) % MAX_FUNCS;
   while (funcRegistry[hash].present) {
      if (funcRegistry[hash].tag == PTR_TO_UINT64(ptr)) {         
         CPUPrint("REGISTRY: OVERRIDE  %#x as %#x entry %d\n",
                   funcRegistry[hash].func, funcRegistry[hash].tag, hash);
         break;
      }
      hash = (hash + 1) % MAX_FUNCS;
   }
   funcRegistry[hash].func = (void (*) (void))func;
   funcRegistry[hash].tag =  PTR_TO_UINT64(ptr);
   funcRegistry[hash].present = TRUE;

   SIM_DEBUG(('b', "REGISTRY: ADDED %#x as %#x entry %d\n", 
              func, ptr, hash));
}

void *
RegistryGetSimFunction(VA address)
{
   uint hash;

   hash = (uint)address % MAX_FUNCS;
   while (funcRegistry[hash].tag != (uint)address) {
      if (funcRegistry[hash].present != TRUE) {
         /* fell off the end of the list.  CPU model should
          * give a bus error to the processor since this
          * is unmapped memory.
          */
         return NULL;
      }
      hash = (hash + 1) % MAX_FUNCS;
   }
   SIM_DEBUG(('b', "BDOOR %lld getfunc: 0x%llx\n",
	      (Reg64)CPUVec.CycleCount(0), (Reg64)address));
   return (void *)funcRegistry[hash].func;
}


/*****************************************************************
 * Add a chunk of the virtual address space to the registry along 
 * with flags and an optional function that can be executed when this
 * range is addressed. The flag indicates whether this reference
 * should be treated as a function or just read uncached as data.
 ****************************************************************/
int
RegistryAddRange(VA start, VA size, uint flag, void *data, char *name)
{
   VA end = start + size;   
   register Entry *ptr;
   register Entry *endPtr = registry + numEntries;

   ASSERT(size > 0);
   ASSERT(!complete);

   /* Check for overlap. */
   for (ptr = registry; ptr < endPtr; ptr++) {
      if ((end <= ptr->start) || (start >= ptr->end)) 
         continue;
      CPUError("Overlap in call to RegistryAddRange\n");
   }
   ASSERT(numEntries < MAX_ENTRIES);
   ptr = endPtr;
   numEntries++;
   ptr->start = start;
   ptr->end = start + size;
   ptr->flag = flag;
   ptr->data = data;
   ptr->name = SaveString(name);

   if (start < minAddress) minAddress = start;
   if (start+size > maxAddress) maxAddress = start+size;


   return 0;
}


/*****************************************************************
 * Function for comparing registry entries. Assumes the
 * entries don't overlap.
 ****************************************************************/
static int
entry_compare(const void* ep1, const void* ep2)
{
  Entry *e1 = (Entry*) ep1;
  Entry *e2 = (Entry*) ep2;

  if (e1->start == e2->start && e1->end == e2->end) return 0;
  if (e1->end <= e2->start)                         return -1;
  if (e2->end <= e1->start)                         return 1;
  ASSERT(0);
  return 0; /* keep compiler happy */
}

static int
elem_compare(const void* xp, const void* ep)
{
  VA     x = *(VA *)xp;
  Entry* e = (Entry*)ep;
  if (e->start <= x && x < e->end) return 0;
  if (e->end <= x)                 return 1;
  else                             return -1;
}


/*****************************************************************
 * Signals that all ranges have been added to the registry.
 * The registry will be sorted, so that we can use fast binary
 * search in RegistryIsInRange.
 ****************************************************************/
void
RegistryComplete(void)
{
  ASSERT(!complete);

  /* sort registry */
  qsort(registry, numEntries, sizeof(Entry), entry_compare);

  complete = 1;
}


/*****************************************************************
 * Given a virtual address, this procedure determines if it is
 * in the special range of addresses. These addresses can be 
 * set to be DATA or FUNCTIONS and the returned flag determines 
 * which is intended.
 *****************************************************************/
bool
RegistryIsInRange(VA addr, void **data, uint *flag)
{
   register Entry *ptr;
   VA vAddr;

   ASSERT(complete && numEntries > 0);

   /* Check cache of last entry */
   if ((addr >= lastEntry->start) && (addr < lastEntry->end)) {
      /* Found a registered address! */
      *flag = lastEntry->flag;
      if (*flag == REG_FUNC)
         *data = (char *)lastEntry->data;
      else
         *data = ((char*)lastEntry->data) + (addr - lastEntry->start);
      SIM_DEBUG(('b', "BDOOR %lld inRange: 0x%llx (%s) to 0x%x\n",
                 (Reg64)CPUVec.CycleCount(0), (Reg64)addr,
		 lastEntry->name, *data));
      return TRUE;
   }
   
   if ((addr < minAddress) || (addr > maxAddress)) {
      return FALSE;
   }

   /* use binary search on registry -- it's sorted */
   vAddr = addr;
   ptr = bsearch((void*)&vAddr,
		 registry, numEntries, sizeof(Entry),
		 elem_compare);
   if (!ptr) return FALSE; /* not found */
   ASSERT(addr >= ptr->start && addr < ptr->end);

   /* Found a registered address! */
   *flag = ptr->flag;
   if (*flag == REG_FUNC)
     *data = (char *)ptr->data;
   else
     *data = ((char*)ptr->data) + (addr - ptr->start);
   lastEntry = ptr;
   SIM_DEBUG(('b', "BDOOR %lld inRange: 0x%llx (%s) to 0x%x\n",
              (Reg64)CPUVec.CycleCount(0), (Reg64)addr,
	      lastEntry->name, *data));
   return TRUE;
}

/*****************************************************************
 * Given a virtual address, this procedure determines the address
 * that the kernel should use to access it.
 *****************************************************************/
VA
RegistryReverseMap(void *data)
{
   register Entry *ptr;
   register Entry *endPtr = registry + numEntries;

#if !(defined(sgi) && defined(_ABIN32))
   for (ptr = registry; ptr < endPtr; ptr++) {
      int size = ptr->end - ptr->start;
      if ((data >= ptr->data) && ((char *)data < (char *)ptr->data+size)) {
         return ptr->start + ((char *)data - (char *)ptr->data);
      }
   }
#else
   /* Workaround for an SGI compiler bug - The "real" version of this code
    * crashes the compiler. */
   for (ptr = registry; ; ptr++) {
      int size = ptr->end - ptr->start;
      if ((data >= ptr->data) && ((char *)data < (char *)ptr->data+size)) {
         return ptr->start + ((char *)data - (char *)ptr->data);
      }
      if ( ptr < endPtr) break;
   }
#endif
   return (VA)0;
}

void
RegistryDumpEntries(void)
{
   Entry *ptr;
   Entry *endPtr = registry + numEntries;

   for (ptr = registry; ptr < endPtr; ptr++) {
      SIM_DEBUG(('b', "REGISTRY: %#llx - %#llx : (%#llx) : %s (%s)\n",
		  (Reg64)ptr->start, (Reg64)ptr->end, (Reg64)ptr->data,
		  ptr->name, (ptr->flag == REG_DATA)? "data":"func"));

   }
}