1
0
Files
irix-657m-src/stand/arcs/IP32prom/simulate/SIM.c
2022-09-29 17:59:04 +03:00

754 lines
17 KiB
C

#include <fcntl.h>
#include <unistd.h>
#include <sys/sbd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fault.h>
#include <genpda.h>
#include <IP32.h>
#include <flash.h>
#include "SIM.h"
extern void _exit(int);
typedef long long longlong_t;
extern longlong_t __ll_lshift (longlong_t, longlong_t);
extern longlong_t __ll_rshift (longlong_t, longlong_t);
extern longlong_t __ull_rshift (longlong_t, longlong_t);
void forwardException(int sig, int code, Ctx_t* sc);
/***************************************************************************
* Simulated Machine State Storage *
***************************************************************************/
/*
* Coprocessor registers
*/
static k_machreg_t cpr0s[32];
/***************************************************************************
* K0 and K1 address matching *
***************************************************************************/
/*
* ADDRESS sign extends x to 64 bits (when I need it and figure out how to
* do it).
*/
#define ADDRESS(x) (x)
/* Address match structure */
typedef struct{
__uint64_t mask;
__uint64_t addr;
simFunc func;
int arg;
} AddrMatch;
/* Address match database */
static AddrMatch matchList[]={
{~0x7ff,ADDRESS(SERIAL_PORT0_BASE),SIM_mace_serial,0},
{~0x7ff,ADDRESS(SERIAL_PORT1_BASE),SIM_mace_serial,1},
{~MEMBLOCK_MASK,ADDRESS(0x80000000),SIM_k0_k1,0},
{~MEMBLOCK_MASK,ADDRESS(0xa0000000),SIM_k0_k1,1},
{~flashblock_MASK,ADDRESS(0xbfc00000),SIM_flash_ram,0},
{ADDRESS(-1),ADDRESS(FLASH_WENABLE),SIM_flash_we,0},
{~0xffff,ADDRESS(RTC_BASE),SIM_rtc,0},
{-0x1ff,ADDRESS(MAC110_BASE),SIM_mac110},
{0,0,0}
};
#define PC63_28(p) (p & ADDRESS(0xf0000000))
/******************
* getEffectiveAddr
*/
Saddr_t getEffectiveAddress(Instr_t inst,Ctx_t* sc){
return inst.i_format.simmediate + sc->sc_regs[inst.i_format.rs];
}
/************
* addr_match Check to see if the address matches a handled address.
*/
AddrMatch*
addr_match(Instr_t inst, __uint64_t eadr, Ctx_t *sc){
AddrMatch* am = matchList;
/* NOTE: Only look at bottom 32 bits for now */
while(am->mask)
if ((eadr & am->mask & 0xffffffff) == (am->addr & 0xffffffff))
return am;
else
am++;
return 0;
}
/*************
* nextAddress Figure out what the next address is.
*------------
* NOTE: I decide what cases I need to support by "discovery". That is,
* I execute the code and let the assertions catch cases I haven't implemented.
* That means there may still be unimplemented cases that might be triggered
* by code I haven't executed yet.
*/
static __uint64_t
nextAddress(Instr_t inst, Ctx_t *sc){
__uint64_t temp;
__uint64_t pc;
__int64_t rs,rd,rt;
/*
* If not in the branch delay slot, the next address is simply the pc + 4.
* Otherwise we have to look at the branch.
*/
if ((sc->sc_cause & CAUSE_BD)==0)
return sc->sc_pc + 4;
/*
* Fetch the Instr_t.
*/
inst = *(Instr_t*)(sc->sc_pc);
rs = sc->sc_regs[inst.i_format.rs];
rt = sc->sc_regs[inst.i_format.rt];
pc = sc->sc_pc + 8;
/*
* Decode and then update the pc appropriately
*/
switch(inst.j_format.opcode){
case beq_op:
if (rs==rt)
pc = sc->sc_pc + (inst.i_format.simmediate<<2) + 4;
break;
case bne_op:
if (rs!=rt)
pc = sc->sc_pc + (inst.i_format.simmediate<<2) + 4;
break;
case blez_op:
if (rs<=0)
pc = sc->sc_pc + (inst.i_format.simmediate<<2) + 4;
case bgtz_op:
if (rs>0)
pc = sc->sc_pc + (inst.i_format.simmediate<<2) + 4;
case bcond_op:
switch(inst.i_format.rt){
case bltz_op:
case bgez_op:
case bltzal_op:
case bgezal_op:
sim_assert(UNIMPLEMENTED); /* Fail all of these for now */
}
case j_op:
temp = inst.j_format.target<<2;
pc = PC63_28(sc->sc_pc) | temp;
break;
case jal_op:
temp = inst.j_format.target<<2;
sc->sc_regs[31] = sc->sc_pc + 8;
pc = PC63_28(sc->sc_pc) | temp;
break;
case spec_op:
temp = sc->sc_regs[rs];
switch(inst.r_format.func){
case jalr_op:
sc->sc_regs[rd] = sc->sc_pc + 8;
case jr_op:
break;
default:
sim_assert(UNIMPLEMENTED); /* Fail any other spec_op's */
}
pc = temp;
break;
default:
sim_assert(UNIMPLEMENTED); /* Fail everything else */
}
return pc;
}
/***********
* advancePC
*/
void
advancePC(Instr_t inst, Ctx_t* sc){
sc->sc_pc = nextAddress(inst,sc);
}
/*********
* getInst Fetch faulting Instr_t
*/
static Instr_t
getInst(Ctx_t* sc){
Instr_t* pc = (Instr_t*)sc->sc_pc;
if (sc->sc_cause & CAUSE_BD)
pc++;
return *pc;
}
/***************
* loadstoreSize Returns loadstore size in bytes.
*/
int
loadstoreSize(Instr_t inst){
switch(inst.i_format.opcode){
case lb_op:
return 1;
case lbu_op:
return 1;
case lh_op:
return 2;
case lhu_op:
return 2;
case lw_op:
return 4;
case lwl_op:
return 4;
case lwr_op:
return 4;
case ll_op:
return 4;
case ld_op:
return 8;
case ldl_op:
return 8;
case ldr_op:
return 8;
case lld_op:
return 8;
case sb_op:
return 1;
case sh_op:
return 2;
case sw_op:
return 4;
case swl_op:
return 4;
case swr_op:
return 4;
case sc_op:
return 4;
case sd_op:
return 8;
case sdl_op:
return 8;
case sdr_op:
return 8;
case scd_op:
return 8;
default:
sim_assert(UNIMPLEMENTED);
}
sim_assert(IMPOSSIBLE);
}
/*********
* isStore Returns 1 if Instr_t is a store Instr_t.
*/
int
isStore(Instr_t inst){
switch(inst.i_format.opcode){
case sb_op:
case sh_op:
case sw_op:
case swl_op:
case swr_op:
case sc_op:
case sd_op:
case sdl_op:
case sdr_op:
case scd_op:
return 1;
}
return 0;
}
/***********
* loadstore Processes a load or a store operation
*----------
* Used to handle remapped load/stores, the register source/destination
* is determined via sc, and the source/destination is in addr.
*
* NOTE: I decide what cases I need to support by "discovery". That is,
* I execute the code and let the assertions catch cases I haven't implemented.
* That means there may still be unimplemented cases that might be triggered
* by code I haven't executed yet.
*/
void
loadstore(Instr_t inst, Ctx_t *sc, void* addr){
__uint64_t* srcdst;
srcdst = &sc->sc_regs[inst.i_format.rt];
/* Dispatch and process according to the Instr_t type */
switch(inst.i_format.opcode){
case lb_op:
*srcdst = *((char*)addr);
break;
case lbu_op:
*srcdst = *((unsigned char*)addr);
break;
case lh_op:
*srcdst = *((short*)addr);
break;
case lhu_op:
*srcdst = *((unsigned short*)addr);
break;
case lw_op:
*srcdst = *((long*)addr);
break;
case lwl_op:
sim_assert(lwl_op==0);
break;
case lwr_op:
sim_assert(lwr_op==0);
break;
case ll_op:
sim_assert(ll_op==0);
break;
case ld_op:
*srcdst = *((long long*)(addr));
break;
case ldl_op:
sim_assert(ldl_op==0);
break;
case ldr_op:
sim_assert(ldr_op==0);
break;
case lld_op:
sim_assert(lld_op==0);
break;
case sb_op:
*((unsigned char*)addr) = *srcdst;
break;
case sh_op:
*((unsigned short*)addr) = *srcdst;
break;
case sw_op:
*((unsigned long*)addr) = *srcdst;
break;
case swl_op:
sim_assert(swl_op==0);
break;
case swr_op:
sim_assert(swr_op==0);
break;
case sc_op:
sim_assert(sc_op==0);
break;
case sd_op:
*((long long*)addr) = *srcdst;
break;
case sdl_op:
sim_assert(sdl_op==0);
break;
case sdr_op:
sim_assert(sdr_op==0);
break;
case scd_op:
sim_assert(scd_op==0);
default:
sim_assert(UNIMPLEMENTED);
}
/* Advance the PC to the next Instr_t */
advancePC(inst,sc);
}
/*********
* SIM_led
*--------
* The led went away for now so just skip this. Leave the hook in for now.
*/
void
SIM_led(Instr_t inst, Saddr_t addr, Ctx_t* sc,int i){
advancePC(inst,sc);
}
/***************************************************************************
* MIPS3 stuff *
***************************************************************************/
/********
* dshift Handle double shift Instr_ts
*/
void
dshift( Instr_t inst, Ctx_t* sc){
__int64_t rt = sc->sc_regs[inst.r_format.rt];
__int64_t sa = inst.r_format.re;
switch(inst.r_format.func){
case dsll32_op:
sc->sc_regs[inst.r_format.rd] = __ll_lshift(rt,sa+32);
break;
case dsrl32_op:
sc->sc_regs[inst.r_format.rd] = __ull_rshift(rt,sa+32);
break;
case dsra32_op:
sc->sc_regs[inst.r_format.rd] = __ll_rshift(rt,sa+32);
break;
default:
sim_assert(UNIMPLEMENTED);
}
advancePC(inst,sc);
}
/***************************************************************************
* Coprocessor registers *
***************************************************************************/
/******
* cop0 Process cop0 registers.
*/
void
cop0(int writeAccess, k_machreg_t* gpr, int cprNum){
char buf[128];
/*
* Dispatch to the appropriate coprocessor register service rtn.
*/
switch(cprNum){
case C0_COUNT:
cpr0s[cprNum] += 5000000; /* .1 sec for 50MHZ processor*/
default:
/*
* For registers with no side effect, just treat them as load/store.
*/
if (writeAccess)
cpr0s[cprNum] = *gpr;
else
*gpr = cpr0s[cprNum];
}
}
/***************************************************************************
* Exception service *
***************************************************************************/
/*************
* sbusHandler
*/
static void
sbusHandler(int sig, int code, Ctx_t *sc){
int ec = sc->sc_cause & CAUSE_EXCMASK;
int addr_idx;
char* msg;
char buf[128];
AddrMatch* ap;
Instr_t inst = getInst(sc);
Saddr_t eadr = getEffectiveAddress(inst,sc);
/* Must be a RADE or WADE exception */
sim_assert(ec==EXC_RADE || ec == EXC_WADE);
/*
* Check to see if it matches a serviced address. If
* so service it. Otherwise fall through and forward
* the exception.
*/
if (ap = addr_match(inst,eadr,sc)){
(*ap->func)(inst,eadr,sc,ap->arg);
return;
}
forwardException(sig,code,sc);
}
/**************
* ssegvHandler
*/
static void
ssegvHandler(int sig, int code, Ctx_t *sc){
/*
* We doen't service any translated addresses so simply forward
* the exception.
*/
forwardException(sig,code,sc);
}
/*************
* sillHandler Service SIGILL signal
*/
static void
sillHandler(int sig, int code, Ctx_t *sc){
Instr_t inst = getInst(sc);
int gprNum;
int cprNum;
AddrMatch* ap;
Saddr_t eadr;
/* Decode the opcode and process appropriately */
switch(inst.r_format.opcode){
case cop0_op:
cop0(inst.r_format.rs,&sc->sc_regs[inst.r_format.rt],inst.r_format.rd);
advancePC(inst,sc);
break;
/* Cache ops */
case cache_op:
advancePC(inst,sc);
break;
/* double word load/store op's */
case ld_op:
case ldl_op:
case ldr_op:
case lld_op:
case sd_op:
case sdl_op:
case sdr_op:
case scd_op:
eadr = getEffectiveAddress(inst,sc);
if (ap = addr_match(inst,eadr,sc)){
(*ap->func)(inst,eadr,sc,ap->arg);
return;
}
forwardException(sig,code,sc);
break;
/* double word shifts */
case spec_op:
switch(inst.r_format.func){
case dsll32_op:
case dsrl32_op:
case dsra32_op:
dshift(inst,sc);
break;
case daddu_op:
sc->sc_regs[inst.r_format.rd] =
sc->sc_regs[inst.r_format.rs] + sc->sc_regs[inst.r_format.rt];
advancePC(inst,sc);
break;
default:
sim_assert(UNIMPLEMENTED);
}
break;
/* Some double word arithmetic */
case daddiu_op:
sc->sc_regs[inst.i_format.rt] =
sc->sc_regs[inst.i_format.rs] + inst.i_format.simmediate;
advancePC(inst,sc);
break;
default:
forwardException(sig,code,sc);
}
}
#define BOGUS(x) regs[x] = 0xdeadface
/******************
* forwardException Passes exception to "firmware"
*-----------------
* It's not possible to simulate the low level exception handling because
* the low level exception processing must use the K0 and K1 cpu registers
* but in user mode, those registers are volatile because the IRIX kernel
* modifies them. Therefore we simulate the "result" of the low level
* exception processing. Namely, the low level code copies the registers
* into the register save area and then passes control to _exception_handler.
* The _exception_handler code is at a higher level so the K0/K1 problem
* doesn't exist. We create the register save area here and then just pass
* control to _exception_handler.
*
* This is really pretty flimsy but it works well enough (mainly because
* interrupts are always masked and because in the firmware nobody ever
* returns from an exception).
*
* This code may be fragile as well because this code must access the
* firmware code namespace and that's definitely fragile as the firmware
* namespace and the host namespace overlap.
*/
void forwardException(int sig, int code, Ctx_t* sc){
extern k_machreg_t excep_regs[];
extern void bcopy(void*, void*, int);
extern void _exception_handler(void);
int i;
/*
* pda contains pointer to register save area. If it's zero, we will
* use &excep_regs instead.
*/
volatile struct generic_pda_s *gp =
(volatile struct generic_pda_s*)&gen_pda_tab;
k_machreg_t* regs = gp->regs;
k_machreg_t sp = (k_machreg_t)gp->pda_fault_sp;
if (sp==0)
sp = (k_machreg_t)_fault_sp;
if (regs==0)
regs = excep_regs;
/* If the BEV bit is set, just announce the error and halt. */
if (cpr0s[C0_SR] & SR_BEV){
char buf[256];
sprintf(buf,
"exception (%d,%d) with sr<BEV> set not supported at pc: %0x\n",
sig,code,sc->sc_pc);
SIM_message(buf);
/*
* Hang so that we can stop the process and look at the call stack.
*/
while (1);
}
/* If not on the fault stack, save all the registers */
if (gp->stack_mode != MODE_FAULT){
/*
* Save various control registers. Note that two copies are made,
* one directly in the pda fields, one in the register save area.
* In a real platform, the register save area is not updated if this
* is a nested exception. We don't handle nested exceptions.
*/
gp->epc_save = regs[R_ERREPC] = sc->sc_pc;
BOGUS(R_CACHERR);
gp->sr_save = regs[R_SR] = sc->sc_status;
gp->exc_save = regs[R_EXCTYPE] = sig==SIGBUS ? EXCEPT_NORM : EXCEPT_UTLB;
gp->badvaddr_save = regs[R_BADVADDR] = sc->sc_badvaddr;
gp->cause_save = regs[R_CAUSE] = sc->sc_cause;
/*
* GPR's
*
* Note that we copy registers one at a time from sc_regs to regs because
* varcs_reg_t (regs[]) might be different than k_machreg_t (sc_regs[]) and
* 32/64 conversions might be required.
*/
for (i=0;i<32;i++)
regs[i] = sc->sc_regs[i];
regs[R_K0]=0xbad00bad;
regs[R_K1]=0xbad11bad;
/* MDxx */
regs[R_MDLO] = sc->sc_mdlo;
regs[R_MDHI] = sc->sc_mdhi;
/* Bogus processor registers */
BOGUS(R_INX);
BOGUS(R_RAND);
BOGUS(R_CTXT);
BOGUS(R_TLBLO);
BOGUS(R_TLBHI);
BOGUS(R_TLBLO1);
BOGUS(R_PGMSK);
BOGUS(R_WIRED);
BOGUS(R_COUNT);
BOGUS(R_COMPARE);
BOGUS(R_LLADDR);
BOGUS(R_WATCHLO);
BOGUS(R_WATCHHI);
BOGUS(R_EXTCTXT);
BOGUS(R_ECC);
BOGUS(R_CACHERR);
BOGUS(R_TAGLO);
BOGUS(R_TAGHI);
BOGUS(R_ERREPC);
BOGUS(R_CONFIG);
/* FPU registers */
for(i=0;i<32;i++)
regs[R_F0+i] = sc->sc_fpregs[i];
BOGUS(R_C1_EIR);
BOGUS(R_C1_SR);
}
/*
* Set the exception type
*/
gp->exc_save = sig==SIGBUS ? EXCEPT_NORM : EXCEPT_UTLB;
/*
* Return to code that passes control to the exception decode code.
* Switch to the fault stack. (Note that we use our own as the
*/
sc->sc_pc = (__uint64_t)(&_exception_handler);
sc->sc_regs[29] = sp;
}
/***************************************************************************
* Initialize simulation *
***************************************************************************/
extern void initSimFlash(void);
extern void flushSimFlash(void);
extern void initSimRTC(void);
extern void initSimMac110(void);
/*********
* simInit Establish simulation hooks.
*/
void
simInit(void){
sigset(SIGBUS, sbusHandler); /* Hook bus error */
sigset(SIGSEGV,ssegvHandler); /* Hook segv error */
sigset(SIGILL,sillHandler); /* Hook ill error */
/* Set up aligned pointers to memory SIMblock and flash SIMblocks */
SIMmemblock = (char*)((long)(SIMblock+4095) & ~4095);
SIMflashblock = (char*)((long)(SIMflash+4095) & ~4095);
initSimFlash();
/* Initialize cpr's */
cpr0s[C0_SR] = SR_BEV;
/* Init RTC and MAC110 */
initSimRTC();
initSimMac110();
}
/*************
* SIM_message
*/
void
SIM_message(const char* str){
extern size_t strlen(const char*);
write(1,str,strlen(str));
}
/************
* SIM_assert Special assert as normal asserts go through simulated IO.
*/
void
SIM_assert(char* ex, char* f, int l){
char buf[128];
sprintf(buf," sim_assert failure: %s at line %d in file %s\n",ex, l, f);
SIM_message(buf);
_exit(1);
}