userflush.c 8.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 to maintain cache consistency after DCG
 *
 * Highly platform-specific. Only sgi and alpha versions
 * implemented so far.
 * ****************************************************/


#include <stdio.h>
#ifdef sgi
#include <sys/cachectl.h>
#include <bstring.h>
#endif

#ifdef __alpha
#include <alpha/inst.h>
#include <machine/pal.h>
#endif

#include <errno.h>
#include <strings.h>

#include "sim_error.h"
#include "userflush.h"



extern int errno;



/* ************************************************************
 * sgi version
 * ************************************************************/

#ifdef sgi
#define LOG2_CACHE_SIZE 15
#define CACHE_LINE_SIZE 16

#define JMP_RA_INST         0x03e00008
#define ADDIU_LINESIZE_INT (0x24840000|((-CACHE_LINE_SIZE)&0xffff))
#define BEQZ_A0_XXX_INST    0x10800000

#define CACHE_SIZE    (1<<LOG2_CACHE_SIZE)
#define CACHE_INDEX(_va) (_va & ((CACHE_SIZE-1)&~(CACHE_LINE_SIZE-1)))




static char flushbuffermem[CACHE_SIZE*2+8];
static char flushbuffermem2[CACHE_SIZE*2+8];
unsigned int flushbuffer;
unsigned int flushbuffer2;

static int userflush_init = 0;



int slowcacheflush(void *addr, int len, int cache)
{
   return cacheflush(addr,len,cache);
}



int flushrange(unsigned int start, int nbytes, int cache);
/*
 * This is a user level version of the cacheflush routine for mshade. Note
 * that it only works on the R4000 & R4400 and it only flushes the 
 * primary caches. 
 */


void usercacheinit(void) {
   int i;
   flushbuffer = (unsigned int) 
      ((((unsigned int)flushbuffermem) + CACHE_SIZE) & ~(CACHE_SIZE-1));
   flushbuffer2 = (unsigned int) 
      ((((unsigned int)flushbuffermem2) + CACHE_SIZE) & ~(CACHE_SIZE-1));
   bzero((char *) flushbuffer, CACHE_SIZE+8); /* Fill with NOPs */
   bzero((char *) flushbuffer2, CACHE_SIZE+8); /* Fill with NOPs */
   for (i = 0; i < CACHE_SIZE; i += CACHE_LINE_SIZE) {
      int o = i/sizeof(unsigned int);
      ((int *)flushbuffer)[o] = ADDIU_LINESIZE_INT; /* add a0,a0,-LSIZE */
      ((int *)flushbuffer)[o+1] = 
         BEQZ_A0_XXX_INST|((CACHE_SIZE-(i+8))/sizeof(unsigned int));
      /* beq a0,zero, ret */
   }
   for (i = 0; i < CACHE_SIZE; i += CACHE_LINE_SIZE) {
      int o = i/sizeof(unsigned int);
      ((int *)flushbuffer2)[o] = ADDIU_LINESIZE_INT; /* add a0,a0,-LSIZE */
      ((int *)flushbuffer2)[o+1] = 
         BEQZ_A0_XXX_INST|((CACHE_SIZE-(i+8))/sizeof(unsigned int));
      /* beq a0,zero, ret */
   }
   ((int *)flushbuffer)[CACHE_SIZE/sizeof(unsigned int)] = JMP_RA_INST;
   ((int *)flushbuffer2)[CACHE_SIZE/sizeof(unsigned int)] = JMP_RA_INST;
   if (slowcacheflush((void *)flushbuffer, CACHE_SIZE+8, BCACHE) < 0) {
      perror("flushing cache in userflush init");
   }
   if (cacheflush((void *)flushbuffer2, CACHE_SIZE+8, BCACHE) < 0) {
      perror("flushing cache in userflush init");
   }
}  


int 
usercacheflush(void *addr, int nbytes, int cache)
{
    unsigned int start_addr, end_addr;
    unsigned int start_offset;
    int i;

  
    if (!userflush_init) {
       usercacheinit();
       userflush_init = 1;
    }
    /*
     * Round address to cache line boundries. Round down at start and
     * up at end.
     */
    start_addr = (unsigned int)addr & ~(CACHE_LINE_SIZE-1);
    end_addr = ((unsigned int)addr+nbytes+CACHE_LINE_SIZE-1) & 
                             ~(CACHE_LINE_SIZE-1);



    /*
     * We never need to flush more than the entire cache. Compute
     * the start and range of bytes to read into the primary cache
     * to flush the user buffer from it.  Note this assumes direct
     * mapped i & d caches.
     */
    nbytes = end_addr - start_addr;
    if (nbytes >= CACHE_SIZE) {
       flushrange(0, CACHE_SIZE, cache);
       return 0;
    }
    start_offset =  start_addr%CACHE_SIZE;
    /*
     * Detect the case of wrapping in the cache. Break this into
     * two different range flushes.
     */
    if (start_offset + nbytes > CACHE_SIZE) {
       (void) flushrange(start_offset, CACHE_SIZE-start_offset, cache);
       flushrange(0, nbytes-(CACHE_SIZE-start_offset), cache);
    } else { 
       flushrange(start_offset, nbytes, cache);
    }

    return 0;
}


struct {
   int start;
   int start2;
   int nbytes;
} bufLog[1024];

int bufCount;

void SyncInstr(void);

int
flushrange(unsigned int start, int nbytes, int cache)
{
   int i,x=0;
   int pid;
   int start2 = start+flushbuffer2;
   start += flushbuffer;
   bufLog[bufCount].start = start;
   bufLog[bufCount].start2 = start2;
   bufLog[bufCount].nbytes = nbytes;
   

   bufCount = (bufCount+1)%1024;;

   if (cache==DCACHE || cache==BCACHE) {
      for (i = 0; i < nbytes; i += CACHE_LINE_SIZE) {
         x += ((volatile int *)(start + i))[0];
         x += ((volatile int *)(start2 + i))[0];
         ((volatile int *)(start + i))[0] =  ((volatile int *)(start + i))[0];
         ((volatile int *)(start2 + i))[0] =  ((volatile int *)(start2 + i))[0];
      }
   }
   
   SyncInstr(); 
   
   if ((cache==ICACHE) || (cache==BCACHE)) {
      ((void(*)(int))(start))(nbytes);
      ((void(*)(int))(start2))(nbytes);
   }
   return x;
}



/* This version only flushes one line of the I-cache and the D-cache.
 * On the MIPS processors, inclusion of the I-cache is maintained
 * but coherency is not maintained. Both I and D caches MUST be flushed
 * if code is emitted at runtime.
 */

int FlushOneLine(TCA tca) {
  uint ca    = CACHE_INDEX((uint)tca);
  uint addr1 = flushbuffer  + ca;
  uint addr2 = flushbuffer2 + ca;
  int x=0;
  /* flush from both the I and D caches */
  x +=((volatile int *)addr1)[0];
  x +=((volatile int *)addr2)[0];
  ((volatile int *)(addr1))[0] =  ((volatile int *)(addr1))[0];
  ((volatile int *)(addr2))[0] =  ((volatile int *)(addr2))[0];

  
  ((void(*)(int))addr1)(CACHE_LINE_SIZE);
  ((void(*)(int))addr2)(CACHE_LINE_SIZE);

  return x;
}

#endif /* sgi */



/* **************************************************************
 * alpha
 * **************************************************************/



#ifdef __alpha
#define LOG2_CACHE_SIZE 13  /* 8kb */
#define CACHE_SIZE (1<<LOG2_CACHE_SIZE)
#define CACHE_LINE_SIZE 16  /* bytes */
#define HOST_PAGE_SIZE (1<<13)
#define NEXT_HOST_PAGE(_x) ( ((size_t)(_x)+HOST_PAGE_SIZE-1) & ~(HOST_PAGE_SIZE-1))


#define CCj(_opcode, _jsr, _ra, _rb, _disp) (uint32)(\
                                       U(_opcode)<<26 | U(_ra)<<21| \
                                       U(_rb)<<16 | U(_jsr)<<14 |   \
                                       ((U(_disp)>>2) & 0x3FFF) ) 


#define REG_ZERO 31
#define REG_RA   26

static Inst buf_space[(CACHE_SIZE+HOST_PAGE_SIZE)/sizeof(Inst)];

static union alpha_instruction *flushbuf;


void usercacheinit(void)
{
   int i;
   union alpha_instruction inst;
   inst.common.opcode = op_jsr;
   inst.j_format.function = jsr_jsr;
   inst.j_format.ra = REG_ZERO;
   inst.j_format.rb = REG_RA;
   inst.j_format.hint = 0;

   flushbuf = (union alpha_instruction*)NEXT_HOST_PAGE(buf_space);
   for (i=0;i<CACHE_SIZE/sizeof(Inst);i++) {
      flushbuf[i] = inst;
   }

   usercacheflush(flushbuf,CACHE_SIZE,0);
}

/* *************************************************
 * FlushOneLine. static function. Make sure to add
 * a "mb" before it. 
 * *************************************************/

#define CACHE_SIZE_MASK ((CACHE_SIZE-1) & ~0x7)
inline int FlushOneLine(TCA  tca) 
{  
   uint64 addr = (uint64)flushbuf+ (CACHE_SIZE_MASK&(uint64)tca);

   volatile uint64 x = *(volatile uint64 *)addr;
   *(volatile uint64*) addr = x;

   ((void(*)()) addr)();
   return 0;
}


int usercacheflush(void *addr, int len, int cache)
{
   int i;

   /* asm ("call_pal 0x86");*/ /* imb */


#if 0
   asm volatile ("mb"::);
   for (i=0;i<=len;i+=CACHE_LINE_SIZE) {
      FlushOneLine((TCA)((char*)addr+i));
   } 
#endif
    asm ("call_pal 0x86"); /* imb */
    for(i=0;i<12;i++) {
       ;
    }
 
  return 0;
}

int slowcacheflush(void *addr, int len, int cache)
{
   /*
    * imb done in usercacheflush already!
    * asm ("call_pal 0x86"); 
    */
   if ( len >CACHE_SIZE) {
      return usercacheflush(addr,CACHE_SIZE,cache);
   } else { 
      return usercacheflush(addr,len,cache);
   }
}


#endif /* __alpha */