Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / legion / src / procs / sunsparc / common / ss_common.c
/*
* ========== Copyright Header Begin ==========================================
*
* OpenSPARC T2 Processor File: ss_common.c
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
*
* The above named program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License version 2 as published by the Free Software Foundation.
*
* The above named program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*
* ========== Copyright Header End ============================================
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "@(#)ss_common.c 1.257 07/10/12 SMI"
/*
* Complete the parsing of a SunSPARC processor.
*
* Pick out the relevent info and allocate the
* simcpu_t structures in the config_proc that
* gets handed to us.
*/
/*
* This is where the definitions get complicated.
* There is a processor specific component, a generic processor
* family component, and then the simulator infrastructure component.
* The latter is basically glue to tie a processor to a domain, and
* the processor specific info. The family component is general
* (ish) e.g. a v9 processor .. with a bunch of data structures
* flags etc. only understood by the family code, and then there is
* abunch of code and data specific to this CPU.
* All of these items get parsed and configured as part of this code.
*
* Note that this file is compiled three times in order to create
* the three processor specific loadable modules: libniagara.so,
* libniagara2.so, and librock.so.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h> /* memcpy/memset */
#include <strings.h>
#include <thread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "ss_common.h"
#include "magictraps.h"
#include "save_restore.h"
/*
* The following chip specific header files contain #if ... #endif
* statements so we don't have to worry about including one chip's
* definitions in the compilation of another's.
*/
#ifdef NIAGARA1
#include "niagara.h"
#endif
#ifdef NIAGARA2
#include "niagara2.h"
#endif
#ifdef ROCK /* { */
#include "rock.h"
#endif /* } */
#include "ss_hwtw.h"
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
/* Need to define this as processor specific code somewhere */
#define PIC_MAX UINT32_MAX
/*
* defines used for manipulating pcr and pic regs
*
* PIC1 PIC0
* [63------32][31------0]
*/
/* Return the appropriate pic value from within the 64bit counter */
#define CPU_COUNTER_TO_PIC0(cntr) ((cntr) & 0xFFFFFFFFULL)
#define CPU_COUNTER_TO_PIC1(cntr) ((cntr) >> 32)
/* Update the 64bit counter with the appropriate pic value */
#define PIC0_PIC1_TO_CPU_COUNTER(pic0, pic1) ((pic0) | (((uint64_t)(pic1)) << 32))
#endif /* } */
uint64_t* lookup_sim_addr(simcpu_t *sp, uint64_t *sim_addr);
ss_trap_list_t ss_trap_list[0x200];
#define DBGP(s) do { s } while (0)
static void parse_debug_hook(ss_proc_t *procp);
#ifdef HW_TABLEWALK
static bool_t ss_hwtw_get_tte_entry(simcpu_t*, uint8_t *, uint64_t*);
#endif /* HW_TABLEWALK */
static void ss_tlb_init(ss_tlb_t * tlbp, uint_t nentries);
static void ss_l1_cache_init(ss_l1_cache_t * cachep, uint32_t cachesize,
bool_t is_immu);
/*
* Global array for calculating page size shift bits
* The sun4v page size is 4 bits
*/
#if defined(ROCK)
uint8_t ss_page_size_shift[16]={ 13, 16, 19, 22, 25, 28, 31, 34 };
#elif defined(NIAGARA1) || defined(NIAGARA2)
uint8_t ss_page_size_shift[16]={ 13, 16, 0, 22, 0, 28, 0, 0 };
#else
#error "No valid processor defined."
#endif
uint64_t ss_ext_signal(config_proc_t * config_procp, ext_sig_t sigtype,
void *vp);
void * ss_dbgr_attach(domain_t *, config_proc_t *, char *);
void ss_dbgr_detach(void *);
bool_t ss_dbgr_regread(void*, uint_t, uint64_t *);
bool_t ss_dbgr_regwrite(void*, uint_t, uint64_t);
uint64_t ss_dbgr_mem_read(void*, tvaddr_t, bool_t, uint8_t *, uint64_t);
uint64_t ss_dbgr_mem_write(void*, tvaddr_t, bool_t, uint8_t *, uint64_t);
uint64_t ss_dbgr_mem_clear(void*, tvaddr_t, bool_t, uint64_t);
void ss_dbgr_set_break(void*, tvaddr_t);
void ss_dbgr_clear_break(void*, tvaddr_t);
static ss_proc_t * ss_proc_alloc(config_proc_t *);
/* genuinely static functions */
/* VtoP tranlsation used by debugger interface */
static bool_t ss_vtop_translate( ss_proc_t *, sparcv9_cpu_t * strandp, tvaddr_t va, tpaddr_t *pap, tvaddr_t * vlenp);
static tpaddr_t ss_pmem_op(
domain_t * domainp,
ss_proc_t * procp,
sparcv9_cpu_t * strandp,
dbgr_mem_op_t memop,
tpaddr_t pa,
uint8_t * bufp,
tpaddr_t size );
static void ss_xdc_miss(simcpu_t *, uint64_t * regp, tvaddr_t, maccess_t op);
static void ss_read_state_reg(simcpu_t * sp, uint_t rdest, uint_t state_reg);
static void ss_write_state_reg(simcpu_t * sp, uint_t state_reg, uint64_t val);
static void ss_read_priv_reg(simcpu_t * sp, uint_t rdest, uint_t priv_reg);
static void ss_write_priv_reg(simcpu_t * sp, uint_t priv_reg, uint64_t val);
static void ss_read_hyp_priv_reg(simcpu_t * sp, uint_t rdest, uint_t priv_reg);
static void ss_write_hyp_priv_reg(simcpu_t * sp, uint_t priv_reg, uint64_t val);
static void ss_done_retry(simcpu_t * sp, bool_t);
static void ss_jpriv(simcpu_t * sp, tvaddr_t);
static void ss_post_precise_trap(simcpu_t *, sparcv9_trap_type_t);
static void ss_reset_trap(simcpu_t * sp, ss_trap_type_t tt);
static void ss_cycle_target_match(simcpu_t *);
#define NA_HPSTATE_ENB_BIT 11 /* ENB bit in HPSTATE is bit 11 */
#define V9_TSTATE_GL_MASK ((uint64_t)0x7)
#define V9_TSTATE_GL_SHIFT 40
#define V9_TSTATE_CCR_MASK ((uint64_t)0xff)
#define V9_TSTATE_CCR_SHIFT 32
#define V9_TSTATE_ASI_MASK ((uint64_t)0xff)
#define V9_TSTATE_ASI_SHIFT 24
#define V9_TSTATE_PSTATE_MASK ((uint64_t)0x7ff) /* FIXME */
#define V9_TSTATE_PSTATE_SHIFT 8
#define V9_TSTATE_CWP_MASK ((uint64_t)0x1f)
#define V9_TSTATE_CWP_SHIFT 0
#define V9_CCR_MASK (0xffLL)
#define V9_ASI_MASK (0xffLL)
#define V9_PIL_MASK (0xfLL)
/*
* Macros used everywhere that we change the processor state
* employ these to make sure that other variables that are defined by
* the cpu state are kept consistent
*/
#if 0 /* { */
#define V9_PSTATE_CHANGED(_v9p)
/* the mmu_bypass flag for niagara depends whether we are in hpriv or red state or not */
#define V9_HPSTATE_CHANGED(_sp, _v9p) do { \
ss_strand_t * _nsp; \
_nsp = _v9p->impl_specificp; \
\
_nsp->mmu_bypass = (_v9p->state == V9_HyperPriv) || (_v9p->state == V9_RED); \
DBGHPS( lprintf(_sp->gid, "hpstate = %s\n", sparcv9_state_name[ _v9p->state ]); ); \
} while (0)
#endif /* } */
#if ERROR_INJECTION
#define SET_ERROR_CHECK(_sp) do { \
if (_sp->error_enabled) \
_sp->error_check = \
(_newstate == _sp->error_priv) ? true : false; \
} while (0)
#else
#define SET_ERROR_CHECK(_sp) do { } while (0)
#endif
#define V9_STATE_CHANGE(_isp, _iv9p, _inewstate) do { \
sparcv9_cpu_t * _v9p = (_iv9p); \
sparcv9_state_t _newstate = (_inewstate); \
ss_strand_t * _nsp; \
simcpu_t * _sp = _isp; \
\
PERF_ACCUMULATE_ICOUNT(_v9p); \
\
_nsp = _v9p->impl_specificp; \
_nsp->mmu_bypass = (V9_HyperPriv==_newstate) || (V9_RED==_newstate); \
DBGHPS( extern char * sparcv9_state_name[]; \
lprintf(_sp->gid, "hpstate = %s -> %s (pc=0x%llx)\n", sparcv9_state_name[ _v9p->state ], sparcv9_state_name[ _newstate ], _sp->pc); ); \
_v9p->state = (_newstate); \
xcache_set_tagstate(_sp); \
SET_ERROR_CHECK(_sp); \
\
} while (0)
#define V9_PSTATE_AM_CHANGED(_v9p) do { \
} while (0)
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
/*
* The simulated PIC register values are only updated when the new values
* are required. To do this, the count source for each counter is sampled
* after each update so a delta can be derived.
*
* The implementation is simplistic in that the count controls are ignored,
* but it is enough to test the Solaris PIC code.
*/
#define UPDATE_PIC( _sp, _nsp, _v9p ) do { \
uint64_t _new; \
_new = RAW_TICK(_v9p); \
(_nsp)->pic0 += (_new - (_nsp)->pic0_sample_base); \
(_nsp)->pic0_sample_base = _new; \
_new = ICOUNT(_sp); \
(_nsp)->pic1 += (ICOUNT(_sp) - (_nsp)->pic1_sample_base); \
(_nsp)->pic1_sample_base = _new; \
} while (0)
#define RESET_PIC_SAMPLE_BASES( _sp, _nsp, _v9p ) do { \
(_nsp)->pic0_sample_base = RAW_TICK(_v9p); \
(_nsp)->pic1_sample_base = ICOUNT(_sp); \
} while (0)
#endif /* } */
ss_proc_t * ss_proc_alloc(config_proc_t * config_procp)
{
ss_proc_t * procp;
procp = Xcalloc(1, ss_proc_t);
procp->config_procp = config_procp;
config_procp->procp = procp;
procp->rstv_addr = (tvaddr_t)0;
procp->has_fpu = true; /* FPU present by default */
procp->nglobals = SS_VER_MAXGL + 1;
procp->maxtl = SS_VER_MAXTL;
procp->nwins = SS_VER_MAXWIN + 1;
procp->itlbspec.nentries = DEFAULT_ITLB_ENTRIES; /* FIXME */
procp->itlbspec.parity = false;
procp->dtlbspec.nentries = DEFAULT_DTLB_ENTRIES;
procp->dtlbspec.parity = false;
#if defined(ROCK)
procp->tlbspec.nentries = DEFAULT_TLB_ENTRIES; /* Rock has a unified L2 TLB also */
procp->tlbspec.parity = false;
/*
* Initialize the real page size info. As per the PRM, for real translations
* we probe the TLB 8 times (8 page sizes enabled) and use the following
* order: 256MB -> 2GB -> 16GB -> 8KB -> 64KB -> 512KB -> 4MB -> 32MB
*/
procp->real_page_size.enabled_page_sizes = 8;
procp->real_page_size.page_size_shift[0] = PGSZCODE_TO_SHIFT(5);
procp->real_page_size.page_size_shift[1] = PGSZCODE_TO_SHIFT(6);
procp->real_page_size.page_size_shift[2] = PGSZCODE_TO_SHIFT(7);
procp->real_page_size.page_size_shift[3] = PGSZCODE_TO_SHIFT(0);
procp->real_page_size.page_size_shift[4] = PGSZCODE_TO_SHIFT(1);
procp->real_page_size.page_size_shift[5] = PGSZCODE_TO_SHIFT(2);
procp->real_page_size.page_size_shift[6] = PGSZCODE_TO_SHIFT(3);
procp->real_page_size.page_size_shift[7] = PGSZCODE_TO_SHIFT(4);
/* Initialize jrand48 randstate array to seed values */
procp->randstate[0] = RANDSEED1;
procp->randstate[1] = RANDSEED2;
procp->randstate[2] = RANDSEED3;
procp->axis_console = false;
procp->erratum66_monitor = false;
procp->no_stingray = false;
#ifdef ROCK_FAKE_SP /* { */
/* For 'fake_sp', default chip id is based on conf file order. */
procp->fake_chip_id = procp->config_procp->domainp->systemp->fake_sp ?
config_procp->proc_id : 0;
#endif /* } */
#endif
#if PERFORMANCE_CHECK
procp->proc_debug.perf_cycle_gap = PERF_CYCLE_GAP;
procp->proc_debug.exit_at = 0x0ULL;
#endif
return procp;
}
#if ERROR_INJECTION
void extract_error_op(error_conf_t * errorconfp)
{
errorconfp->op_namep = strdup(lex.strp);
if (streq(lex.strp,"ifetch"))
errorconfp->op = IFETCH;
else if (streq(lex.strp,"ld"))
errorconfp->op = LD;
else if (streq(lex.strp,"asi_ld"))
errorconfp->op = ASI_LD;
else if (streq(lex.strp,"st"))
errorconfp->op = ST;
else if (streq(lex.strp,"any"))
errorconfp->op = ANY_OP;
else
lex_fatal("unknown error type parsing error config");
}
void extract_error_priv(error_conf_t * errorconfp)
{
errorconfp->priv_namep = strdup(lex.strp);
if (streq(lex.strp,"hyper"))
errorconfp->priv = V9_HyperPriv;
else if (streq(lex.strp,"priv"))
errorconfp->priv = V9_Priv;
else if (streq(lex.strp,"user"))
errorconfp->priv = V9_User;
else if (streq(lex.strp,"red"))
errorconfp->priv = V9_RED;
else
lex_fatal("unknown error type parsing error config");
}
void dump_errconf(error_conf_t * ep) {
uint_t count = 1;
while (ep != NULL) {
lprintf(-1, "Error Config #%u\n",count);
lprintf(-1, " cycle: %llu\n",ep->cycle);
lprintf(-1, " type: %s\n",ep->type_namep);
lprintf(-1, " op: %s\n",ep->op_namep);
lprintf(-1, " priv: %s\n",ep->priv_namep);
ep = ep->nextp;
count++;
}
}
void parse_error(domain_t * domainp)
{
error_conf_t **pep, *ep, *errorconfp;
errorconfp = Xmalloc( sizeof(error_conf_t) );
errorconfp->cycle = ~0ull;
errorconfp->priv = V9_HyperPriv;
errorconfp->type = NULL;
errorconfp->op = NULL;
errorconfp->npp = NULL;
lex_get(T_L_Brace);
do {
lexer_tok_t tok;
tok = lex_get_token();
if (tok == T_EOF)
lex_fatal("unexpected end of file parsing cpu");
if (tok == T_R_Brace) break;
if (tok != T_Token)
lex_fatal("token expected in SunSPARC processor specification");
if (streq(lex.strp,"cycle")) {
errorconfp->cycle = parse_number_assign();
} else
if (streq(lex.strp,"type")) {
lex_get(T_String);
extract_error_type(errorconfp);
lex_get(T_S_Colon);
} else
if (streq(lex.strp,"op")) {
lex_get(T_String);
extract_error_op(errorconfp);
lex_get(T_S_Colon);
} else
if (streq(lex.strp,"priv")) {
lex_get(T_String);
extract_error_priv(errorconfp);
lex_get(T_S_Colon);
} else
lex_fatal("unknown token %s in error specification",
lex.strp);
} while (1);
/* insert error_conf_t into list sorted by cycle */
for (pep = &(domainp->errlistp); (ep=*pep) != NULL; pep =&(ep->nextp) ) {
if (errorconfp->cycle < ep->cycle) break;
}
errorconfp->nextp = *pep;
*pep = errorconfp;
lex_unget();
lex_get(T_R_Brace);
}
error_conf_t * new_errconf(error_op_t op, error_type_t type) {
error_conf_t * ep;
ep = Xcalloc(1, error_conf_t);
ep->op = op;
ep->type = type;
ep->op_namep = strdup("unknown");
ep->type_namep = ep->op_namep;
ep->priv_namep = ep->op_namep;
return ep;
}
error_conf_t * find_errconf(simcpu_t * sp, error_op_t op, error_type_t type) {
error_conf_t * ep;
ep = sp->errorp->errlistp;
while (ep != NULL) {
if ((ep->op & op) && (ep->type & type)) break;
ep = ep->nextp;
}
return ep;
}
/*
* remove error config from the list of current errors
* return NULL if no more errors in current error list although there may
* still be more in the processor pending error list waiting for a cycle match
*/
bool_t remove_errconf(simcpu_t * sp, error_conf_t * rmep) {
error_conf_t ** pep, * ep;
for (pep = &(sp->errorp->errlistp); (ep=*pep) != NULL; pep =&(ep->nextp)) {
if (ep == rmep) break;
}
*pep = ep->nextp;
Xfree(rmep);
return (sp->errorp->errlistp ? true : false);
}
void clear_errflags(simcpu_t * sp) {
sp->error_enabled = false;
sp->error_check = false;
sp->errorp->check_xicache = false;
sp->errorp->check_xdcache = false;
sp->errorp->check_dtlb = false;
}
#endif /* ERROR_INJECTION */
/*
* Complete the creation and parsing of this specific cpu
*
* .. basically parse and allocate what is necessary.
*/
void ss_parse(domain_t * domainp, config_proc_t * config_procp)
{
ss_proc_t *procp;
lexer_tok_t tok;
char buf[64];
config_dev_t *pd;
config_dev_t *overlapp;
config_addr_t *ap;
bool_t debug_hook_defined = false;
uint64_t manuf = SS_VER_MANUF;
uint64_t impl = SS_VER_IMPL;
uint64_t mask = SS_VER_MASK;
uint_t nvthreads = 0, ncores = 0;
uint64_t core_mask = 0;
sprintf(buf, "cpu-%02d", config_procp->proc_id);
DBG( lprintf(-1, "SunSPARC: parsing cpu %s\n", buf); );
/*
* fill in with default values where necessary
*/
procp = ss_proc_alloc(config_procp);
do {
tok = lex_get_token();
if (tok == T_EOF) lex_fatal("unexpected end of file parsing cpu");
if (tok == T_R_Brace) break;
if (tok != T_Token) lex_fatal("token expected in SunSPARC processor specification");
if (streq(lex.strp,"rstv")) {
procp->rstv_addr = parse_number_assign();
if ((procp->rstv_addr & 31) != 0) lex_fatal("SunSPARC CPU RSTV addr needs to be 32byte aligned");
} else
if (streq(lex.strp,"maxtl")) {
procp->maxtl = parse_number_assign();
if (procp->maxtl >= SPARCv9_TLSPACE) fatal("Sorry maxtl must be %u or less (recompile sim for more)", SPARCv9_TLSPACE-1);
} else
if (streq(lex.strp,"ver")) {
tok = lex_get_token();
if (tok == T_Number) {
lex_unget();
procp->ver = parse_number_assign() &
(0xFFFFFFFFFFULL<<SS_VER_MASK_OFFSET);
} else
if (tok == T_String) {
while (tok != T_S_Colon) {
if (streq(lex.strp,"mask")) {
lex_get(T_Number);
mask = lex.val;
} else
if (streq(lex.strp,"impl")) {
lex_get(T_Number);
impl = lex.val;
} else
if (streq(lex.strp,"manuf")) {
lex_get(T_Number);
manuf = lex.val;
} else {
printf("\nUnknown ver option %s\n", lex.strp);
}
tok = lex_get_token();
}
}
} else
if (streq(lex.strp,"clkfreq")) {
procp->clkfreq = parse_number_assign();
} else
if (streq(lex.strp,"core_mask")) {
core_mask = parse_number_assign();
} else
if (streq(lex.strp,"cores")) {
ncores = parse_number_assign();
} else
if (streq(lex.strp, "vthreads") || streq(lex.strp, "strands")) {
nvthreads = parse_number_assign();
} else
if (streq(lex.strp,"nwins")) {
procp->nwins = parse_number_assign();
} else
if (streq(lex.strp,"nglobals")) {
procp->nglobals = parse_number_assign();
} else
if (streq(lex.strp,"itlb")) {
ss_parse_tlb(&procp->itlbspec);
} else
if (streq(lex.strp,"dtlb")) {
ss_parse_tlb(&procp->dtlbspec);
} else
if (streq(lex.strp,"nofpu")) {
procp->has_fpu = false;
lex_get(T_S_Colon);
} else
#ifdef NIAGARA2
if (streq(lex.strp, "crypto_synchronous")) {
procp->crypto_synchronous = !!parse_number_assign();
} else
#endif /* NIAGARA2 */
#if ERROR_INJECTION /* { */
if (streq(lex.strp,"error")) {
parse_error(domainp);
} else
#endif /* ERROR_INJECTION } */
#if PERFORMANCE_CHECK /* { */
if (streq(lex.strp,"exit_at")) {
procp->proc_debug.exit_at = parse_number_assign();
} else
if (streq(lex.strp,"perf_cycle_gap")) {
procp->proc_debug.perf_cycle_gap = parse_number_assign();
} else
#endif /* PERFORMANCE_CHECK } */
if (streq(lex.strp, "debug_hook")) {
if (debug_hook_defined)
lex_fatal("Cannot have more than one debug_hook definition in conf file");
else
debug_hook_defined = true;
parse_debug_hook(procp);
} else
#if ERROR_TRAP_GEN /* { */
if (streq(lex.strp,"error_asi")) {
ss_error_asi_parse(procp, false);
} else
if (streq(lex.strp,"error_event")) {
ss_error_event_parse(procp, false);
} else
if (streq(lex.strp,"error_reload_file_name")) {
ss_error_parse_filename(procp);
} else
#endif /* ERROR_TRAP_GEN } */
if (ss_parse_proc_entry(procp, domainp) == true) {
/* Handled processor specific token */
} else
lex_fatal("unknown token %s in cpu specification", lex.strp);
} while (1);
lex_unget(); /* put the r brace back for the caller ! FIXME */
#if ERROR_INJECTION
dump_errconf(domainp->errlistp);
#endif
/*
* Round out with values that didn't get set
* during parsing.
*/
if (procp->clkfreq == 0) lex_fatal("clkfreq not specified in processor definition");
/* nvthreads and ncores are for backward compatibility */
if (core_mask == 0) {
if (ncores == 0 && nvthreads == 0 ) lex_fatal("core_mask not specified in processor definition");
if (ncores == 0) lex_fatal("ncores not specified in processor definition");
if (ncores > CORESPERCHIP) lex_fatal("ncores must be <= %d", CORESPERCHIP);
if (nvthreads == 0) lex_fatal("nvthreads not specified in processor definition");
if (nvthreads > STRANDSPERCORE) lex_fatal("nvthreads must be <= %d", STRANDSPERCORE);
core_mask = (1 << nvthreads) - 1;
for (ncores--; ncores > 0 ; ncores--)
core_mask |= core_mask << STRANDSPERCORE;
} else {
if (ncores != 0 || nvthreads != 0)
lex_fatal("nvthreads/ncores should not be present when using core_mask");
/* check for non-existant strands */
if ((core_mask & VALID_CORE_MASK) != core_mask)
lex_fatal("core_mask has bits for non-existant strands");
}
procp->core_mask = core_mask;
if (procp->nwins != (SS_VER_MAXWIN + 1))
warning("nwins set to non-standard value for this processor");
if (procp->maxtl != SS_VER_MAXTL)
warning("maxtl set to non-standard value for this processor");
if (procp->nglobals != (SS_VER_MAXGL + 1))
warning("nglobals set to non-standard value for this processor");
if (procp->ver == 0)
procp->ver = SS_MAKE_VER_UPPER40(manuf, impl, mask);
procp->ver |= SS_MAKE_VER_LOWER24(procp->nglobals, procp->maxtl,
procp->nwins);
/*
* So now we know we got a specific chip (Niagara or Rock for example) in the domain,
* we create the pseudo physical IO devices that this chip has for it's control registers.
* For things like the clock unit and memory controllers etc.
*/
ss_setup_pseudo_devs(domainp, procp);
}
/*
* Parse a TLB description for either the
* I or D TLB for the proc.
*/
void ss_parse_tlb(ss_tlb_spec_t * tlbspecp)
{
/* just swallow for the moment */
lex_get(T_L_Brace);
do {
lexer_tok_t tok;
tok = lex_get_token();
if (tok == T_EOF) lex_fatal("unexpected end of file parsing SunSPARC CPU tlb spec");
if (tok == T_R_Brace) break;
if (tok != T_Token) lex_fatal("token expected in SunSPARC processor tlb specification");
if (streq(lex.strp,"entries")) {
uint_t num;
num = (uint_t)parse_number_assign();
if (num<1) lex_fatal("TLB requires at least one entry");
if (num>4096) lex_fatal("More than 4096 entries is not allowed");
tlbspecp->nentries = num;
} else if (streq(lex.strp,"parity")) {
tlbspecp->parity = true;
lex_get(T_S_Colon);
} else
lex_fatal("unknown token %s in cpu TLB specification", lex.strp);
} while (1);
}
static bool_t
ss_check_vahole(simcpu_t* sp, tvaddr_t pc)
{
#if /* defined(NIAGARA1) || */ defined(NIAGARA2) /* { */
if (VA48(pc) != pc) {
sparcv9_cpu_t * v9p;
ss_strand_t * nsp;
v9p = (sparcv9_cpu_t *)(sp->specificp);
nsp = v9p->impl_specificp;
SET_DTLB_FAULT( nsp, VA48(pc) );
v9p->post_precise_trap(sp, (sparcv9_trap_type_t) N2_trap_mem_address_range);
return true;
}
#endif /* } */
return false;
}
#if WALL_TIME /* { */
hrtime_t base_hrtime;
/*ARGSUSED*/
static void
init_tick_counter_scale(tick_counter_t *tcp, uint64_t freq)
{
if (!options.walltime)
return;
/* We convert gethrtime() to the simulated tick_counter frequency. */
tcp->scale = (double)freq / (double)NANOSEC;
tcp->scale_recip = 1.0 / tcp->scale;
if (base_hrtime == 0)
base_hrtime = RAW_HRTIME;
}
#endif /* } */
static uint_t
count_bits(uint64_t m)
{
uint_t n;
for (n = 0; m != 0; m &= m-1)
n++;
return n;
}
static uint_t
count_cores(uint64_t m)
{
uint_t n;
for (n = 0; m != 0; m >>= STRANDSPERCORE)
n++;
return n + 1;
}
/*
* Initialise the processor after parsing is complete
* In this case we create a number of SPARC v9 sparccpu_t
* one per strand available in this processor.
*/
void ss_init(domain_t * domainp, config_proc_t * config_procp)
{
extern void debug_init(simcpu_t *sp);
uint_t ncpus;
ss_proc_t * pnp;
uint_t idx, core, vthread;
uint_t vcore_id;
uint64_t core_avail = 0, core_running = 0;
ss_tlb_t * dtlbp, * itlbp, * tlbp;
sparcv9_cpu_t * tick_v9p = NULL;
uint_t ncores;
uint64_t tmp_core_mask;
pnp = (ss_proc_t*)(config_procp->procp);
/*
* OK for each core and vthread create a sparc v9 CPU
* with appropriate call backs to the SunSPARC CPU module for
* CPU specific components.
*/
/*
* FIXME: due to core indexing of some arrays, we get the
* max core number plus one.
*/
ncores = count_cores(pnp->core_mask);
ncpus = count_bits(pnp->core_mask);
pnp->nstrands = ncpus;
pnp->strand = Xcalloc(ncpus, sparcv9_cpu_t *);
/* Note: allocate actual structs - NOT pointer list */
pnp->ss_strandp = Xcalloc(ncpus, ss_strand_t);
/* Init HW strand# to ss_strandp[]/starnd[] index table */
for (idx = 0; idx < STRANDS_PER_CHIP; idx++)
pnp->str_to_idx[idx] = NO_STRAND;
#ifdef ROCK /* { */
ASSERT(ncpus <= STRANDS_PER_CHIP);
/*
* Rock has only one Unified (IMMU and DMMU) TLB per chip.
*/
pnp->tlbp = Xcalloc(1, ss_tlb_t);
tlbp = pnp->tlbp;
ss_tlb_init(tlbp, pnp->tlbspec.nentries);
tlbp->parity = pnp->tlbspec.parity;
tlbp->lru_array = Xcalloc(ROCK_TLB_SETS, uint8_t);
/*
* Create Reverse Mapped Table for Rock interrupts
*/
pnp->rmt_basep = Xcalloc(STRANDS_PER_CHIP, uint64_t);
/* FIXME: set to false by reset, true by running 0->non-0 */
pnp->sp_read_dis = true;
pnp->sp_write_dis = true;
#endif /* } */
pnp->ndtlb = ncores;
pnp->nitlb = ncores;
pnp->dtlbp = Xcalloc(pnp->ndtlb, ss_tlb_t);
pnp->itlbp = Xcalloc(pnp->nitlb, ss_tlb_t);
pnp->icachep = Xcalloc(ncores, ss_l1_cache_t);
pnp->dcachep = Xcalloc(ncores, ss_l1_cache_t);
#if INTERNAL_BUILD /* { */
#ifdef NIAGARA1
pnp->mod_arith_p = (mod_arith_t *) Xcalloc(ncores, mod_arith_t);
(void) pthread_mutex_init(&pnp->mod_arith_p->lock, NULL);
#endif
#ifdef NIAGARA2
pnp->stream_cwq_p = (asi_stream_CWQ_t *) Xcalloc(ncores, asi_stream_CWQ_t);
pnp->mod_arith_p = (asi_stream_MA_t *) Xcalloc(ncores, asi_stream_MA_t);
(void) pthread_mutex_init(&pnp->stream_cwq_p->cwq_mtx, NULL);
(void) pthread_cond_init(&pnp->stream_cwq_p->cwq_cv, NULL);
(void) pthread_mutex_init(&pnp->mod_arith_p->ma_mtx, NULL);
(void) pthread_cond_init(&pnp->mod_arith_p->ma_cv, NULL);
#endif
#endif /* INTERNAL_BUILD } */
#ifdef NIAGARA2
/* Initialised to zeros by default */
pnp->sparc_power_mgmtp = Xcalloc(ncores, sparc_power_mgmt_t);
#endif
#if ERROR_INJECTION /* { */
pnp->pend_errlistp = domainp->errlistp;
pthread_mutex_init(&pnp->err_lock, NULL);
pnp->errorp = Xcalloc(1, error_proc_t);
pnp->error_check = false;
#endif /* } */
#ifdef ROCK /* { */
pthread_mutex_init(&pnp->ucr_lock, NULL);
pthread_mutex_init(&pnp->tw_lock, NULL);
#endif /* } */
#if ERROR_TRAP_GEN /* { */
ss_error_trap_proc_init(config_procp);
#endif /* } ERROR_TRAP_GEN */
#if defined(ROCK) /* { */
tick_v9p = NULL;
#endif /* } */
tmp_core_mask = pnp->core_mask;
idx = 0;
for (core=0; core < CORESPERCHIP; core++,
tmp_core_mask >>= STRANDSPERCORE) {
ss_l1_cache_t * icachep, * dcachep;
/* skip cores with no strands */
if ((tmp_core_mask & ((1u << STRANDSPERCORE) - 1)) == 0)
continue;
/* First init the TLB structures each core uses */
itlbp = &(pnp->itlbp[core]);
dtlbp = &(pnp->dtlbp[core]);
ss_tlb_init(itlbp, pnp->itlbspec.nentries);
itlbp->parity = pnp->itlbspec.parity;
ss_tlb_init(dtlbp, pnp->dtlbspec.nentries);
dtlbp->parity = pnp->dtlbspec.parity;
icachep = &(pnp->icachep[core]);
dcachep = &(pnp->dcachep[core]);
ss_l1_cache_init(icachep, SS_ICACHE_SIZE, true);
ss_l1_cache_init(dcachep, SS_DCACHE_SIZE, false);
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
tick_v9p = NULL;
#endif /* } */
for (vthread=0; vthread < STRANDSPERCORE; vthread++) {
sparcv9_cpu_t * v9p;
ss_strand_t * nsp;
simcpu_t * sp;
/* skip missing strands */
if ((tmp_core_mask & (1u << vthread)) == 0)
continue;
/* Need something here to determine which
* scheduler wheel the simcpu_t ends up on.
*/
v9p = sparcv9_cpu_alloc(domainp,
config_procp,
pnp->nwins, pnp->nglobals, pnp->maxtl,
pnp->ver, pnp->has_fpu,
pnp);
pnp->strand[idx] = v9p;
/*
* Tie to SunSPARC CPU specific info
*/
nsp = &(pnp->ss_strandp[idx]);
sp = v9p->simp;
#if 0
EXEC_WARNING(("ss_init: core: %u strand: %u idx: %u nsp: %p sp: %p", core, vthread, idx, nsp, sp));
#endif
v9p->impl_specificp = nsp;
v9p->read_state_reg = ss_read_state_reg;
v9p->write_state_reg = ss_write_state_reg;
v9p->read_priv_reg = ss_read_priv_reg;
v9p->write_priv_reg = ss_write_priv_reg;
v9p->read_hyp_priv_reg = ss_read_hyp_priv_reg;
v9p->write_hyp_priv_reg = ss_write_hyp_priv_reg;
v9p->check_vahole = ss_check_vahole;
v9p->done_retry = ss_done_retry;
v9p->jpriv = ss_jpriv;
v9p->asi_access = ss_asi_access;
v9p->post_precise_trap = ss_post_precise_trap;
/*
* Initialize shared tick/stick register pointers.
*/
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
if (tick_v9p == NULL) {
tick_v9p = v9p;
#if WALL_TIME /* { */
init_tick_counter_scale(&tick_v9p->_tick,
pnp->clkfreq);
#endif /* } */
}
/* STICK is an alias of TICK */
v9p->stick = v9p->tick = &tick_v9p->_tick;
#elif ROCK /* } { */
if (tick_v9p == NULL) {
tick_v9p = v9p;
#if WALL_TIME /* { */
init_tick_counter_scale(&tick_v9p->_tick,
pnp->clkfreq);
init_tick_counter_scale(&tick_v9p->_stick,
domainp->sysclkfreq);
#endif /* } */
}
v9p->tick = &tick_v9p->_tick;
v9p->stick = &tick_v9p->_stick;
#else /* } { */
#error "No valid processor defined."
#endif /* } */
v9p->tick_cmpr.counter = v9p->tick;
v9p->stick_cmpr.counter = v9p->stick;
v9p->hstick_cmpr.counter = v9p->stick;
sp->cycle_target_match = ss_cycle_target_match;
/*
* determine virtual core ID
*/
nsp->core = core;
nsp->vthread = vthread;
vcore_id = (core << SS_COREID_SHIFT) | vthread;
nsp->vcore_id = vcore_id;
pnp->str_to_idx[vcore_id] = idx;
core_avail |= 1ull << vcore_id;
/*
* CMP states that the only the lowest numbered
* strand starts executing.
*/
if (core_running == 0)
core_running |= 1ull << vcore_id;
#ifdef ROCK_FAKE_SP /* { */
if (pnp->config_procp->domainp->systemp->fake_sp) {
/*
* SP initializes the current strand id
* in a hyper scratch pad register.
* Legion temporarily handles this job
* by combining vcore_id and chip_id
* in HSCRATCH0 register.
*/
nsp->strand_reg[SSR_HSCRATCHPAD_INDEX] =
vcore_id |
(pnp->fake_chip_id << RK_CHIP_ID_SHIFT);
DBGSCRATCH( lprintf(sp->gid, "HSCRATCH0 init 0x%llx\n",
nsp->strand_reg[SSR_HSCRATCHPAD_INDEX]); );
}
#endif /* } */
/*
* set up the execution instruction cache for it
*/
sp->xicachep = xicache_alloc(sp);
sp->xic_miss = ss_xic_miss;
/*
* Now set up the execution data cache ..
*/
sp->xdc.miss = ss_xdc_miss;
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
/*
* Init internal strand control registers
*/
nsp->lsu_control_raw = LSU_CTRL_INITVAL;
nsp->dmmu.enabled = (nsp->lsu_control_raw & LSU_CTRL_DMMU_EN) != 0;
nsp->immu.enabled = (nsp->lsu_control_raw & LSU_CTRL_IMMU_EN) != 0;
/*
* Init the strand's MMU ...
*/
nsp->dmmu.is_immu = false;
nsp->immu.is_immu = true;
#elif ROCK /* } { */
/*
* Init internal strand control registers
*/
nsp->mmu.dmmu_enabled = false;
nsp->mmu.immu_enabled = false;
/*
* This is_immu field gets updated each time before it is used, but
* just so that we have a starting point...
*/
nsp->mmu.is_immu = false;
nsp->tlbp = tlbp;
tlbp->shares++;
/*
* Initialize some Transactional Memory stuff.
* N.B. tm_chkpt==NULL is taken to mean that this implementation
* does not support chkpt instruction.
* Similar assumption is made with respect to commit instruction.
*/
nsp->tm_chkpt = ss_tm_chkpt;
nsp->tm_commit = ss_tm_commit;
nsp->tm_fail = ss_tm_fail;
TM_SET_INACTIVE(nsp);
/*
* TM is disabled by default in Legion. This is
* enabled by hypervisor, dumbreset (reset.s)
*/
pnp->dcucr[core] = 0;
nsp->tm_enabled = false;
nsp->cps = 0;
/* Floating Point Arithmetic Enable */
nsp->fpae = false;
pthread_mutex_init(&nsp->ifucr_lock, NULL);
#else /* } { */
#error "No valid processor defined."
#endif /* } */
/*
* Other misc stuff ...
*/
nsp->dtlbp = dtlbp;
dtlbp->shares++;
nsp->itlbp = itlbp;
itlbp->shares++;
nsp->icachep = icachep;
nsp->dcachep = dcachep;
#if ERROR_INJECTION /* { */
if (pnp->pend_errlistp)
sp->error_priv = pnp->pend_errlistp->priv;
#endif /* } */
#if PERFORMANCE_CHECK /* { */
/* FIXME: HACK TO GO AWAY */
sp->last_hrtime = gethrtime();
sp->total_hrtime = 0;
sp->perf_target = pnp->proc_debug.perf_cycle_gap;
sp->xdc_hits = 0;
sp->xdc_misses = 0;
sp->xdc_flushes = 0;
sp->xic_hits = 0;
sp->xic_misses = 0;
sp->xic_flushes = 0;
#endif /* } */
/*
* Per-thread performance counter
* initialization
*/
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
nsp->pic0 = 0;
nsp->pic1 = 0;
nsp->pcr = 0LL;
RESET_PIC_SAMPLE_BASES(sp, nsp, v9p);
#endif /* } */
/*
* Initialise with power on reset
*/
nsp->pending_precise_tt = SS_trap_NONE;
nsp->pending_async_tt = SS_trap_NONE; /* FIXME ? */
#if defined(NIAGARA2) || defined(ROCK) /* { */
/*
* Niagara2 calculates the CMP virtual CORE_ID[5:0]
* and CORE_AVAIL[63:0] (physical core id in [5:3]
* and strand id in [2:0]).
*
* for example, an N2 with 4 physical cores and 2 strands per core,
* the virtual CORE_ID for the 8 strands would be numbered as
* 0,1,8,9,16,17,24,25, and the CORE_AVAIL would be set to 0x3030303.
*
* Rock contains up to 16 cores (micro cores). Each
* contains up to 2 strands. A config file specifies
* 4 cores 1 thread would have strand ids numbered as
* 0,2,4,6.
*/
/*
* for Niagara 2, CPU's initial state is determined
* by the initial value of asi_core_running_status
* register
*/
if (((1ull << vcore_id) & core_running) == 0) {
SET_PARKED(sp);
}
DBG( lprintf(sp->gid, "cpu0x%llx (%s)\n",
nsp->vcore_id,
RUNNABLE(sp) ?"Unparked":"Parked"); );
#endif /* } */
#if defined(NIAGARA1) /* { */
/*
* For Niagara, the CPU's initial state is that only one thread
* will be running, the other threads are put in the idle state
* and are then parked.
* The Reset code for Niagara expects that only one cpu (can be
* any one) will sign-on after a POR so we just choose the first
* cpu (sp->gid ==0).
*
* Its like 'Highlander' - there can be only one !
*/
if (sp->gid == 0) {
SET_THREAD_STS_SFSM(pnp, nsp, THREAD_STS_TSTATE_RUN);
CLR_PARKED(sp);
} else {
SET_THREAD_STS_SFSM(pnp, nsp, THREAD_STS_TSTATE_IDLE);
SET_PARKED(sp);
}
DBG( lprintf(sp->gid, "cpu0x%llx "
"sfsm_state = 0x%llx (%s)\n",
nsp->vcore_id, pnp->sfsm_state[nsp->vcore_id],
RUNNABLE(sp) ?"Unparked":"Parked"); );
#endif /* } NIAGARA1 */
debug_init(sp);
#if ERROR_TRAP_GEN /* { */
ss_error_trap_strand_init(config_procp, sp);
#endif /* } ERROR_TRAP_GEN */
/* fake up a start of execution */
sp->config_procp->proc_typep->exec_setup(sp);
/* do the reset */
ss_reset_trap( sp, SS_trap_power_on_reset );
/* cleanup after "execution" */
sp->config_procp->proc_typep->exec_cleanup(sp);
idx ++;
} /* for vtrhead */
} /* for core */
#ifdef NIAGARA1 /* { */
/*
* For IOB: */
pnp->core_avail = core_avail;
#endif /* } */
#ifdef NIAGARA2 /* { */
/*
* Initialize CMP registers, each one is shared by all virtual cores.
*
* Implementation note on the storage:
*
* - asi_cmp_core_intr_id & asi_cmp_core_id are not stored internally
* as they can be calculated on the fly
*
* - cmp_regs.core_enable_status stores
*
* asi_core_available
* asi_core_enable_status
* asi_core_enable
*
* - cmp_regs.core_running_status stores
*
* asi_core_running_rw
* asi_core_running_w1s
* asi_core_running_w1c
* asi_core_running_status
*
*/
pnp->cmp_regs.core_enable_status = core_avail;
pnp->cmp_regs.xir_steering = core_avail;
pnp->cmp_regs.tick_enable = false;
pnp->cmp_regs.core_running_status = core_running;
/* Also init tick_stop flag and its lock */
pnp->tick_stop = true;
pthread_mutex_init(&pnp->tick_en_lock, NULL);
#endif /* } */
#ifdef ROCK_FAKE_SP /* { */
/*
* Revisit it when SP can provide data for these registers.
*/
pnp->cmp_regs.strand_available = core_avail;
if (simstatus.sp.mode != true) {
pnp->cmp_regs.strand_enable = core_avail;
pnp->cmp_regs.strand_running = core_running;
pnp->cmp_regs.strand_running_status = core_running;
} else {
pnp->cmp_regs.strand_enable = 0;
pnp->cmp_regs.strand_running = 0;
ss_change_exec_state(pnp, 0);
pnp->cmp_regs.strand_running_status = 0;
}
#endif /* } */
#ifdef NIAGARA1 /* { */
pthread_mutex_init(&pnp->thread_sts_lock, NULL);
#endif /* } */
#if defined(NIAGARA2) || defined(ROCK) /* { */
pthread_mutex_init(&pnp->cmp_lock, NULL);
#endif /* } */
#ifdef ROCK /* { */
pnp->sp_intr_status = 0;
pnp->spie_status = 1;
#endif /* } */
}
/*************************************************************
*
* Execution support functions (and instruction impls)
*
*************************************************************/
/*
* When execution starts some simcpu_t setup may be required
*/
void ss_exec_setup(simcpu_t * sp)
{
sparcv9_cpu_t * v9p = (sparcv9_cpu_t *)(sp->specificp);
sparcv9_active_window(sp, v9p->cwp);
sparcv9_active_globals(sp, v9p->gl);
}
/*
* When execution stop some cleanup of state is required for debugger access
*/
void ss_exec_cleanup(simcpu_t * sp)
{
sparcv9_active_window(sp, -1);
sparcv9_active_globals(sp, -1);
}
/*
* Functions to dump the SunSPARC processor state
*/
void ss_dump(domain_t * domainp, config_proc_t * config_procp)
{
ss_proc_t * procp;
procp = config_procp->procp;
pi("\t// processor node id %u\n", config_procp->proc_id);
pi("processor \"%s\" {\n", config_procp->proc_typep->proc_type_namep);
dumpinfo.indent++;
pi("clkfreq 0x%llx ;\n", procp->clkfreq);
pi("core_mask 0x%llx ;\n", procp->core_mask);
pi("nwins %u ;\n", procp->nwins);
pi("maxtl %u ;\n", procp->maxtl);
pi("ver 0x%p ;\n", procp->ver);
if (!procp->has_fpu) pi("nofpu ;\n");
pi("rstv 0x%llx ;\n", procp->rstv_addr);
pi("itlb {\n");
dumpinfo.indent++;
pi("entries %u;\n", procp->itlbspec.nentries);
if (procp->itlbspec.parity)
pi("parity;\n");
dumpinfo.indent--;
pi("}\n");
pi("dtlb {\n");
dumpinfo.indent++;
pi("entries %u;\n", procp->dtlbspec.nentries);
if (procp->dtlbspec.parity)
pi("parity;\n");
dumpinfo.indent--;
pi("}\n");
#if defined(ROCK) /* Rock has a unified L2 TLB also */
pi("unified_tlb {\n");
dumpinfo.indent++;
pi("entries %u;\n", procp->tlbspec.nentries);
if (procp->tlbspec.parity)
pi("parity;\n");
dumpinfo.indent--;
pi("}\n");
#endif
dumpinfo.indent--;
pi("}\n");
}
bool_t ss_dev_mem_access(config_proc_t *config_procp, tpaddr_t addr, uint8_t *datap,
uint64_t size, dev_access_t type)
{
domain_t *domainp;
config_addr_t *config_addrp;
tpaddr_t extent;
uint8_t *bufp;
domainp = config_procp->domainp;
config_addrp = find_domain_address(domainp, addr);
if (config_addrp == NULL) {
EXEC_WARNING(("ss_dev_mem_access: physical address 0x%llx not valid", addr));
return false;
}
/*
* For now only handle cacheable device memory
*/
extent = config_addrp->config_devp->dev_typep->dev_cacheable(config_addrp, type,
addr-config_addrp->baseaddr, &bufp);
if (extent < size) {
EXEC_WARNING(("ss_dev_mem_access: physical address 0x%llx out of range 0x%llx",
addr, extent));
return false;
}
/*
* Do memory read/write between physical memory (pointed by 'bufp')
* and device memory (pointed by 'datap')
*/
switch (type) {
case DA_Load:
memcpy(datap, bufp, size);
break;
case DA_Store:
memcpy(bufp, datap, size);
break;
default:
ASSERT(0);
}
return true;
}
/*
*------------------------------------------------------------
*
* Debugger and generic interface code ..
* ... mostly vectoring into the V9 code.
*
*------------------------------------------------------------
*/
void * ss_dbgr_attach(domain_t * domainp, config_proc_t * config_procp, char * attach_strp)
{
ss_dbgr_t * ndp;
ss_proc_t * np;
np = (ss_proc_t*)(config_procp->procp);
ndp = Xcalloc(1, ss_dbgr_t);
/* For now just fake up attaching to core 0 strand 0 FIXME */
ndp->domainp = domainp;
ndp->config_procp = config_procp;
ndp->core = 0;
ndp->strand = 0;
ndp->strandp = np->strand[0];
return (void*)ndp;
}
void ss_dbgr_detach(void * ptr)
{
ss_dbgr_t * ndp = (ss_dbgr_t*)ptr;
/* Clean up SunSPARC specific debugger state FIXME */
Xfree(ndp);
}
/*
* read internal register ...
* ... returns false on failure ...
* should we specific regname, and not a number ? FIXME
*/
bool_t ss_dbgr_regread(void * ptr, uint_t regnum, uint64_t * valp)
{
ss_dbgr_t * ndp = (ss_dbgr_t *)ptr;
sparcv9_cpu_t * sp = ndp->strandp;
/* Check for any SunSPARC specific regs first */
return sparcv9_regread(sp, regnum, valp);
}
/* returns false on failure */
bool_t ss_dbgr_regwrite(void * ptr, uint_t regnum, uint64_t val)
{
ss_dbgr_t * ndp = (ss_dbgr_t *)ptr;
sparcv9_cpu_t * sp = ndp->strandp;
/* Check for any SunSPARC specific regs first */
return sparcv9_regwrite(sp, regnum, val);
}
uint64_t ss_dbgr_mem_read(void * ptr, tvaddr_t vaddr, bool_t do_translate, uint8_t * bufp, uint64_t len)
{
ss_dbgr_t * ndp = (ss_dbgr_t *)ptr;
return ss_cpu_mem(
ndp->domainp,
ndp->config_procp->procp,
ndp->strandp,
NA_mem_read,
vaddr, do_translate,
bufp, len);
}
uint64_t ss_dbgr_mem_write(void * ptr, tvaddr_t vaddr, bool_t do_translate, uint8_t * bufp, uint64_t len)
{
ss_dbgr_t * ndp = (ss_dbgr_t *)ptr;
return ss_cpu_mem(
ndp->domainp,
ndp->config_procp->procp,
ndp->strandp,
NA_mem_write,
vaddr, do_translate,
bufp, len);
}
uint64_t ss_dbgr_mem_clear(void * ptr, tvaddr_t vaddr, bool_t do_translate, uint64_t len)
{
ss_dbgr_t * ndp = (ss_dbgr_t *)ptr;
return ss_cpu_mem(
ndp->domainp,
ndp->config_procp->procp,
ndp->strandp,
NA_mem_clear,
vaddr, do_translate,
(void*)0, len);
}
uint64_t
ss_cpu_mem(
domain_t * domainp,
void *procp_param,
sparcv9_cpu_t * strandp,
dbgr_mem_op_t memop,
tvaddr_t addr,
bool_t do_translate,
uint8_t * bp,
uint_t len)
{
uint64_t tx_count;
uint64_t tx_size;
uint64_t tx_left;
uint8_t * bufp;
uint64_t size;
tvaddr_t va;
ss_proc_t *procp = (ss_proc_t *)procp_param;
va = addr;
bufp = (uint8_t*)bp;
tx_size = len;
tx_left = len;
/*
* We keep going until either there is no
* translation available, or the target memory
* device fails us.
* *valid_lenp tells us how much succeeded
*/
for (tx_count = 0LL; tx_count < tx_size; tx_count += size) {
tpaddr_t pa;
uint64_t vlen;
pa = (tpaddr_t)va;
size = tx_left;
/* insane max !! */
if (size >= (1LL<<30)) size = 1LL<<30;
/* First translate if necessary vtop */
if (do_translate) {
if (!ss_vtop_translate(procp, strandp,
va, &pa, &vlen)) return false;
if (size > vlen) size = vlen;
}
/* Note: if cpu had virtual caches we'd have
* to pass in VA here too
*/
size = ss_pmem_op(domainp, procp, strandp, memop, pa, bufp, size);
if (size == 0) break;
bufp += size;
tx_left -= size;
va += size;
}
/* returns the amount successfully handled - 0 if failed */
return tx_count;
}
/*
* Handles the V to P conversion for us for a given strand.
* Of course = 1:1 if in hypervisor mode. FIXME
*/
static bool_t ss_vtop_translate( ss_proc_t * np, sparcv9_cpu_t * strandp, tvaddr_t va, tpaddr_t *pap, tvaddr_t * vlenp)
{
/* HACK FOR NOW - FIXME */
*pap = va;
*vlenp = 0x10000-(va & 0xffff); /* round to end of 64K page ;-) */
return true;
}
static tpaddr_t ss_pmem_op(
domain_t * domainp,
ss_proc_t * procp,
sparcv9_cpu_t * strandp,
dbgr_mem_op_t memop,
tpaddr_t pa,
uint8_t * bufp,
tpaddr_t size )
{
config_addr_t *config_addrp;
tpaddr_t offset;
tpaddr_t extent;
uint8_t * cbp;
/*
* Find the actual domain device
*/
config_addrp = find_domain_address(domainp, pa);
if (config_addrp == NULL) return false;
/*
* For now only handle cacheable device memory
*/
extent = config_addrp->config_devp->dev_typep->dev_cacheable(config_addrp, DA_Other,
pa-config_addrp->baseaddr, &cbp);
if (extent == 0) return 0;
if (size > extent) size = extent;
/*
* Handle all the appropriate niagaraness here for the cache
* models we have : FIXME
*/
/* operate on the cacheable state */
switch (memop) {
case NA_mem_read:
memcpy( bufp, cbp, size);
break;
case NA_mem_write:
memcpy( cbp, bufp, size);
break;
case NA_mem_clear:
memset( cbp, 0, size);
break;
}
return size;
}
/*
*------------------------------------------------------------
* Breakpoint support
*------------------------------------------------------------
*/
void ss_dbgr_set_break(void * ptr, tvaddr_t bpaddr)
{
ss_dbgr_t * ndp = (ss_dbgr_t *)ptr;
sparcv9_cpu_t * sp = ndp->strandp;
/* Check for any SunSPARC specific regs first */
sparcv9_set_break(sp, bpaddr);
}
void ss_dbgr_clear_break(void * ptr, tvaddr_t bpaddr)
{
ss_dbgr_t * ndp = (ss_dbgr_t *)ptr;
sparcv9_cpu_t * sp = ndp->strandp;
/* Check for any SunSPARC specific regs first */
sparcv9_clear_break(sp, bpaddr);
}
/*------------------------------------------------------------*
*
* Interrupt support
*
*------------------------------------------------------------*/
/*
* Typically called after any processor state change,
* or interrupt operation.
* The function will post and call attention to any pending
* device interrupts that may be legitimately delivered
* given the current processor state.
*
* Note that a trap is not invoked here - just the indication
* that the simcpu should pay attention for one
*/
void ss_check_interrupts(simcpu_t *sp)
{
sparcv9_cpu_t * v9p;
ss_strand_t * nsp;
v9p = (sparcv9_cpu_t *)(sp->specificp);
nsp = v9p->impl_specificp;
/* OK - if interrupts enabled, can at least trigger timer interrupts */
/* FIXME: These trap posts should be in the trap management code */
/* where priorities are correctly assigned */
/* NOTE:
* Do not put returns in this function .. all possible traps should be
* posted. The post_precise_trap function correctly determines priority.
* Also, just because a trap is posted here not not mean that that will be the
* next trap taken.
*/
switch (v9p->state) {
case V9_User:
case V9_Priv:
/* hstick traps cant be blocked except at hpriv level */
if (v9p->hstick_cmpr.pending) {
v9p->post_precise_trap(sp, (sparcv9_trap_type_t)SS_trap_hstick_match);
}
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
/* Device interrupts to hypervisor are not blockable in user/priv mode */
/* vector should not need a lock for test ... */
if (nsp->irq_vector) {
v9p->post_precise_trap(sp, (sparcv9_trap_type_t)SS_trap_interrupt_vector_trap);
}
#endif /* } */
#ifdef ROCK /* { */
/*
* these interrupts are non-maskable in user/priv mode
*/
if (nsp->flag_queue_irq[RK_Q_hpriv_0]) {
v9p->post_precise_trap(sp, (sparcv9_trap_type_t)RK_trap_hyperprivileged_queue_0);
}
if (nsp->flag_queue_irq[RK_Q_hpriv_1]) {
v9p->post_precise_trap(sp, (sparcv9_trap_type_t)RK_trap_hyperprivileged_queue_1);
}
#endif /* } */
if (v9p->pstate.int_enabled) {
if (nsp->flag_queue_irq[NA_Q_mondo]) {
v9p->post_precise_trap(sp, (sparcv9_trap_type_t)SS_trap_cpu_mondo_trap);
}
if (nsp->flag_queue_irq[NA_Q_device]) {
v9p->post_precise_trap(sp, (sparcv9_trap_type_t)SS_trap_dev_mondo_trap);
}
if (nsp->flag_queue_irq[NA_Q_resumable]) {
v9p->post_precise_trap(sp, (sparcv9_trap_type_t)SS_trap_resumable_error);
}
/*
* non-resumable error queue is not checked by HW on niagara
* only delivered by hypervisor
*/
if ((v9p->stick_cmpr.pending || v9p->tick_cmpr.pending) && (v9p->pil < 14)) {
v9p->post_precise_trap(sp, Sparcv9_trap_interrupt_level_e);
}
if ((v9p->softint >> (v9p->pil +1)) != 0) {
uint_t i;
for (i=15; i > v9p->pil; i--) {
if ((v9p->softint>>i)&1LL) {
v9p->post_precise_trap(sp, Sparcv9_trap_interrupt_level_1+i-1);
goto softint_caught;
}
}
fatal("Internal error: softint triggered, but not found (softint=0x%llx, pil=0x%x)",
(uint64_t)v9p->softint, v9p->pil);
softint_caught:;
}
}
break;
case V9_HyperPriv:
/* All disrupting traps are blockable in hpriv mode */
if (v9p->pstate.int_enabled) {
#ifdef ROCK /* { */
if (nsp->flag_queue_irq[RK_Q_hpriv_0]) {
v9p->post_precise_trap(sp, (sparcv9_trap_type_t)RK_trap_hyperprivileged_queue_0);
}
if (nsp->flag_queue_irq[RK_Q_hpriv_1]) {
v9p->post_precise_trap(sp, (sparcv9_trap_type_t)RK_trap_hyperprivileged_queue_1);
}
#endif /* } */
if (v9p->hstick_cmpr.pending) {
v9p->post_precise_trap(sp, (sparcv9_trap_type_t)SS_trap_hstick_match);
}
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
if (nsp->irq_vector) {
v9p->post_precise_trap(sp, (sparcv9_trap_type_t)SS_trap_interrupt_vector_trap);
}
#endif /* } */
}
break;
case V9_RED:
if (v9p->pstate.int_enabled) FIXME_WARNING(("Interrupts enabled in RED state not implemented!"));
break;
default:
ASSERT(0); /* must be in user, priv or H priv states here */
}
#if ERROR_TRAP_GEN /* { */
ss_check_error_traps(sp);
#endif /* } ERROR_TRAP_GEN */
}
/*
* Very similar to check_interrupts, but only ever called
* from the main execution loop when attention is called,
* and the async_event flag is set.
* This routine is responsible for posting a trap if an
* externally generated (i.e. not by this executing thread)
* asynchronous event has happened, and as a result the
* changed cpu state results in a trap ...
* .... however, we do not call post_precise_trap, since
* this may not be precise, but *especially* since that function
* causes a set_attention ... and we're already responding to that.
* Instead, if a trap is necessary we deliberately just insert the trap
* type into the pending_async_tt and set take_exception instead.
*
* By async, we mean asynchonous to the simcpu execution thread ...
*/
void ss_check_async_event(simcpu_t *sp)
{
sp->async_event = false;
ss_check_interrupts(sp);
}
/*
* This is the temporary callback function for when we get a cycle count
* match.
* The idea is that one of our cycle counters has reached its target value
* and so we go off to set the appropriate interrupt flags if enabled, and
* then reschedule the next target interrupt.
*/
/*
* Since the cycle_base's count at different rates, the match
* is (now >= target) && !triggered. triggered is cleared
* when *tick_cmpr is written and set on reset.
*/
static void ss_cycle_target_match(simcpu_t *sp)
{
ss_strand_t * nsp;
ss_proc_t * npp;
sparcv9_cpu_t * v9p;
simcycle_t now;
ticktarg_t now_targ;
error_conf_t * ep;
bool_t chk_intr = false;
v9p = (sparcv9_cpu_t *)(sp->specificp);
nsp = v9p->impl_specificp;
npp = (ss_proc_t *)(sp->config_procp->procp);
now = sp->cycle;
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
/*
* Check for Performance Counter match
*/
if (nsp->pcr & SS_PCR_UT_ST) {
uint32_t old_pic0, old_pic1;
uint64_t old_pcr;
old_pic0 = nsp->pic0;
old_pic1 = nsp->pic1;
UPDATE_PIC(sp, nsp, v9p);
old_pcr = nsp->pcr;
/* approximate overflow detection by testing for PIC decrease */
if (nsp->pic0 < old_pic0) {
DBGPIC( lprintf(sp->gid, "Performance Counter Overflow pic0 @ cycle=0x%llx," \
" pic0=0x%x, pic1=0x%x\n",
now, nsp->pic0, nsp->pic1); );
nsp->pcr |= SS_PCR_OV0; /* set OVFL */
}
if (nsp->pic1 < old_pic1) {
DBGPIC( lprintf(sp->gid, "Performance Counter Overflow pic1 @ cycle=0x%llx," \
" pic0=0x%x, pic1=0x%x\n",
now, nsp->pic0, nsp->pic1); );
nsp->pcr |= SS_PCR_OV1; /* set OVFH */
}
if (nsp->pcr != old_pcr) {
v9p->softint |= BIT(15);
DBGSOFTINT( lprintf(sp->gid,
"softint pic overflow: pc=0x%llx, o7=0x%llx, val=0x%llx, stick=%u, tick=%u\n",
sp->pc, sp->intreg[Reg_sparcv9_o7], v9p->softint, v9p->stick_cmpr.pending,
v9p->tick_cmpr.pending); );
chk_intr = true;
}
}
#endif /* } */
#if ERROR_TRAP_GEN /* { */
if (now >= sp->error_cycle) {
lprintf(sp->gid, "ERROR_TRAP_GEN: cycle target matched [0x%llx] at "\
"instn_cnt 0x%llx\n", sp->error_cycle, now);
sp->error_cycle = UINT64_MAX;
sp->error_cycle_reached = true;
}
#endif /* ERROR_TRAP_GEN } */
#if WALL_TIME
if (options.walltime)
now_targ = RAW_HRTIME;
else
#endif /* WALL_TIME */
now_targ = now;
if (!v9p->tick_cmpr.triggered && now_targ >= v9p->tick_cmpr.target) {
ASSERT( v9p->tick_cmpr.interrupt_enabled );
v9p->tick_cmpr.triggered = true;
v9p->tick_cmpr.pending = true;
chk_intr = true;
DBGSOFTINT( lprintf(sp->gid,
"softint update: pc=0x%llx, o7=0x%llx, val=0x%llx, stick=%u, tick=%u\n",
sp->pc, sp->intreg[Reg_sparcv9_o7], v9p->softint, v9p->stick_cmpr.pending,
v9p->tick_cmpr.pending); );
}
if (!v9p->stick_cmpr.triggered && now_targ >= v9p->stick_cmpr.target) {
ASSERT( v9p->stick_cmpr.interrupt_enabled );
v9p->stick_cmpr.triggered = true;
v9p->stick_cmpr.pending = true;
chk_intr = true;
DBGSOFTINT( lprintf(sp->gid,
"softint update: pc=0x%llx, o7=0x%llx, val=0x%llx, stick=%u, tick=%u\n",
sp->pc, sp->intreg[Reg_sparcv9_o7], v9p->softint, v9p->stick_cmpr.pending,
v9p->tick_cmpr.pending); );
}
if (!v9p->hstick_cmpr.triggered && now_targ >= v9p->hstick_cmpr.target) {
ASSERT( v9p->hstick_cmpr.interrupt_enabled );
v9p->hstick_cmpr.triggered = true;
v9p->hstick_cmpr.pending = true;
chk_intr = true;
DBGSOFTINT( lprintf(sp->gid,
"hintp update: pc=0x%llx, o7=0x%llx, hintp=%u\n",
sp->pc, sp->intreg[Reg_sparcv9_o7],
v9p->hstick_cmpr.pending); );
}
if ((now >= options.save_restore.trigger_icount) &&
(options.save_restore.trigger_icount != 0)) {
uint64_t c;
/*
* Any cpu can hit this trigger point but we only
* want one cpu to send the SAVE_STATE trap to all
* strands in all processors.
*/
c = host_cas64(&options.save_restore.trigger_icount,
options.save_restore.trigger_icount, 0);
if (c != 0) {
int i;
domain_t *dp;
lprintf(sp->gid,
"options.save_restore.trigger_icount reached. trigger=0x%llx, icount=0x%llx\n",
c, now);
dp = sp->config_procp->domainp;
for (i=0; i<dp->procs.count; i++) {
config_proc_t * cp;
cp = LIST_ENTRY(dp->procs, i);
cp->proc_typep->ext_signal(cp, ES_LEGION_SAVE_STATE, NULL);
}
}
}
#if ERROR_INJECTION /* { */
/*
* Turn on the error_enabled flag to begin checking for error
* conditions. Flush caches so all subsequent instruction
* fetches and ld/st operations are detected by the error handling
*/
ep = NULL;
pthread_mutex_lock(&npp->err_lock);
if (npp->pend_errlistp && now == npp->pend_errlistp->cycle) {
DBGERR( lprintf(sp->gid, "ss_cycle_target_match(): error cycle\n"); );
while (npp->pend_errlistp && now == npp->pend_errlistp->cycle) {
ep = npp->pend_errlistp;
npp->pend_errlistp = ep->nextp;
if (sp->errorp->errlistp)
ep->nextp = sp->errorp->errlistp;
else
ep->nextp = NULL;
sp->errorp->errlistp = ep;
}
}
pthread_mutex_unlock(&npp->err_lock);
if (ep) {
sp->error_enabled = true;
sp->error_check = (v9p->state == sp->error_priv) ? true : false;
update_errflags(sp);
DBGERR( dump_errconf(sp->errorp->errlistp); );
DBGERR( lprintf(sp->gid, "check_xicache = %u check_xdcache = %u\n",
sp->errorp->check_xicache, sp->errorp->check_xdcache); );
sp->xicache_trans_flush_pending = true;
sp->xdcache_trans_flush_pending = true;
}
#endif /* ERROR_INJECTION } */
#if PERFORMANCE_CHECK /* { */
if (now >= sp->perf_target) {
hrtime_t hrt, gap;
double mips, amips;
hrt = gethrtime();
gap = hrt - sp->last_hrtime;
sp->total_hrtime += gap;
mips = (1.0e3*(double)(ICOUNT(sp) - sp->prev_icount)) / (double)gap;
amips =(1.0e3*(double)ICOUNT(sp)) / (double)sp->total_hrtime;
log_lock();
log_printf(sp->gid, "Simulator mips=%.2lf, average mips=%.2lf, "\
"total_time=%llu seconds, delta_time=%llu milliseconds\n",
mips, amips, HRT_TO_SEC(sp->total_hrtime), HRT_TO_MILLISEC(gap));
sp->config_procp->proc_typep->perf_dump(sp->specificp);
log_flush_unlock();
/* Don't count the time spent printing... */
sp->last_hrtime = gethrtime();
/*
* exit_at is parsed from the conf file.
* If it's non-zero, we exit the simulator when we reach or
* exceed that number of instns
*/
if ((npp->proc_debug.exit_at != 0) &&
(ICOUNT(sp) >= npp->proc_debug.exit_at)) {
fatal("Reached the value for exit_at in conf file "
"0x%llx, current icount=llu\n",
npp->proc_debug.exit_at, ICOUNT(sp));
}
#if WALL_TIME
if (options.walltime) {
sp->perf_target = now + npp->proc_debug.perf_cycle_gap;
ASSERT(sp->perf_target > sp->cycle);
} else
#endif /* WALL_TIME */
sp->perf_target += npp->proc_debug.perf_cycle_gap;
}
#endif /* PERFORMANCE_CHECK } */
ss_recomp_cycle_target(sp);
if (chk_intr)
ss_check_interrupts(sp);
}
/*------------------------------------------------------------*
*
* Privileged and status registers accessed here ...
*
*------------------------------------------------------------*/
uint64_t ss_read_pstate(sparcv9_cpu_t * v9p)
{
uint64_t pstate;
pstate = v9p->pstate.priv ? (1<<V9_PSTATE_PRIV_BIT) : 0;
pstate |= v9p->pstate.mm << V9_PSTATE_MM_BITS;
pstate |= v9p->pstate.int_enabled ? (1<< V9_PSTATE_IE_BIT) : 0;
pstate |= v9p->pstate.addr_mask ? (1<< V9_PSTATE_AM_BIT) : 0;
pstate |= v9p->pstate.fpu_enabled ? (1<< V9_PSTATE_PEF_BIT) : 0;
pstate |= v9p->pstate.tle ? (1<< V9_PSTATE_TLE_BIT) : 0;
pstate |= v9p->pstate.cle ? (1<< V9_PSTATE_CLE_BIT) : 0;
pstate |= v9p->pstate.tct ? (1<< V9_PSTATE_TCT_BIT) : 0;
return pstate;
}
static uint64_t ss_read_hpstate(sparcv9_cpu_t * v9p)
{
uint64_t hpstate;
hpstate = v9p->hpstate.hpriv ? (1<<V9_HPSTATE_HPRIV_BIT) : 0;
hpstate |= v9p->hpstate.tlz ? (1<<V9_HPSTATE_TLZ_BIT) : 0;
hpstate |= v9p->hpstate.red ? (1<<V9_HPSTATE_RED_BIT) : 0;
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
hpstate |= v9p->hpstate.ibe ? (1<<V9_HPSTATE_IBE_BIT) : 0;
#endif /* } */
#if defined(NIAGARA1)
hpstate |= (1LL<<NA_HPSTATE_ENB_BIT);
#elif NIAGARA2
/* Nothing extra for NIAGARA2 */
#elif ROCK
{
ss_strand_t * ssr = (ss_strand_t *)v9p->impl_specificp;
hpstate |= ssr->tick_npt ? (1LL<<HPSTATE_TNPT_BIT) : 0;
}
#else
#error "No valid processor defined."
#endif
return hpstate;
}
/*
* This function currently gets called by the done/retry
* implementations as well as the wrpr instruction ...
* ... we may want to settle on a distinct & more
* efficient combined version for done/rety that
* covers both pstate and hpstate -- FIXME
*/
static void ss_write_pstate(simcpu_t * sp, uint64_t val)
{
sparcv9_cpu_t * v9p;
bool_t was_enabled;
v9p = (sparcv9_cpu_t *)(sp->specificp);
v9p->pstate.tle = BIT_TEST( val, V9_PSTATE_TLE_BIT ) ? true : false;
was_enabled = v9p->pstate.cle;
v9p->pstate.cle = BIT_TEST( val, V9_PSTATE_CLE_BIT ) ? true : false;
if (was_enabled != v9p->pstate.cle)
sp->xdcache_trans_flush_pending = true;
was_enabled = v9p->fpu_on;
v9p->pstate.fpu_enabled = BIT_TEST( val, V9_PSTATE_PEF_BIT ) ? true : false;
v9p->fpu_on = v9p->pstate.fpu_enabled && v9p->fprs.fef && v9p->has_fpu;
#ifdef FP_DECODE_DISABLED
/* flush instn cache to force new decodes - thus can trap on FP instructions */
if ( v9p->fpu_on != was_enabled) {
sp->xicache_instn_flush_pending = true;
set_sync_pending(sp);
}
#endif /* FP_DECODE_DISABLED */
#ifdef ROCK
v9p->pstate.mm = (val >> V9_PSTATE_MM_BITS) & V9_PSTATE_MM_MASK; /* Ignore effects */
#endif
v9p->pstate.addr_mask = BIT_TEST( val, V9_PSTATE_AM_BIT ) ? true : false;
V9_PSTATE_AM_CHANGED(v9p);
v9p->pstate.int_enabled = BIT_TEST( val, V9_PSTATE_IE_BIT ) ? true : false;
/* Have potentially just enabled a whole bunch of disrupting traps ... check them here */
/* - handled by check interrupts call below */
v9p->pstate.tct = BIT_TEST( val, V9_PSTATE_TCT_BIT ) ? true : false;
if ( v9p->pstate.tct ) IMPL_WARNING(("wr %%pstate: TCT=1 @ pc=0x%llx - TCT not supported", sp->pc));
was_enabled = v9p->pstate.priv;
v9p->pstate.priv = BIT_TEST( val, V9_PSTATE_PRIV_BIT ) ? true : false;
if (was_enabled != v9p->pstate.priv) {
/* Change of execution state ? */
if (v9p->state!=V9_HyperPriv && v9p->state!=V9_RED) {
V9_STATE_CHANGE(sp, v9p, (v9p->pstate.priv ? V9_Priv : V9_User));
}
}
/* V9_PSTATE_CHANGED(v9p); */
ss_check_interrupts(sp); /* because of state change */
/*
* Note: if interrupts were enabled, it is the next instructions
* that would be observed as taking the trap (so we dont re-execute
* the wrpr %pstate again after the return from trap.
*/
}
/*
* This function currently gets called by the done/retry
* implementations as well as the wrpr instruction ...
* ... we may want to settle on a distinct & more
* efficient combined version for done/rety that
* covers both pstate and hpstate -- FIXME
*/
static void ss_write_hpstate(simcpu_t * sp, bool_t from_htstate, uint64_t val)
{
uint64_t hpstate;
sparcv9_cpu_t * v9p;
bool_t flag_icflush = false;
bool_t flag_dcflush = false;
bool_t was_enabled;
sparcv9_state_t new_state;
v9p = (sparcv9_cpu_t *)(sp->specificp);
v9p->hpstate.hpriv = BIT_TEST( val, V9_HPSTATE_HPRIV_BIT ) ? true : false;
v9p->hpstate.tlz = BIT_TEST( val, V9_HPSTATE_TLZ_BIT ) ? true : false;
v9p->hpstate.red = BIT_TEST( val, V9_HPSTATE_RED_BIT ) ? true : false;
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
v9p->hpstate.ibe = BIT_TEST( val, V9_HPSTATE_IBE_BIT ) ? true : false;
if (v9p->hpstate.ibe)
IMPL_WARNING(("HPSTATE.IBE not implemented (pc=0x%llx)", sp->pc));
#endif /* } */
if (v9p->hpstate.tlz)
IMPL_WARNING(("HPSTATE.TLZ not implemented (pc=0x%llx)", sp->pc));
#if defined(NIAGARA1)
if (!from_htstate && !BIT_TEST(val, NA_HPSTATE_ENB_BIT))
IMPL_WARNING(("ENB bit in hpstate cleared by executing code, but not implemented by legion (pc=0x%llx)", sp->pc));
#elif NIAGARA2
/* nothing to do */
#elif ROCK
if (1 /* !from_htstate ?? */) {
ss_strand_t * ssr = (ss_strand_t *)v9p->impl_specificp;
ssr->tick_npt = BIT_TEST( val, HPSTATE_TNPT_BIT ) ? true : false;
v9p->_tick.non_priv_trap = ssr->tick_npt;
v9p->_stick.non_priv_trap = ssr->tick_npt;
}
#else
#error "No valid processor defined."
#endif
if (v9p->hpstate.red) {
new_state = V9_RED;
} else
if (v9p->hpstate.hpriv) {
new_state = V9_HyperPriv;
} else {
new_state = v9p->pstate.priv ? V9_Priv : V9_User;
}
if (v9p->state != new_state) {
/* Have potentially just enabled a whole bunch of disrupting traps ... check them here */
/* - handled by check interrupts call below */
V9_STATE_CHANGE(sp, v9p, new_state);
ss_check_interrupts(sp); /* because of state change */
}
}
/* tick is read as a pr and asr, so make code common here */
uint64_t ss_read_tick(simcpu_t * sp)
{
sparcv9_cpu_t * v9p;
uint64_t val;
ss_proc_t * npp = (ss_proc_t *)sp->config_procp->procp;
v9p = (sparcv9_cpu_t *)(sp->specificp);
val = v9p->tick->offset + RAW_TICK(v9p);
#ifdef NIAGARA1
val &= MASK64(62, 2); /* Low bits are clear on Niagara */
#endif
#ifdef NIAGARA2 /* { */
if (npp->tick_stop)
val = v9p->tick->offset;
#endif /* } */
return val;
}
uint64_t ss_read_stick(simcpu_t * sp)
{
sparcv9_cpu_t * v9p;
uint64_t val;
ss_proc_t * npp = (ss_proc_t *)sp->config_procp->procp;
v9p = (sparcv9_cpu_t *)(sp->specificp);
val = v9p->stick->offset + RAW_STICK(v9p);
#ifdef NIAGARA1
val &= MASK64(62, 2); /* Low bits are clear on Niagara */
#endif
#ifdef NIAGARA2 /* { */
if (npp->tick_stop)
val = v9p->stick->offset;
#endif /* } */
return val;
}
#if WALL_TIME
#define CALC_TARG(_tcmp) (options.walltime ? \
(TICK_TO_HRT((_tcmp)->compare - (_tcmp)->counter->offset, \
(_tcmp)->counter->scale_recip)) : \
((_tcmp)->compare - (_tcmp)->counter->offset))
#else /* WALL_TIME */
#define CALC_TARG(_tcmp) ((_tcmp)->compare - \
(_tcmp)->counter->offset)
#endif /* WALL_TIME */
#define RECALC_TARG(_sp, _tcmp) \
(_tcmp)->target = CALC_TARG(_tcmp); \
(_tcmp)->triggered = ((_tcmp)->target <= (_sp)->cycle)
uint64_t ss_tick_cmpr_read(simcpu_t * sp, tick_compare_t *tcmp)
{
return (tcmp->interrupt_enabled ? 0ULL : (1ULL << 63)) |
tcmp->compare;
}
void ss_tick_cmpr_write(simcpu_t * sp, tick_compare_t *tcmp, uint64_t val)
{
ss_proc_t * npp;
npp = (ss_proc_t *)(sp->config_procp->procp);
tcmp->compare = val & MASK64(62,0);
tcmp->interrupt_enabled = (val>>63) ? false : true;
if (tcmp->interrupt_enabled) {
#ifdef NIAGARA2
if (!npp->tick_stop) {
#endif
RECALC_TARG(sp, tcmp);
#ifdef NIAGARA2 /* { */
} else {
/*
* If tick is stopped, no need to update tick target
* values as only sp->cycle is increasing, but
* tick is stationary, so we will not hit the
* target. The only exception to this is if,
* while the tick is stopped, either the tick
* counter or the tick_cmpr.compare is written
* to such that they are both equal. Then we
* need to have an interrupt happen right away.
*/
if (tcmp->compare == tcmp->counter->offset)
tcmp->target = sp->cycle + 1;
else
tcmp->triggered = true;
tcmp->target = 0ull;
}
#endif /* } */
} else {
/* "Don't do match compare" */
tcmp->triggered = true;
tcmp->target = 0;
}
}
/*
* Account for TICK/STICK offset changes.
* TODO: must apply to all cmprs sharing the specific TICK/STICK.
*/
void ss_recomp_tick_target(simcpu_t * sp)
{
sparcv9_cpu_t * v9p;
ss_proc_t * npp;
npp = (ss_proc_t *)(sp->config_procp->procp);
v9p = (sparcv9_cpu_t *)(sp->specificp);
#ifdef NIAGARA2
if (!npp->tick_stop) {
#endif
if (v9p->tick_cmpr.interrupt_enabled) {
RECALC_TARG(sp, &v9p->tick_cmpr);
}
if (v9p->stick_cmpr.interrupt_enabled) {
RECALC_TARG(sp, &v9p->stick_cmpr);
}
if (v9p->hstick_cmpr.interrupt_enabled) {
RECALC_TARG(sp, &v9p->hstick_cmpr);
}
#ifdef NIAGARA2 /* { */
} else {
/*
* If tick is stopped, no need to update tick target
* values as only sp->cycle is increasing, but
* tick is stationary, so we will not hit the
* target. The only exception to this is if,
* while the tick is stopped, either the tick
* counter or the tick_cmpr.compare is written
* to such that they are both equal. Then we
* need to have an interrupt happen right away.
*/
if (v9p->tick_cmpr.compare == v9p->tick_cmpr.counter->offset)
v9p->tick_cmpr.target = sp->cycle + 1;
else
v9p->tick_cmpr.target = 0ull;
if (v9p->stick_cmpr.compare == v9p->stick_cmpr.counter->offset)
v9p->stick_cmpr.target = sp->cycle + 1;
else
v9p->stick_cmpr.target = 0ull;
if (v9p->hstick_cmpr.compare == v9p->hstick_cmpr.counter->offset)
v9p->hstick_cmpr.target = sp->cycle + 1;
else
v9p->hstick_cmpr.target = 0ull;
}
#endif /* } */
DBGTICKREAD( lprintf(sp->gid,
"recomp_tick_target: pc=0x%llx, cycle=0x%llx, tick_targ=0x%llx, "
"stick_targ=0x%llx hstick_targ=0x%llx\n",
sp->pc, sp->cycle, v9p->tick_cmpr.target, v9p->stick_cmpr.target,
v9p->hstick_cmpr.target); );
ss_recomp_cycle_target(sp);
}
void ss_recomp_cycle_target(simcpu_t * sp)
{
sparcv9_cpu_t *v9p = (sparcv9_cpu_t *)(sp->specificp);
simcycle_t now;
simcycle_t old_target;
#if ERROR_INJECTION /* { */
ss_proc_t *npp;
uint64_t error_cycle;
#endif /* } */
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
ss_strand_t *nsp = v9p->impl_specificp;
#endif /* } */
now = sp->cycle;
old_target = sp->cycle_target;
sp->cycle_target = UINT64_MAX;
/* For each of the following figure out the target value - assuming an
* interrupt is enabled, then we figure out which is the nearest target
*/
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
/*
* Since the PIC implementation is not exact and the overflow traps
* are disrupting (can be blocked sometimes), just cause a check
* to happen every PIC_OVERFLOW_CHECK_INTERVAL cycles if counting is
* enabled.
*/
#define PIC_OVERFLOW_CHECK_INTERVAL 1000
if (nsp->pcr & SS_PCR_UT_ST) {
if ((now + PIC_OVERFLOW_CHECK_INTERVAL) < sp->cycle_target)
sp->cycle_target = (now + PIC_OVERFLOW_CHECK_INTERVAL);
}
#endif /* } */
/*
* WALL_TIME_TARGET_POLL_HACK can go away once there is a running MIPS
* average available to predict the right cycle_target. The gethrtime()
* function traps to the kernel, so it would be inefficient to do that
* for each instruction executed.
*/
#define WALL_TIME_TARGET_POLL_HACK 1000
#define CHECK_CMPR_TARGET(_tcmp) \
if ((_tcmp)->target > now && \
((_tcmp)->target < sp->cycle_target)) \
sp->cycle_target = options.walltime ? (sp->cycle) + WALL_TIME_TARGET_POLL_HACK : (_tcmp)->target
CHECK_CMPR_TARGET(&v9p->tick_cmpr);
CHECK_CMPR_TARGET(&v9p->stick_cmpr);
CHECK_CMPR_TARGET(&v9p->hstick_cmpr);
/* Check if save_restore trigger is the next trigger */
if ((options.save_restore.trigger_icount > now) &&
(options.save_restore.trigger_icount < sp->cycle_target))
sp->cycle_target = options.save_restore.trigger_icount;
#if PERFORMANCE_CHECK
if (sp->perf_target < sp->cycle_target)
sp->cycle_target = sp->perf_target;
#endif
#if ERROR_TRAP_GEN /* { */
if (sp->error_cycle>now && sp->error_cycle<sp->cycle_target)
sp->cycle_target = sp->error_cycle;
#endif /* ERROR_TRAP_GEN } */
#if ERROR_INJECTION /* { */
npp = (ss_proc_t *)(sp->config_procp->procp);
pthread_mutex_lock(&npp->err_lock);
if (npp->pend_errlistp)
error_cycle = npp->pend_errlistp->cycle;
else
error_cycle = 0;
pthread_mutex_unlock(&npp->err_lock);
if ((error_cycle > now) && (error_cycle < sp->cycle_target)) {
sp->cycle_target = error_cycle;
}
#endif /* } */
if (sp->cycle_target != old_target) {
if (sp->cycle_target != UINT64_MAX) {
ASSERT(sp->cycle_target >= sp->cycle);
sp->exec_loop_reset = true;
if (old_target == UINT64_MAX) {
DBGTICK( lprintf(sp->gid,
"recomp_cycle_target: pc=0x%llx, "
"cycle=0x%llx cycle_target was never\n",
sp->pc, sp->cycle); );
}
} else {
DBGTICK( lprintf(sp->gid, "recomp_cycle_target: "
"pc=0x%llx, cycle=0x%llx cycle_target set to "
"never\n", sp->pc, sp->cycle); );
}
}
DBGTICKREAD( lprintf(sp->gid,
"recomp_cycle_target: pc=0x%llx, cycle=0x%llx, "
"cycle_target-cycle=0x%llx\n",
sp->pc, sp->cycle, sp->cycle_target - sp->cycle); );
}
void ss_write_tick(simcpu_t * sp, uint64_t val)
{
sparcv9_cpu_t * v9p;
ss_proc_t * npp = (ss_proc_t *)sp->config_procp->procp;
v9p = (sparcv9_cpu_t *)(sp->specificp);
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
/* NPT bit is per-strand, stick aliases to tick */
v9p->_tick.non_priv_trap = (val >> 63) ? true : false;
v9p->_stick.non_priv_trap = v9p->_tick.non_priv_trap;
#endif /* } */
val &= 0x7fffffffffffffffLL;
v9p->tick->offset = val - RAW_TICK(v9p);
#ifdef NIAGARA2
if (npp->tick_stop)
v9p->tick->offset = val;
#endif
ss_recomp_tick_target(sp);
}
void ss_write_stick(simcpu_t * sp, uint64_t val)
{
sparcv9_cpu_t * v9p;
ss_proc_t *npp;
npp = (ss_proc_t *)(sp->config_procp->procp);
v9p = (sparcv9_cpu_t *)(sp->specificp);
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
/* NPT bit is per-strand, stick aliases to tick */
v9p->_tick.non_priv_trap = (val >> 63) ? true : false;
v9p->_stick.non_priv_trap = v9p->_tick.non_priv_trap;
#endif /* } */
val &= 0x7fffffffffffffffLL;
v9p->stick->offset = val - RAW_STICK(v9p);
#ifdef NIAGARA2
if (npp->tick_stop)
v9p->stick->offset = val;
#endif
ss_recomp_tick_target(sp);
}
static void ss_read_state_reg(simcpu_t * sp, uint_t rdest, uint_t state_reg)
{
uint64_t val;
sparcv9_cpu_t * v9p;
ss_strand_t * nsp;
v9p = (sparcv9_cpu_t *)(sp->specificp);
nsp = v9p->impl_specificp;
switch (state_reg) {
case 0x00: /* y */
#ifdef ROCK
if (!nsp->fpae) {
v9p->post_precise_trap(sp,
Sparcv9_trap_illegal_instruction);
return;
}
#endif
val = sp->v9_y;
break;
case 0x02: /* ccr */
val = sp->v9_ccr;
break;
case 0x03: /* asi */
val = sp->v9_asi;
break;
case 0x04: /* tick */
if (v9p->state == V9_User && v9p->_tick.non_priv_trap) {
v9p->post_precise_trap(sp, Sparcv9_trap_privileged_action);
return;
}
val = ss_read_tick(sp);
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
if (v9p->_tick.non_priv_trap) val |= 1ULL<<63;
#endif /* } */
DBGTICKREAD( lprintf(sp->gid,
"tick read asr: pc=0x%llx, o7=0x%llx, val=0x%llx\n",
sp->pc, sp->intreg[Reg_sparcv9_o7], val); );
break;
case 0x05: /* pc */
val = sp->pc; /* Use a macro - FIXME */
if (v9p->pstate.addr_mask) val &= MASK64(31,0); /* FIXME: SV9_ID125 */
break;
case 0x06: /* fprs */
/* Looks like that even if there is no FPU, we still need an FPRS register.
* There doesn't seem to be scope even for emulation.
*/
val = ((v9p->fprs.fef) ? (1LL << V9_FPRS_FEF_BIT) :0LL) |
(1LL << V9_FPRS_DU_BIT) | /* for now du & dl are always 1 if fef */
(1LL << V9_FPRS_DL_BIT);
DBGFPRS( lprintf(sp->gid, "ss_read_state_reg: %%fprs=%x @ pc=0x%llx\n", val, sp->pc); );
break;
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
case 0x10: /* PCR */
if (v9p->state == V9_User) {
v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode);
return;
}
val = nsp->pcr;
DBGPIC( lprintf(sp->gid, "ss_read_state_reg: (rd %%pcr @ pc=0x%llx, val=0x%llx)\n",
sp->pc, val); );
nsp->pcr &= ~SS_PCR_CLEAR_ON_READ;
break;
case 0x11: /* PIC */
/*
* Need to create a proper pic value containing [pic1][pic0] where each pic
* is a 32bit number.
*
* pic0 needs to be a 32bit Instr count based on the sp->pic0 value
* For now, pic1 can be anything as long as it's increasing
*/
if (v9p->state == V9_User && (nsp->pcr & SS_PCR_PRIV) != 0) {
v9p->post_precise_trap(sp, Sparcv9_trap_privileged_action);
return;
}
if (nsp->pcr & SS_PCR_UT_ST) {
UPDATE_PIC(sp, nsp, v9p);
}
val = PIC0_PIC1_TO_CPU_COUNTER(nsp->pic0, nsp->pic1);
DBGPIC( lprintf(sp->gid, "ss_read_state_reg: rd %%pic @ pc=0x%llx, val=0x%llx [cycle=0x%llx]\n",
sp->pc, val, sp->cycle); );
break;
#endif /* } */
case 0x13:
#ifdef ROCK
if (!nsp->fpae) {
v9p->post_precise_trap(sp,
Sparcv9_trap_illegal_instruction);
return;
}
#endif
if (!v9p->fpu_on) {
v9p->post_precise_trap(sp, Sparcv9_trap_fp_disabled);
return;
}
val = sp->v9_gsr;
break;
case 0x16: /* softint */
if (v9p->state == V9_User) {
v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode);
return;
}
val = ((v9p->stick_cmpr.pending) ? (1LL<<16) : 0LL) |
v9p->softint |
((v9p->tick_cmpr.pending) ? 1LL : 0LL );
break;
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
case 0x17: /* tick_cmpr */
if (v9p->state == V9_User) {
v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode);
return;
}
val = ss_tick_cmpr_read(sp, &v9p->tick_cmpr);
DBGTICKREAD( lprintf(sp->gid,
"tick_cmpr read priv: pc=0x%llx, o7=0x%llx, val=0x%llx\n",
sp->pc, sp->intreg[Reg_sparcv9_o7], val); );
break;
#endif /* } */
case 0x18: /* stick */
if (v9p->state == V9_User && v9p->_stick.non_priv_trap) {
v9p->post_precise_trap(sp, Sparcv9_trap_privileged_action);
return;
}
val = ss_read_stick(sp);
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
if (v9p->_stick.non_priv_trap) val |= 1ULL<<63;
#endif /* } */
DBGTICKREAD( lprintf(sp->gid,
"stick read priv: pc=0x%llx, o7=0x%llx, val=0x%llx\n",
sp->pc, sp->intreg[Reg_sparcv9_o7], val); );
break;
case 0x19: /* stick_cmpr */
if (v9p->state == V9_User) {
v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode);
return;
}
val = ss_tick_cmpr_read(sp, &v9p->stick_cmpr);
DBGTICKREAD( lprintf(sp->gid,
"stick_cmpr read priv: pc=0x%llx, o7=0x%llx, val=0x%llx\n",
sp->pc, sp->intreg[Reg_sparcv9_o7], val); );
break;
#ifdef NIAGARA1
case 0x1a: /* strand_sts_reg */
switch (v9p->state) {
case V9_User:
v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode);
return;
case V9_Priv:
/* If a strand is reading it, it must be active... */
val = THREAD_STS_ACTIVE;
IMPL_WARNING(("ss_read_state_reg: (rd %%tsr @ pc=0x%llx) deprecated access to strand status register", sp->pc));
break;
case V9_HyperPriv:
case V9_RED:
{
ss_proc_t *npp;
uint_t i, ie;
npp = (ss_proc_t *)(sp->config_procp->procp);
val = 0;
i = nsp->core * STRANDSPERCORE;
ie = i + STRANDSPERCORE;
while (i < ie) {
val <<= THREAD_STS_TSTATE_BITS;
val |= npp->sfsm_state[i++];
}
val <<= THREAD_STS_TSTATE_SHIFT;
val |= (nsp->vcore_id << THREAD_STS_SHIFT);
if (nsp->spec_en)
val |= THREAD_STS_SPEC_EN;
val |= THREAD_STS_ACTIVE;
}
break;
default:
abort();
}
DBGCMP( lprintf(sp->gid,
"%%tsr read: pc=0x%llx, o7=0x%llx, val=0x%llx\n",
sp->pc, sp->intreg[Reg_sparcv9_o7], val); );
break;
#endif
#ifdef ROCK
case 0x1c: /* cps */
val = nsp->cps;
DBGTM( lprintf(sp->gid, "%%cps read: "
"pc=0x%llx, o7=0x%llx, val=0x%llx\n",
sp->pc, sp->intreg[Reg_sparcv9_o7], val); );
break;
#endif
#if 0
/* No unimplemented registers currently. */
case 0x??:
FIXME_WARNING(("ss_read_state_reg: (rd) Unimplemented register 0x%x @ pc=0x%llx", state_reg, sp->pc));
#endif
default:
v9p->post_precise_trap(sp, Sparcv9_trap_illegal_instruction);
return;
}
if (rdest != Reg_sparcv9_g0) sp->intreg[rdest] = val; /* Use a macro - FIXME */
NEXT_INSTN(sp);
}
static void ss_write_state_reg(simcpu_t * sp, uint_t state_reg, uint64_t val)
{
sparcv9_cpu_t * v9p;
ss_strand_t * nsp;
bool_t prev_val;
v9p = (sparcv9_cpu_t *)(sp->specificp);
nsp = (ss_strand_t *)v9p->impl_specificp;
#ifdef ROCK
if (TM_ACTIVE(nsp)) {
EXEC_WARNING(("TM ERROR: TM active in 'ss_write_state_reg' function pc=0x%llx.", sp->pc));
nsp->tm_fail(sp, V9_CPS_INSTR);
return;
}
#endif
switch (state_reg) {
case 0x0: /* y */
#ifdef ROCK
if (!nsp->fpae) {
v9p->post_precise_trap(sp,
Sparcv9_trap_illegal_instruction);
return;
}
#endif
sp->v9_y = val & MASK64(31,0);
break;
case 0x2: /* ccr */
sp->v9_ccr = val & V9_CCR_MASK;
break;
case 0x3: /* asi */
sp->v9_asi = val & V9_ASI_MASK;
break;
case 0x4: /* tick */
#if defined(NIAGARA1) || defined(NIAGARA2)
switch (v9p->state) {
case V9_User:
v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode);
return;
case V9_Priv:
v9p->post_precise_trap(sp, Sparcv9_trap_illegal_instruction);
return;
case V9_HyperPriv:
case V9_RED:
break;
}
/* On niagara stick is an alias for tick */
ss_write_tick(sp, val);
DBGTICK( lprintf(sp->gid,
"tick write asr: pc=0x%llx, o7=0x%llx, val=0x%llx tick=0x%llx\n",
sp->pc, sp->intreg[Reg_sparcv9_o7], val, ss_read_tick(sp)); );
break;
#elif ROCK
v9p->post_precise_trap(sp, Sparcv9_trap_illegal_instruction);
return;
#else
#error "No valid processor defined."
#endif
case 0x6: /* fprs */
/* Looks like that even if there is no FPU, we still need an FPRS register.
* There doesn't seem to be scope even for emulation.
*/
prev_val = v9p->fpu_on;
DBGFPRS( lprintf(sp->gid, "ss_write_state_reg: %%fprs=%x @ pc=0x%llx\n", val, sp->pc); );
v9p->fprs.fef = ((val >> V9_FPRS_FEF_BIT)&0x1) ? true : false;
v9p->fpu_on = (v9p->pstate.fpu_enabled && v9p->fprs.fef && v9p->has_fpu);
#ifdef FP_DECODE_DISABLED
/* if FPU was enabled, we may have just changed the behaviour of FP instns */
/* in which case flush the decoded instruction cache */
if (v9p->fpu_on != prev_val) {
sp->xicache_instn_flush_pending = true;
set_sync_pending(sp);
}
#endif /* FP_DECODE_DISABLED */
v9p->fprs.du = ((val >> V9_FPRS_DU_BIT)&0x1) ? true : false;
v9p->fprs.dl = ((val >> V9_FPRS_DL_BIT)&0x1) ? true : false;
break;
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
case 0x10: /* PCR */
if (v9p->state == V9_User) {
v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode);
return;
}
val &= SS_PCR_MASK;
DBGPIC( lprintf(sp->gid, "ss_write_state_reg: "
"(wr %%pcr @ pc=0x%llx, 0x%llx -> 0x%llx)\n",
sp->pc, nsp->pcr, val); );
/*
* If the pcr is active, we need to start counting instns
* so that when the pic is read, it counts the number of
* instns.
* As soon as the pic starts counting, we need to catch
* when it will overflow so that we can trigger a PIL 15 intr
*/
if (val & SS_PCR_UT_ST) {
/* Starting to count? Sample from now. */
if ((nsp->pcr & SS_PCR_UT_ST) == 0)
RESET_PIC_SAMPLE_BASES(sp, nsp, v9p);
nsp->pcr = val;
ss_recomp_cycle_target(sp); /* catch next overflow event */
} else {
/* Stopping counting? Collect last samples. */
if ((nsp->pcr & SS_PCR_UT_ST) != 0)
UPDATE_PIC(sp, nsp, v9p);
nsp->pcr = val;
}
if (SS_PCR_TEST_OVF_PENDING(nsp->pcr)) {
v9p->softint |= BIT(15);
DBGSOFTINT( lprintf(sp->gid,
"softint wr pcr overflow: pc=0x%llx, o7=0x%llx, val=0x%llx, stick=%u, tick=%u\n",
sp->pc, sp->intreg[Reg_sparcv9_o7], v9p->softint, v9p->stick_cmpr.pending,
v9p->tick_cmpr.pending); );
ss_check_interrupts(sp);
}
break;
case 0x11: /* PIC */
if (v9p->state == V9_User && (nsp->pcr & SS_PCR_PRIV) != 0) {
v9p->post_precise_trap(sp, Sparcv9_trap_privileged_action);
return;
}
DBGPIC( lprintf(sp->gid, "ss_write_state_reg: (wr %%pic @ pc=0x%llx, val=0x%llx)\n", sp->pc, val); );
nsp->pic0 = CPU_COUNTER_TO_PIC0(val);
nsp->pic1 = CPU_COUNTER_TO_PIC1(val);
RESET_PIC_SAMPLE_BASES(sp, nsp, v9p);
ss_recomp_cycle_target(sp); /* catch next overflow event */
break;
#endif /* } */
case 0x13: /* GSR */
#ifdef ROCK
if (!nsp->fpae) {
v9p->post_precise_trap(sp,
Sparcv9_trap_illegal_instruction);
return;
}
#endif
if (!v9p->fpu_on) {
v9p->post_precise_trap(sp, Sparcv9_trap_fp_disabled);
return;
}
DBGFSR( lprintf(sp->gid, "ss_write_state_reg: pc=0x%llx, gsr=0x%llx (was 0x%llx)\n", sp->pc, val, sp->v9_gsr); );
sp->v9_gsr = val & V9_GSR_REG_MASK;
break;
case 0x14: /* set softint register */
if (v9p->state == V9_User) {
v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode);
return;
}
if ((val>>16)&1LL) v9p->stick_cmpr.pending = true;
if ((val>>0)&1LL) v9p->tick_cmpr.pending = true;
v9p->softint |= val & MASK64(15,1);
DBGSOFTINT( lprintf(sp->gid,
"softint write set: pc=0x%llx, o7=0x%llx, val=0x%llx, "
"softint=0x%llx, stick=%d, tick=%d\n",
sp->pc, sp->intreg[Reg_sparcv9_o7], val, v9p->softint,
v9p->stick_cmpr.pending, v9p->tick_cmpr.pending); );
ss_check_interrupts(sp);
break;
case 0x15: /* clear softint register */
if (v9p->state == V9_User) {
v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode);
return;
}
if ((val>>16)&1LL) v9p->stick_cmpr.pending = false;
if ((val>>0)&1LL) v9p->tick_cmpr.pending = false;
v9p->softint &= (~val) & MASK64(15,1);
DBGSOFTINT( lprintf(sp->gid,
"softint write clear: pc=0x%llx, o7=0x%llx, val=0x%llx, "
"softint=0x%llx, stick=%d, tick=%d\n",
sp->pc, sp->intreg[Reg_sparcv9_o7], val, v9p->softint,
v9p->stick_cmpr.pending, v9p->tick_cmpr.pending); );
ss_check_interrupts(sp);
break;
case 0x16: /* softint */
if (v9p->state == V9_User) {
v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode);
return;
}
v9p->stick_cmpr.pending = ((val >> 16)&1LL) ? true : false;
v9p->tick_cmpr.pending = ((val >> 0)&1LL) ? true : false;
v9p->softint = val & MASK64(15,1);
DBGSOFTINT( lprintf(sp->gid,
"softint write: pc=0x%llx, o7=0x%llx, softint=0x%llx, "
"stick=%d, tick=%d\n",
sp->pc, sp->intreg[Reg_sparcv9_o7], v9p->softint,
v9p->stick_cmpr.pending, v9p->tick_cmpr.pending); );
ss_check_interrupts(sp);
break;
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
case 0x17: /* tick_cmpr */
if (v9p->state == V9_User) {
v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode);
return;
}
ss_tick_cmpr_write(sp, &v9p->tick_cmpr, val);
DBGTICK( lprintf(sp->gid,
"tick_cmpr write: pc=0x%llx, o7=0x%llx, val=0x%llx inten=%u val-tick=0x%llx\n",
sp->pc, sp->intreg[Reg_sparcv9_o7], v9p->tick_cmpr.compare, v9p->tick_cmpr.interrupt_enabled,
v9p->tick_cmpr.compare - ss_read_tick(sp)); );
ss_recomp_cycle_target(sp);
break;
#endif /* } */
case 0x18: /* stick */
#if defined(NIAGARA1) || defined(NIAGARA2)
switch (v9p->state) {
case V9_User:
v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode);
return;
case V9_Priv:
v9p->post_precise_trap(sp, Sparcv9_trap_illegal_instruction);
return;
case V9_HyperPriv:
case V9_RED:
break;
}
ss_write_stick(sp, val);
DBGTICK( lprintf(sp->gid,
"stick write: pc=0x%llx, o7=0x%llx, val=0x%llx stick=0x%llx\n",
sp->pc, sp->intreg[Reg_sparcv9_o7], val, ss_read_stick(sp)); );
break;
#elif defined(ROCK)
v9p->post_precise_trap(sp, Sparcv9_trap_illegal_instruction);
return;
#else
#error "No valid processor defined."
#endif
case 0x19: /* stick_cmpr */
if (v9p->state == V9_User) {
v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode);
return;
}
ss_tick_cmpr_write(sp, &v9p->stick_cmpr, val);
DBGTICK( lprintf(sp->gid,
"stick_cmpr write: pc=0x%llx, o7=0x%llx, val=0x%llx inten=%u val-stick=0x%llx\n",
sp->pc, sp->intreg[Reg_sparcv9_o7], v9p->stick_cmpr.compare, v9p->stick_cmpr.interrupt_enabled,
v9p->stick_cmpr.compare - ss_read_stick(sp)); );
ss_recomp_cycle_target(sp);
break;
#ifdef NIAGARA1
case 0x1a: /* strand_sts_reg */
switch (v9p->state) {
case V9_User:
v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode);
return;
case V9_Priv:
DBGCMP( lprintf(sp->gid, "%%tsr write priv: "
"pc=0x%llx, o7=0x%llx, val=0x%llx\n",
sp->pc, sp->intreg[Reg_sparcv9_o7], val); );
IMPL_WARNING(("ss_write_state_reg: (wr %%tsr @ pc=0x%llx) deprecated access to strand status register", sp->pc));
goto thread_halt;
case V9_HyperPriv:
case V9_RED:
DBGCMP( lprintf(sp->gid, "%%tsr write: "
"pc=0x%llx, o7=0x%llx, val=0x%llx\n",
sp->pc, sp->intreg[Reg_sparcv9_o7], val); );
nsp->spec_en = BIT_TEST( val, THREAD_STS_SPEC_EN_BIT ) ? true : false;
thread_halt: if (!(val & THREAD_STS_ACTIVE)) {
IMPL_WARNING(("ss_write_state_reg: (wr %%tsr @ pc=0x%llx) thread halt not supported", sp->pc));
}
break;
default:
abort();
}
break;
#endif
#ifdef ROCK
case 0x1c: /* cps */
DBGTM( lprintf(sp->gid, "%%cps write: "
"pc=0x%llx, o7=0x%llx, val=0x%llx\n",
sp->pc, sp->intreg[Reg_sparcv9_o7], val); );
nsp->cps = val & V9_CPS_REG_MASK;
break;
#endif
#if 0
/* No unimplemented registers currently. */
case 0x??:
FIXME_WARNING(("ss_write_state_reg: (wr) Unimplemented register 0x%x @ pc=0x%llx", state_reg, sp->pc));
#endif
default:
v9p->post_precise_trap(sp, Sparcv9_trap_illegal_instruction);
return;
}
NEXT_INSTN(sp);
}
static void ss_read_priv_reg(simcpu_t * sp, uint_t rdest, uint_t priv_reg)
{
sparcv9_cpu_t * v9p;
uint64_t val;
v9p = (sparcv9_cpu_t *)(sp->specificp);
ASSERT(0LL==sp->intreg[Reg_sparcv9_g0]);
if (v9p->state == V9_User) {
ASSERT( !v9p->pstate.priv && !v9p->hpstate.hpriv );
v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode);
return;
}
switch (priv_reg) {
case 0: /* tpc */
if (v9p->tl == 0) goto illegal_instruction;
val = N_TPC(v9p, v9p->tl);
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
VA48_ASSERT(val);
#endif /* } */
if (v9p->pstate.addr_mask) {
val &= MASK64(31,0); /* FIXME: SV9_ID125 */
EXEC_WARNING(("@ pc=0x%llx : TPC read with pstate.am == 1", sp->pc));
}
break;
case 1: /* tnpc */
if (v9p->tl == 0) goto illegal_instruction;
val = N_TNPC(v9p, v9p->tl);
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
VA48_ASSERT(val);
#endif /* } */
if (v9p->pstate.addr_mask) {
val &= MASK64(31,0); /* FIXME: SV9_ID125 */
EXEC_WARNING(("@ pc=0x%llx : TNPC read with pstate.am == 1", sp->pc));
}
break;
case 2:
if (v9p->tl == 0) goto illegal_instruction;
val = N_TSTATE(v9p, v9p->tl);
break;
case 3:
if (v9p->tl == 0) goto illegal_instruction;
val = N_TT(v9p, v9p->tl);
break;
case 4: /* tick */
val = ss_read_tick(sp);
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
if (v9p->_tick.non_priv_trap) val |= 1ULL<<63;
#endif /* } */
DBGTICKREAD( lprintf(sp->gid,
"tick read priv: pc=0x%llx, o7=0x%llx, val=0x%llx\n",
sp->pc, sp->intreg[Reg_sparcv9_o7], val); );
break;
case 5: /* tba */
val = v9p->tba;
break;
case 6: /* pstate */
val = ss_read_pstate(v9p);
break;
case 7: /* tl */
val = v9p->tl;
break;
case 8: /* pil */
val = v9p->pil;
break;
case 9: /* cwp */
val = v9p->cwp;
break;
case 0xa: /* cansave */
val = v9p->cansave;
break;
case 0xb: /* canrestore */
val = v9p->canrestore;
break;
case 0xc: /* cleanwin */
val = v9p->cleanwin;
break;
case 0xd: /* otherwin */
val = v9p->otherwin;
break;
case 0xe:
val = (v9p->wstate_normal << V9_WSTATE_NORMAL_BITS) |
(v9p->wstate_other << V9_WSTATE_OTHER_BITS);
break;
case 0x10:
val = v9p->gl;
break;
default:
illegal_instruction:
v9p->post_precise_trap(sp, Sparcv9_trap_illegal_instruction);
return;
}
if (rdest != Reg_sparcv9_g0) sp->intreg[rdest] = val;
NEXT_INSTN(sp);
}
static void ss_write_priv_reg(simcpu_t * sp, uint_t priv_reg, uint64_t val)
{
sparcv9_cpu_t * v9p = (sparcv9_cpu_t *)(sp->specificp);
uint64_t old_val;
ss_strand_t * nsp = (ss_strand_t *)(v9p->impl_specificp);
ASSERT(0LL==sp->intreg[Reg_sparcv9_g0]);
if (v9p->state == V9_User) {
ASSERT( !v9p->pstate.priv && !v9p->hpstate.hpriv );
v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode);
return;
}
#ifdef ROCK
if (TM_ACTIVE(nsp)) {
EXEC_WARNING(("TM ERROR: TM active in 'ss_write_priv_reg' function pc=0x%llx.", sp->pc));
nsp->tm_fail(sp, V9_CPS_INSTR);
return;
}
#endif
switch (priv_reg) {
case 0: /* tpc */
if (v9p->tl == 0) goto illegal_instruction;
if (v9p->pstate.addr_mask) EXEC_WARNING(("writing to tpc with pstate.am=1"));
val &= ~3;
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
val = VA48(val);
#endif /* } */
N_TPC(v9p, v9p->tl) = val; /* FIXME: SV9_ID125 issue ? */
break;
case 1: /* tnpc */
if (v9p->tl == 0) goto illegal_instruction;
if (v9p->pstate.addr_mask) EXEC_WARNING(("writing to tnpc with pstate.am=1"));
val &= ~3;
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
val = VA48(val);
#endif /* } */
N_TNPC(v9p, v9p->tl) = val; /* FIXME: SV9_ID125 issue ? */
break;
case 2: /* tstate */
if (v9p->tl == 0) goto illegal_instruction;
N_TSTATE(v9p, v9p->tl) = val &
((V9_TSTATE_GL_MASK<<V9_TSTATE_GL_SHIFT) |
(V9_TSTATE_CCR_MASK<<V9_TSTATE_CCR_SHIFT) |
(V9_TSTATE_ASI_MASK<<V9_TSTATE_ASI_SHIFT) |
(V9_TSTATE_PSTATE_MASK<<V9_TSTATE_PSTATE_SHIFT) |
(V9_TSTATE_CWP_MASK<<V9_TSTATE_CWP_SHIFT) );
break;
case 3: /* tt */
if (v9p->tl == 0) goto illegal_instruction;
DBGTSTACK(
log_lock();
log_printf(sp->gid, "Pre-TT write pc=0x%llx\n", sp->pc);
sparcv9_dump_state(sp); );
if ((val & V9_TT_MASK) != N_TT(v9p, v9p->tl)) {
N_TT(v9p, v9p->tl) = val & V9_TT_MASK;
DBGTSTACK(log_printf(sp->gid, "Post-TT write\n"); sparcv9_dump_state(sp););
} else {
DBGTSTACK(log_printf(sp->gid, "Post-TT write - TT unchanged\n"););
}
DBGTSTACK( log_unlock(); );
break;
case 0x4: /* tick */
#if defined(NIAGARA1) || defined(NIAGARA2)
switch (v9p->state) {
default:
case V9_Priv:
goto illegal_instruction;
case V9_HyperPriv:
case V9_RED:
break;
}
/* On niagara stick is an alias for tick */
ss_write_tick(sp, val);
DBGTICK( lprintf(sp->gid,
"tick write priv: pc=0x%llx, o7=0x%llx, val=0x%llx tick=0x%llx\n",
sp->pc, sp->intreg[Reg_sparcv9_o7], val, ss_read_tick(sp)); );
break;
#elif ROCK
goto illegal_instruction;
#else
#error "No valid processor defined."
#endif
case 5: /* tba */
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
val = VA48(val);
#endif /* } */
v9p->tba = val & V9_TBA_MASK;
break;
case 6: /* pstate */
ss_write_pstate(sp, val); /* like to change states */
break; /* pc updated as usual */
case 7: /* tl - saturates */
if (v9p->state == V9_Priv) {
if (val > Q_MAXPTL) {
EXEC_WARNING(("privileged write to %%tl of %u saturated to %u (%%pc=0x%llx)", val, Q_MAXPTL, sp->pc));
val = Q_MAXPTL;
}
#if DEBUG_TL_RAISE
/* Unsafe raising of %tl, trash values */
if (val > v9p->tl) {
/* bogus values must pass the VA48_ASSERT */
N_TPC(v9p, val) = 0xffffbeefdeadbeecull;
N_TNPC(v9p, val) = 0xffffbeefdeadbeecull;
N_TSTATE(v9p, val) =
((V9_TSTATE_GL_MASK<<V9_TSTATE_GL_SHIFT) |
(V9_TSTATE_CCR_MASK<<V9_TSTATE_CCR_SHIFT) |
(V9_TSTATE_ASI_MASK<<V9_TSTATE_ASI_SHIFT) |
(V9_TSTATE_PSTATE_MASK<<V9_TSTATE_PSTATE_SHIFT) |
(V9_TSTATE_CWP_MASK<<V9_TSTATE_CWP_SHIFT) );
N_TT(v9p, val) = 0xffff & V9_TT_MASK;
}
#endif /* DEBUG_TL_RAISE */
v9p->tl = val;
} else {
if (val > v9p->maxtl) {
EXEC_WARNING(("hyperprivileged write to %%tl of %u saturated to %u (%%pc=0x%llx)", val, v9p->maxtl, sp->pc));
}
v9p->tl = (val>v9p->maxtl) ? v9p->maxtl : val;
}
xcache_set_tagstate(sp);
break;
case 8: /* pil */
v9p->pil = val & V9_PIL_MASK;
ss_check_interrupts(sp);
break;
case 9: /* cwp - saturates */
old_val = val;
val &= v9p->nwins_mask;
if (val >= v9p->nwins) {
val = (v9p->nwins - 1);
#if 0 /* this is expected */
EXEC_WARNING(("write to %%cwp of %u saturated to %u (%%pc=0x%llx)",
old_val, val , sp->pc));
#endif
}
if (v9p->cwp != val) {
v9p->cwp = val;
sparcv9_active_window(sp, v9p->cwp);
}
break;
case 10: /* cansave - saturates */
old_val = val;
val &= v9p->nwins_mask;
if (val >= v9p->nwins) {
val = (v9p->nwins - 1);
EXEC_WARNING(("write to %%cansave of %u saturated to %u (%%pc=0x%llx)",
old_val, val, sp->pc));
}
v9p->cansave = val;
break;
case 11: /* canrestore - saturates */
old_val = val;
val &= v9p->nwins_mask;
if (val >= v9p->nwins) {
val = (v9p->nwins - 1);
EXEC_WARNING(("write to %%canrestore of %u saturated to %u (%%pc=0x%llx)",
old_val, val, sp->pc));
}
v9p->canrestore = val;
break;
case 12: /* cleanwin - saturates */
old_val = val;
val &= v9p->nwins_mask;
if (val >= v9p->nwins) {
val = (v9p->nwins - 1);
EXEC_WARNING(("write to %%cleanwin of %u saturated to %u(%%pc=0x%llx)",
old_val, val, sp->pc));
}
v9p->cleanwin = val;
break;
case 13: /* otherwin - saturates */
old_val = val;
val &= v9p->nwins_mask;
if (val >= v9p->nwins) {
val = (v9p->nwins - 1);
EXEC_WARNING(("write to %%otherwin of %u saturated to %u (%%pc=0x%llx)",
old_val, val, sp->pc));
}
v9p->otherwin = val;
break;
case 14: /* wstate */
v9p->wstate_normal = (val >> V9_WSTATE_NORMAL_BITS) & V9_WSTATE_MASK;
v9p->wstate_other = (val >> V9_WSTATE_OTHER_BITS) & V9_WSTATE_MASK;
break;
case 16:
switch(v9p->state) {
default:
case V9_User: abort(); /* shouldn't get here */
case V9_Priv:
if (val > Q_MAXPGL) {
EXEC_WARNING(("privileged write to %%gl of %u saturated to %u (%%pc=0x%llx)", val, Q_MAXPGL, sp->pc));
val = Q_MAXPGL;
}
break;
case V9_HyperPriv:
case V9_RED:
if (val >= v9p->nglobals) {
EXEC_WARNING(("hyperprivileged write to %%gl of %u saturated to %u (%%pc=0x%llx)", val, v9p->nglobals - 1, sp->pc));
val = v9p->nglobals-1;
}
break;
}
#if DEBUG_GL_RAISE
/* Unsafe raising of %gl, trash values */
if (v9p->state == V9_Priv && val > v9p->gl) {
uint_t j, i;
uint64_t *arch_regp;
for (j = val; j > v9p->gl; j--) {
arch_regp = &(v9p->globalsp[(val)*V9_GLOBAL_GROUP]);
arch_regp++; /* skip %g0 */
for (i = V9_GLOBAL_GROUP - 1; i > 0;i-- ) {
*arch_regp++ = 0xdeadbeefdeadbeefull;
}
}
}
#endif /* DEBUG_GL_RAISE */
if (v9p->gl != val) {
v9p->gl = val;
sparcv9_active_globals(sp, v9p->gl);
}
break;
default:
illegal_instruction:
v9p->post_precise_trap(sp, Sparcv9_trap_illegal_instruction);
return;
}
NEXT_INSTN(sp);
}
static void ss_read_hyp_priv_reg(simcpu_t * sp, uint_t rdest, uint_t priv_reg)
{
sparcv9_cpu_t * v9p;
uint64_t val;
ss_strand_t * nsp;
v9p = (sparcv9_cpu_t *)(sp->specificp);
nsp = v9p->impl_specificp;
if (V9_HyperPriv!=v9p->state && V9_RED!=v9p->state) {
ASSERT( !v9p->hpstate.hpriv );
goto illegal_instruction;
}
switch (priv_reg) {
case 0x0: /* hpstate */
val = ss_read_hpstate(v9p);
break;
case 0x1: /* htstate */
if (v9p->tl == 0) goto illegal_instruction;
val = N_HTSTATE(v9p, v9p->tl);
break;
case 0x3:
val = v9p->hstick_cmpr.pending ? 1 : 0;
break;
case 0x5: /* htba */
val = v9p->htba;
break;
#if !defined(ROCK)
case 0x6: /* hver */
val = v9p->ver;
break;
#endif
#ifdef NIAGARA2 /* { */
case 0x1e: /* halt */
val = 0;
IMPL_WARNING(("ss_read_hyp_priv_reg: (rdhpr %%halt, <reg> @ pc=0x%llx) halt not implemented", sp->pc));
break;
#endif /* } */
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
case 0x1f: /* hstick_cmpr */
val = ss_tick_cmpr_read(sp, &v9p->hstick_cmpr);
DBGTICKREAD( lprintf(sp->gid,
"hstick_cmpr read: pc=0x%llx, o7=0x%llx, val=0x%llx\n",
sp->pc, sp->intreg[Reg_sparcv9_o7], val); );
break;
#endif /* } */
default:
illegal_instruction:
v9p->post_precise_trap(sp, Sparcv9_trap_illegal_instruction);
return;
}
if (rdest != Reg_sparcv9_g0) sp->intreg[rdest] = val;
NEXT_INSTN(sp);
}
static void ss_write_hyp_priv_reg(simcpu_t * sp, uint_t priv_reg, uint64_t val)
{
sparcv9_cpu_t * v9p;
ss_strand_t * nsp;
v9p = (sparcv9_cpu_t *)(sp->specificp);
nsp = v9p->impl_specificp;
if (V9_HyperPriv!=v9p->state && V9_RED!=v9p->state) {
ASSERT( !v9p->hpstate.hpriv );
goto illegal_instruction;
}
#ifdef ROCK
if (TM_ACTIVE(nsp)) {
EXEC_WARNING(("TM ERROR: TM active in 'ss_write_hyp_priv_reg' function pc=0x%llx.", sp->pc));
nsp->tm_fail(sp, V9_CPS_INSTR);
return;
}
#endif
switch (priv_reg) {
case 0x0: /* hpstate */
ss_write_hpstate(sp, false, val);
break;
case 0x1: /* htstate */
if (v9p->tl == 0) goto illegal_instruction;
#if defined(NIAGARA1)
#define SS_HTSTATE_MASK 0x0425
#elif defined(NIAGARA2)
#define SS_HTSTATE_MASK 0x0425
#elif defined(ROCK)
#define SS_HTSTATE_MASK 0xf025
#else
#error "No processor defined"
#endif
N_HTSTATE(v9p, v9p->tl) = val & SS_HTSTATE_MASK;
break;
case 0x3:
if (val & 1) {
v9p->hstick_cmpr.pending = 1;
ss_check_interrupts(sp);
} else
v9p->hstick_cmpr.pending = 0;
DBGSOFTINT( lprintf(sp->gid,
"hintp write: pc=0x%llx, o7=0x%llx, hintp=%u\n",
sp->pc, sp->intreg[Reg_sparcv9_o7],
v9p->hstick_cmpr.pending); );
break;
case 0x5: /* htba */
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
val = VA48(val);
#endif /* } */
v9p->htba = val & V9_HTBA_MASK;
break;
case 0x6: /* ver */
/* read only register ! */
goto illegal_instruction;
#ifdef NIAGARA2 /* { */
case 0x1e: /* halt */
IMPL_WARNING(("ss_write_hyp_priv_reg: (wrhpr <reg>, %%halt @ pc=0x%llx) halt not implemented", sp->pc));
break;
#endif /* } */
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
case 0x1f: /* hstick_cmpr */
ss_tick_cmpr_write(sp, &v9p->hstick_cmpr, val);
DBGTICK( lprintf(sp->gid,
"hstick_cmpr write: pc=0x%llx, o7=0x%llx, val=0x%llx inten=%u val-stick=0x%llx\n",
sp->pc, sp->intreg[Reg_sparcv9_o7], v9p->hstick_cmpr.compare, v9p->hstick_cmpr.interrupt_enabled,
v9p->hstick_cmpr.compare - ss_read_stick(sp)); );
ss_recomp_cycle_target(sp);
break;
#endif /* } */
default:
illegal_instruction:
v9p->post_precise_trap(sp, Sparcv9_trap_illegal_instruction);
return;
}
NEXT_INSTN(sp);
}
/*************************************************************/
/*
* Done / Retry handler
*/
void ss_done_retry(simcpu_t * sp, bool_t is_done)
{
sparcv9_cpu_t * v9p;
uint64_t val;
uint_t win, gl;
tvaddr_t new_pc, new_npc;
v9p = (sparcv9_cpu_t *)(sp->specificp);
if (v9p->state == V9_User) {
v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode);
return;
}
if (v9p->tl == 0) {
v9p->post_precise_trap(sp, Sparcv9_trap_illegal_instruction);
return;
}
if (!is_done) {
new_pc = N_TPC(v9p, v9p->tl); /* retry */
new_npc = N_TNPC(v9p, v9p->tl);
} else {
new_pc = N_TNPC(v9p, v9p->tl); /* done */
new_npc = new_pc + 4;
}
val = N_TSTATE(v9p, v9p->tl);
DBGE( lprintf(sp->gid, "%s: tl=%u : gl=%u : pc=0x%llx : new_pc=0x%llx "
"new_npc=0x%llx tstate=0x%llx\n",
(is_done ? "done" : "retry"),
v9p->tl, v9p->gl, sp->pc, new_pc, new_npc, val); );
/* Check for return to 32bit mode */
if (((val >> V9_TSTATE_PSTATE_SHIFT) >> V9_PSTATE_AM_BIT)&1) {
/* simple sanity checks ... */
if (new_pc & MASK64(63,32)) EXEC_WARNING(("done/retry to non-32bit PC with pstate.am =1"));
if (new_npc & MASK64(63,32)) EXEC_WARNING(("done/retry to non-32bit NPC with pstate.am =1"));
new_pc &= MASK64(31,0);
new_npc &= MASK64(31,0);
}
/* setup the new pc and npc */
sp->pc = new_pc;
sp->npc = new_npc;
/* restore everything from TSTATE register */
sp->v9_ccr = (val >> V9_TSTATE_CCR_SHIFT) & V9_TSTATE_CCR_MASK;
sp->v9_asi = (val >> V9_TSTATE_ASI_SHIFT) & V9_TSTATE_ASI_MASK;
win = (val >> V9_TSTATE_CWP_SHIFT) & V9_TSTATE_CWP_MASK;
if (win >= v9p->nwins) {
warning("restoring cwp with value too large from TSTATE (%u) ; saturating",win);
win = v9p->nwins-1;
}
if (v9p->cwp != win) {
v9p->cwp = win;
sparcv9_active_window(sp, v9p->cwp);
}
gl = (val >> V9_TSTATE_GL_SHIFT) & V9_TSTATE_GL_MASK;
switch(v9p->state) {
default:
case V9_User: abort(); /* shouldn't get here */
case V9_Priv:
if (gl > Q_MAXPGL) {
EXEC_WARNING(("Restoring gl with value too large from TSTATE (%u) ; saturating to %u", gl, Q_MAXPGL));
gl = Q_MAXPGL;
}
break;
case V9_HyperPriv:
case V9_RED:
if (gl >= v9p->nglobals) {
EXEC_WARNING(("Restoring gl with value too large from TSTATE (%u) ; saturating to %u", gl, v9p->nglobals-1));
gl = v9p->nglobals-1;
}
break;
}
if (v9p->gl != gl) {
v9p->gl = gl;
sparcv9_active_globals(sp, v9p->gl);
}
ss_write_pstate( sp, (val >> V9_TSTATE_PSTATE_SHIFT) & V9_TSTATE_PSTATE_MASK );
#ifdef NIAGARA1
/* FIXME: care not to overwrite the ENB bit in HPSTATE if we ever implement it */
#endif /* NIAGARA1 */
ss_write_hpstate( sp, true, N_HTSTATE(v9p, v9p->tl) );
v9p->tl --;
xcache_set_tagstate(sp);
}
/*************************************************************/
/*
* jpriv handler
*/
void ss_jpriv(simcpu_t * sp, tvaddr_t addr)
{
sparcv9_cpu_t * v9p;
ss_strand_t * nsp;
v9p = (sparcv9_cpu_t *)(sp->specificp);
nsp = v9p->impl_specificp;
if (v9p->state != V9_HyperPriv) {
v9p->post_precise_trap(sp, Sparcv9_trap_illegal_instruction);
return;
}
if ((addr & 3) != 0) {
v9p->post_precise_trap(sp, Sparcv9_trap_mem_address_not_aligned);
return;
}
/* setup the new pc and npc */
sp->pc = addr;
sp->npc = addr + 4;
/* clear HPSTATE.hpriv */
v9p->hpstate.hpriv = false;
/* set PSTATE.priv */
v9p->pstate.priv = true;
V9_STATE_CHANGE(sp, v9p, V9_Priv);
sp->xicache_trans_flush_pending = true;
sp->xdcache_trans_flush_pending = true;
ss_check_interrupts(sp); /* because of state change */
/* ROCK PRM 1.2 (wip) does not specify PSTATE.AM behavior for jpriv */
}
/*************************************************************/
/*
* Exception handling - very processor specific ...
*/
/*
* This function supports all synchronous and asynchronous traps
* The result is to leave the cpu in the correct state ready
* for trap execution.
* This model supports only the new v9 trap gl globals
* and of course the privileged, hyperpriv trap modes.
*/
void ss_take_exception(simcpu_t * sp)
{
sparcv9_cpu_t * v9p;
sparcv9_state_t new_state;
ss_strand_t * nsp;
ss_proc_t * npp;
ss_trap_type_t RED_tt = 0;
bool_t prev_state;
uint_t tl, oldtl;
ss_trap_type_t tt; /* the trap we deliver */
ss_trap_type_t actual_tt; /* the trap that occurred */
tvaddr_t new_trap_pc;
v9p = (sparcv9_cpu_t *)(sp->specificp);
nsp = v9p->impl_specificp;
npp = (ss_proc_t *)(sp->config_procp->procp);
ASSERT(0LL==sp->intreg[Reg_sparcv9_g0]);
/*
* OK, let's figure out which trap we need to take -
* based on the list that is pending, and the
* outstanding disrupting trap events.
*/
actual_tt = nsp->pending_precise_tt;
/*
* anything precise will get generated by re-execution of the
* same code sequence - so clear immediately
*/
nsp->pending_precise_tt = SS_trap_NONE;
if (SS_trap_NONE != nsp->pending_async_tt) {
tt = nsp->pending_async_tt;
/* NB larger priority number = lower priority */
if ( SS_trap_NONE == actual_tt || ss_trap_list[actual_tt].priority > ss_trap_list[tt].priority) {
actual_tt = tt;
/* Only clear if we already detected it was not clear
* and we took the trap
*/
nsp->pending_async_tt = SS_trap_NONE;
}
}
/* More tests here - FIXME */
/*
* Clear the global pending notice .. if nothing left pending.
*
* Note this could be simpler since above pending_precise_tt
* is always set to SS_trap_NONE and pending_async_tt is
* aleady being tested for SS_trap_NONE.
*/
if (SS_trap_NONE == nsp->pending_async_tt &&
SS_trap_NONE == nsp->pending_precise_tt) {
sp->exception_pending = false;
}
/* Bail if we found nothing to do ! error ? - FIXME */
if (actual_tt == SS_trap_NONE) {
DBGE( lprintf(sp->gid, "No exception?\n"); );
return;
}
DBGE( if (v9p->tl != v9p->gl) {
lprintf(sp->gid, "tl != gl: exception 0x%02x : %s : tl=%u : gl=%u : pc=0x%llx\n",
actual_tt, ss_trap_list[actual_tt].trap_namep,
v9p->tl, v9p->gl, sp->pc);
} );
#if RELINQUISH_QUANTUM_HACKS /* { */
if (actual_tt == SS_trap_htrap_instruction &&
sp->etp->nsimcpus > 1 &&
sp->intreg[Reg_sparcv9_o5] == 0x12) {
/* CPU yield hcall - yield this CPU's quantum */
set_sync_pending(sp);
}
#endif /* } */
DBGE( lprintf(sp->gid, "Taking exception 0x%02x : %s : tl=%u : gl=%u : pc=0x%llx, npc=0x%llx\n",
actual_tt, ss_trap_list[actual_tt].trap_namep,
v9p->tl, v9p->gl, sp->pc, sp->npc); );
DBGHC( if (actual_tt >= SS_trap_htrap_instruction && v9p->state == V9_Priv)
if (actual_tt == SS_trap_htrap_instruction)
lprintf(sp->gid, "hcall fast:"
" (%%o5=0x%llx)(0x%llx, 0x%llx, 0x%llx, 0x%llx, 0x%llx)"
" [pc=0x%llx %%o6=0x%llx %%o7=0x%llx]\n",
sp->intreg[Reg_sparcv9_o5],
sp->intreg[Reg_sparcv9_o0],
sp->intreg[Reg_sparcv9_o1],
sp->intreg[Reg_sparcv9_o2],
sp->intreg[Reg_sparcv9_o3],
sp->intreg[Reg_sparcv9_o4],
sp->pc,
sp->intreg[Reg_sparcv9_o6],
sp->intreg[Reg_sparcv9_o7]);
else
lprintf(sp->gid, "hcall hyper-fast:"
" (TT=0x%03x)"
"(0x%llx, 0x%llx, 0x%llx, 0x%llx, 0x%llx, 0x%llx)"
" [pc=0x%llx %%o6=0x%llx %%o7=0x%llx]\n",
actual_tt,
sp->intreg[Reg_sparcv9_o0],
sp->intreg[Reg_sparcv9_o1],
sp->intreg[Reg_sparcv9_o2],
sp->intreg[Reg_sparcv9_o3],
sp->intreg[Reg_sparcv9_o4],
sp->intreg[Reg_sparcv9_o5],
sp->pc,
sp->intreg[Reg_sparcv9_o6],
sp->intreg[Reg_sparcv9_o7]); );
switch (actual_tt) {
/*
* We special case the early reset traps that get special
* behaviour and need to use the reset trap vector.
*/
case SS_trap_power_on_reset:
case SS_trap_watchdog_reset:
case SS_trap_externally_initiated_reset:
case SS_trap_software_initiated_reset:
#if defined(NIAGARA1) || defined(NIAGARA2)
case SS_trap_RED_state_exception:
#endif
ss_reset_trap(sp, actual_tt);
return;
#ifdef NIAGARA2
case N2_trap_control_word_queue_interrupt:
case N2_trap_modular_arithmetic_interrupt:
break;
/*
* FIXME: for now assert on traps that are not implemented yet
*/
case SS_trap_control_transfer_instruction:
case SS_trap_instruction_VA_watchpoint:
case SS_trap_instruction_breakpoint:
EXEC_WARNING(("Legion should not generate an unimplemented trap (TT=0x%x)",actual_tt));
ASSERT(0);
#endif
#ifdef ROCK
case RK_trap_hyperprivileged_queue_0:
case RK_trap_hyperprivileged_queue_1:
#endif
case SS_trap_cpu_mondo_trap:
case SS_trap_dev_mondo_trap:
case SS_trap_resumable_error:
DBGMONDO( lprintf(sp->gid, "Mondo trap 0x%02x : %s : "
"pc=0x%llx, npc=0x%llx\n",
actual_tt, ss_trap_list[actual_tt].trap_namep,
sp->pc, sp->npc); );
break;
default:
break;
}
/* save the state of the
* strand as the trap occurred.
*/
oldtl = v9p->tl;
ASSERT(oldtl <= v9p->maxtl);
if (oldtl == v9p->maxtl) {
RED_tt = SS_trap_watchdog_reset;
} else {
v9p->tl++;
if (v9p->tl == v9p->maxtl)
RED_tt = SS_trap_RED_state_exception;
}
tl = v9p->tl;
ASSERT(tl!=0);
if (v9p->pstate.addr_mask) {
N_TPC( v9p, tl ) = sp->pc & MASK64(31,0);
N_TNPC( v9p, tl ) = sp->npc & MASK64(31,0);
} else {
N_TPC( v9p, tl ) = sp->pc;
N_TNPC( v9p, tl ) = sp->npc;
}
ASSERT(actual_tt<SS_trap_illegal_value);
N_TT( v9p, tl ) = actual_tt;
/* assemble tstate */
N_TSTATE( v9p, tl ) =
((v9p->gl & V9_TSTATE_GL_MASK)<<V9_TSTATE_GL_SHIFT) |
((sp->v9_ccr & V9_TSTATE_CCR_MASK)<<V9_TSTATE_CCR_SHIFT) |
((sp->v9_asi & V9_TSTATE_ASI_MASK)<<V9_TSTATE_ASI_SHIFT) |
(ss_read_pstate(v9p)<<V9_TSTATE_PSTATE_SHIFT) |
((v9p->cwp & V9_TSTATE_CWP_MASK)<<V9_TSTATE_CWP_SHIFT);
N_HTSTATE( v9p, tl ) = ss_read_hpstate(v9p);
DBGTSTACK(
log_lock();
log_printf(sp->gid, "Precise trap\n");
sparcv9_dump_state(sp);
log_unlock(); );
/* Now for window traps there are some window reg adjustments
* to be made here ...
*/
/* Modify cwp for window traps. */
/* Even for hypervisor versions ? */
if (actual_tt == SS_trap_clean_window) {
/* Increment cwp */
v9p->cwp = INC_MOD(v9p->cwp, v9p->nwins);
sparcv9_active_window(sp, v9p->cwp);
} else
if ( actual_tt>= SS_trap_spill_0_normal && actual_tt< (SS_trap_spill_7_other+3)) {
/* Increment cwp by 2+CANSAVE */
v9p->cwp = (v9p->cwp + 2 + v9p->cansave) % v9p->nwins;
sparcv9_active_window(sp, v9p->cwp);
} else
if ( actual_tt>= SS_trap_fill_0_normal && actual_tt<=(SS_trap_fill_7_other+3)) {
/* Decrement cwp */
v9p->cwp = (v9p->cwp + v9p->nwins-1) % v9p->nwins;
sparcv9_active_window(sp, v9p->cwp);
}
tt = actual_tt; /* common case */
/*
* First step is to handle all those operation common to trap setup
*/
prev_state = v9p->fpu_on;
v9p->pstate.fpu_enabled = v9p->has_fpu;
v9p->fpu_on = (v9p->fprs.fef && v9p->has_fpu);
#ifdef FP_DECODE_DISABLED
/* flush decoded instns if behaviour of FP instns is to change */
if (v9p->fpu_on != prev_state) {
sp->xicache_instn_flush_pending = true;
set_sync_pending(sp);
}
#endif /* FP_DECODE_DISABLED */
v9p->pstate.addr_mask = false;
V9_PSTATE_AM_CHANGED(v9p);
/* OK, based on the trap type, and a few other
* pieces of info ... like what state we're in
* - which state are we headed to ...
* .. RED_tt is set if we saturated TL
*/
switch( v9p->state ) {
default:
break;
case V9_User:
switch ( ss_trap_list[actual_tt].from_user) {
case TFlag_Not_Poss:
fatal("Hardware should not be able to generate TT=0x%x from user mode", actual_tt);
case TFlag_Priv:
if (tl<=Q_MAXPTL) goto priv_trap_setup;
goto hyperpriv_trap_setup;
case TFlag_HypPriv:
goto hyperpriv_trap_setup;
default: abort();
}
case V9_Priv:
switch ( ss_trap_list[actual_tt].from_priv) {
case TFlag_Not_Poss:
fatal("Hardware should not be able to generate TT=0x%x from privileged mode", actual_tt);
case TFlag_Priv:
if (tl<=Q_MAXPTL) goto priv_trap_setup;
goto hyperpriv_trap_setup;
case TFlag_HypPriv:
goto hyperpriv_trap_setup;
default: abort();
}
case V9_HyperPriv:
switch ( ss_trap_list[actual_tt].from_hyperpriv) {
case TFlag_Not_Poss:
fatal("Hardware should not be able to generate TT=0x%x from hyper-privileged mode", actual_tt);
case TFlag_Priv:
fatal("Hardware cant generate a trap 0x%x to priv mode from hyper-priv mode", actual_tt);
case TFlag_HypPriv:
if (RED_tt != 0) goto REDstate_trap_setup;
goto hyperpriv_trap_setup;
default: abort();
}
case V9_RED:
if (RED_tt == 0)
RED_tt = SS_trap_RED_state_exception;
if (v9p->had_RED_trap) {
fatal("Caught trap type 0x%x (%s) at pc=0x%llx while "
"in RED state - aborting simulation",
actual_tt, ss_trap_list[actual_tt].trap_namep,
sp->pc);
}
v9p->had_RED_trap = true;
goto REDstate_trap_setup;
}
abort(); /* internal error if we get here ! */
/*
* OK setup for trap into privileged mode
*/
priv_trap_setup:;
new_trap_pc = v9p->tba | (oldtl != 0 ? (1<<14) : 0) | (tt << 5);
v9p->pstate.priv = true;
v9p->pstate.int_enabled = false; /* pstate.ie = 0 */
prev_state = v9p->pstate.cle;
v9p->pstate.cle = v9p->pstate.tle;
if (v9p->pstate.cle != prev_state)
sp->xdcache_trans_flush_pending = true;
/* hpstate unchanged */
/* adjust and saturate GL appropriately */
ASSERT( v9p->gl <= Q_MAXPGL ); /* internal error if otherwise */
if (v9p->gl < Q_MAXPGL) {
v9p->gl++;
} else {
EXEC_WARNING(("Taking privileged trap with gl value too large (%u) ; saturating to %u (tl=%u tpc=0x%llx, tt=0x%x, actual_tt=0x%x)", v9p->gl, Q_MAXPGL, v9p->tl, N_TPC(v9p, tl), tt, actual_tt));
}
sparcv9_active_globals(sp, v9p->gl);
new_state = V9_Priv;
goto take_trap;
/*
* OK setup for trap into hyper-privileged mode
*/
hyperpriv_trap_setup:;
new_trap_pc = v9p->htba | (tt << 5);
/* v9p->pstate.priv unchanged */
/* v9p->pstate.mm unchanged */
v9p->pstate.int_enabled = false; /* pstate.ie = 0 */
prev_state = v9p->pstate.cle;
v9p->pstate.cle = false; /* always big endian */
if (v9p->pstate.cle != prev_state)
sp->xdcache_trans_flush_pending = true;
v9p->hpstate.red = false;
#ifndef NIAGARA2
v9p->hpstate.tlz = false;
#endif
v9p->hpstate.hpriv = true;
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
v9p->hpstate.ibe = false; /* not supported ? FIXME */
#endif /* } */
ASSERT( v9p->gl <= (v9p->nglobals-1) );
if (v9p->gl < (v9p->nglobals-1)) {
v9p->gl ++;
} else {
EXEC_WARNING(("Taking hyper-priv trap with gl value too large (%u) ; saturating to %u", v9p->gl, v9p->nglobals-1));
}
sparcv9_active_globals(sp, v9p->gl);
new_state = V9_HyperPriv;
goto take_trap;
REDstate_trap_setup:;
EXEC_WARNING(("Entering RED at pc=0x%llx", sp->pc));
new_trap_pc = npp->rstv_addr | (RED_tt << 5);
/* v9p->pstate.priv unchanged */
v9p->pstate.int_enabled = false; /* pstate.ie = 0 */
prev_state = v9p->pstate.cle;
v9p->pstate.cle = false; /* always big endian */
if (v9p->pstate.cle != prev_state)
sp->xdcache_trans_flush_pending = true;
v9p->pstate.mm = v9_mm_tso; /* as per UltraSPARC 2006 sec 12.6.2 */
v9p->hpstate.red = true;
v9p->hpstate.tlz = false;
v9p->hpstate.hpriv = true;
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
v9p->hpstate.ibe = false; /* not supported ? FIXME */
#endif /* } */
ASSERT( v9p->gl <= (v9p->nglobals-1) );
if (v9p->gl < (v9p->nglobals-1)) {
v9p->gl ++;
} else {
EXEC_WARNING(("entering RED state with gl value too large (%u) ; saturating to %u", v9p->gl, v9p->nglobals-1));
}
sparcv9_active_globals(sp, v9p->gl);
new_state = V9_RED;
goto take_trap;
take_trap:;
DBGTSTACK(lprintf(sp->gid, "Trap new pc=0x%llx\n", new_trap_pc););
/* V9_PSTATE_CHANGED(v9p); V9_HPSTATE_CHANGED(sp, v9p); */
if (v9p->state != new_state) {
V9_STATE_CHANGE(sp, v9p, new_state);
/* V9_STATE_CHANGE includes xcache_set_tagstate() */
} else {
xcache_set_tagstate(sp);
}
if (actual_tt == SS_trap_legion_save_state) {
new_trap_pc = options.save_restore.trap_pc;
log_printf(sp->gid,
"ss_take_exception : SS_trap_legion_save_state : divert to %%pc 0x%llx\n",
new_trap_pc);
}
sp->pc = new_trap_pc;
sp->npc = new_trap_pc+4;
#if ERROR_TRAP_GEN /* { */
ss_error_taking_trap(sp, (sparcv9_trap_type_t)tt);
#endif /* } ERROR_TRAP_GEN */
ss_check_interrupts(sp); /* because of state change */
}
/*
* These reset traps always go to the hypervisor
* and always use the reset trap vector
*/
void ss_reset_trap(simcpu_t * sp, ss_trap_type_t tt)
{
sparcv9_cpu_t * v9p;
ss_strand_t * nsp;
ss_proc_t * npp;
uint_t tl;
bool_t prev_state;
v9p = (sparcv9_cpu_t *)(sp->specificp);
nsp = v9p->impl_specificp;
npp = (ss_proc_t *)(sp->config_procp->procp);
switch (tt) {
case SS_trap_power_on_reset:
v9p->tl = v9p->maxtl;
v9p->tt[v9p->tl] = SS_trap_power_on_reset;
v9p->gl = v9p->nglobals - 1;
v9p->pstate.mm = v9_mm_tso; /* TSO */
v9p->pstate.fpu_enabled = v9p->has_fpu;
v9p->pstate.addr_mask = false;
V9_PSTATE_AM_CHANGED(v9p);
v9p->pstate.priv= true;
v9p->pstate.int_enabled = false;
v9p->pstate.cle = false;
v9p->pstate.tle = false;
v9p->hpstate.red= true;
v9p->hpstate.tlz= false;
v9p->hpstate.hpriv= true;
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
v9p->hpstate.ibe= false;
#endif /* } */
v9p->softint = 0;
V9_STATE_CHANGE(sp, v9p, V9_RED);
/* stuff for tick interrupts */
v9p->_tick.non_priv_trap = true; /* tick.npt = 1; */
v9p->tick->offset = 0LL;
v9p->_stick.non_priv_trap = true; /* stick.npt = 1; */
v9p->stick->offset = 0LL;
#define SS_TICK_CMPR_RESET (1ULL << 63)
ss_tick_cmpr_write(sp, &v9p->tick_cmpr, SS_TICK_CMPR_RESET);
ss_tick_cmpr_write(sp, &v9p->stick_cmpr, SS_TICK_CMPR_RESET);
ss_tick_cmpr_write(sp, &v9p->hstick_cmpr, SS_TICK_CMPR_RESET);
ss_recomp_cycle_target(sp);
break;
case SS_trap_externally_initiated_reset:
tl = v9p->tl+1;
if (tl>v9p->maxtl) tl = v9p->maxtl;
v9p->tl = tl;
N_TPC( v9p, tl ) = sp->pc;
N_TNPC( v9p, tl ) = sp->npc;
N_TT( v9p, tl ) = tt;
/* assemble tstate */
N_TSTATE( v9p, tl ) =
((v9p->gl & V9_TSTATE_GL_MASK)<<V9_TSTATE_GL_SHIFT) |
((sp->v9_ccr & V9_TSTATE_CCR_MASK)<<V9_TSTATE_CCR_SHIFT) |
((sp->v9_asi & V9_TSTATE_ASI_MASK)<<V9_TSTATE_ASI_SHIFT) |
(ss_read_pstate(v9p)<<V9_TSTATE_PSTATE_SHIFT) |
((v9p->cwp & V9_TSTATE_CWP_MASK)<<V9_TSTATE_CWP_SHIFT);
N_HTSTATE( v9p, tl ) = ss_read_hpstate(v9p);
/* Entering RED state gl can saturate at nglobals-1 */
if (v9p->gl < (v9p->nglobals-1)) v9p->gl ++;
/* sparcv9_active_globals selected at function exit */
v9p->pstate.mm = v9_mm_tso; /* TSO */
v9p->pstate.fpu_enabled = v9p->has_fpu;
v9p->pstate.addr_mask = false;
V9_PSTATE_AM_CHANGED(v9p);
v9p->pstate.priv= true;
v9p->pstate.int_enabled = false;
prev_state = v9p->pstate.cle;
v9p->pstate.cle = v9p->pstate.tle;
if (v9p->pstate.cle != prev_state)
sp->xdcache_trans_flush_pending = true;
v9p->hpstate.red= true;
v9p->hpstate.tlz= false;
v9p->hpstate.hpriv= true;
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
v9p->hpstate.ibe= false;
#endif /* } */
/* v9p->softint = 0; FIXME: this what happens on Niagara ? */
V9_STATE_CHANGE(sp, v9p, V9_RED);
break;
#if 0 /* { */
-- case T_WATCHDOG_RESET:
-- if (++sp->trap_level > sp->max_trap_level) {
-- sp->trap_level = sp->max_trap_level;
-- }
-- TRACEMOD_TTEXT( ("TRAP_LEVEL %d",(int)sp->trap_level) );
--
-- save_trap_state(sp, sp->trap_level, trap_pc, trap_npc);
--
-- /*
-- * After saving state increment CWP .This is implementation
-- * specific to spitfire. Bug:1194954
-- */
--
-- if(from_error_mode){
-- /*
-- * Modify cwp for window traps.
-- * on entering error mode.
-- */
-- if (error_tt == T_CLEAN_WINDOW) {
-- P_CWP = INC_MOD_NWIN(P_CWP);
-- if (P_CWP != sp->current_window)
-- update_cur_win_regs(sp);
-- } else if (is_spill_trap(error_tt)) {
-- /* Increment CWP by 2 + CANSAVE. */
-- int count = 2 + sp->cansave;
-- while (count--) {
-- P_CWP = INC_MOD_NWIN(P_CWP);
-- }
-- if (P_CWP != sp->current_window)
-- update_cur_win_regs(sp);
-- } else if (is_fill_trap(error_tt)) {
-- /* Decrement CWP. */
-- P_CWP= DEC_MOD_NWIN(P_CWP);
-- /* Update cur_win_regs[]. */
-- if (P_CWP != sp->current_window)
-- update_cur_win_regs(sp);
-- }
-- }
-- sp->trap_type[sp->trap_level] = from_error_mode ? error_tt: trno;
--
-- sp->pstate.s.mm = 0x0; /* TSO */
-- Old_val = sp->pstate.s.red;
-- sp->pstate.s.red = 1;
-- PSTATE_RED_CHANGED(sp->pstate.s.red);
-- sp->pstate.s.pef = 1;
-- sp->pstate.s.am = 0;
-- PSTATE_AM_CHANGED(sp->pstate.s.am);
-- sp->pstate.s.priv = 1;
-- sp->pstate.s.ie = 0;
-- sp->pstate.s.ag = 1;
-- sp->pstate.s.vg = 0;
-- sp->pstate.s.mg = 0;
-- sp->pstate.s.cle = sp->pstate.s.tle;
--
-- set_pc_for_red_mode_trap(sp, T_WATCHDOG_RESET);
-- break;
--
--
-- case T_SOFTWARE_RESET:
-- if (++sp->trap_level > sp->max_trap_level) {
-- sp->trap_level = sp->max_trap_level;
-- }
-- TRACEMOD_TTEXT( ("TRAP_LEVEL %d",(int)sp->trap_level) );
--
-- save_trap_state(sp, sp->trap_level, trap_pc, trap_npc);
--
-- sp->trap_type[sp->trap_level] = T_SOFTWARE_RESET;
--
-- sp->pstate.s.mm = 0x0; /* TSO */
-- Old_val = sp->pstate.s.red;
-- sp->pstate.s.red = 1;
-- PSTATE_RED_CHANGED(sp->pstate.s.red);
-- sp->pstate.s.pef = 1;
-- sp->pstate.s.am = 0;
-- PSTATE_AM_CHANGED(sp->pstate.s.am);
-- sp->pstate.s.priv = 1;
-- sp->pstate.s.ie = 0;
-- sp->pstate.s.ag = 1;
-- sp->pstate.s.vg = 0;
-- sp->pstate.s.mg = 0;
-- sp->pstate.s.cle = sp->pstate.s.tle;
--
-- set_pc_for_red_mode_trap(sp, T_SOFTWARE_RESET);
-- break;
#endif /* } */
default:
fatal("cpu: unknown reset trap 0x%x.\n", tt);
break;
}
/* setup the mmu_bypass to reflect hpstate */
/* V9_PSTATE_CHANGED(v9p); V9_HPSTATE_CHANGED(sp, v9p); */
sp->pc = npp->rstv_addr | (tt<<5);
sp->npc = sp->pc + 4;
sparcv9_active_window(sp, v9p->cwp);
sparcv9_active_globals(sp, v9p->gl);
v9p->fpu_on = (v9p->pstate.fpu_enabled && v9p->fprs.fef && v9p->has_fpu);
#ifdef FP_DECODE_DISABLED
sp->xicache_instn_flush_pending = true; /* for such things as fpu_on changed */
set_sync_pending(sp);
#endif /* FP_DECODE_DISABLED */
xcache_set_tagstate(sp);
ss_check_interrupts(sp); /* because of state change */
}
static void ss_post_precise_trap(simcpu_t * sp, sparcv9_trap_type_t code)
{
sparcv9_cpu_t * v9p;
ss_strand_t * nsp;
ss_trap_type_t tt; /* the trap we deliver */
ss_trap_type_t pendtt; /* the trap we deliver */
v9p = (sparcv9_cpu_t *)(sp->specificp);
nsp = v9p->impl_specificp;
#ifdef ROCK
if (TM_ACTIVE(nsp)) {
switch((uint_t)code) {
/* Set of all disruptive traps specified in PRM (for rock) */
case SS_trap_sw_recoverable_error:
case SS_trap_interrupt_level_1:
case SS_trap_interrupt_level_2:
case SS_trap_interrupt_level_3:
case SS_trap_interrupt_level_4:
case SS_trap_interrupt_level_5:
case SS_trap_interrupt_level_6:
case SS_trap_interrupt_level_7:
case SS_trap_interrupt_level_8:
case SS_trap_interrupt_level_9:
case SS_trap_interrupt_level_a:
case SS_trap_interrupt_level_b:
case SS_trap_interrupt_level_c:
case SS_trap_interrupt_level_d:
case SS_trap_interrupt_level_e:
case SS_trap_interrupt_level_f:
case SS_trap_hstick_match:
case SS_trap_trap_level_zero:
case SS_trap_hw_corrected_error:
case RK_trap_store_data_value:
case RK_trap_no_retire:
case RK_trap_SIU_inbound_exception:
case RK_trap_data_access_SIU_error:
case RK_trap_hyperprivileged_queue_0:
case RK_trap_hyperprivileged_queue_1:
case SS_trap_cpu_mondo_trap:
case SS_trap_dev_mondo_trap:
case SS_trap_resumable_error:
nsp->tm_fail(sp, V9_CPS_ASYNC);
break; /* Handle the trap since these are disruptive type */
default:
nsp->tm_fail(sp, V9_CPS_PRECISE);
return; /* Return without handling the trap */
}
}
#endif
tt = (ss_trap_type_t)code;
/*
* post this trap if it has higher priority than the
* trap already in place
*/
pendtt = nsp->pending_precise_tt;
ASSERT( ss_trap_list[tt].trap_namep != (char*)0 ); /* legit HW trap ? */
/* NB larger priority number = lower priority */
if (pendtt==SS_trap_NONE ||
ss_trap_list[pendtt].priority > ss_trap_list[tt].priority) {
nsp->pending_precise_tt = tt;
sp->exception_pending = true;
}
}
#if 0 /* { */
--
-- /* Called to execute all SunSPARC synchronous and asynchronous traps. */
-- void ss_execute_trap(FcpuT* sp, Word trno, const char* trap_str,
-- LWord trap_pc, LWord trap_npc, enum Trap_globals p_globals, bool_t sync_trap)
-- {
-- Word error_tt;
-- Bool from_error_mode = FALSE;
-- u_tba_addr tba_addr;
-- Byte Old_val;
--
-- if (trno > MAX_TRAP_NUM) {
-- fatal("cpu: called with illegal trap number of 0x%x.\n", trno);
-- }
--
--
-- sp->trap_linkage_pending = TRUE;
--
-- if (sp->trap_level == sp->max_trap_level) {
--
-- /* ERROR mode. */
-- /* If stop_on_reset flag is set, stop the simulation. */
-- if (sp->stop_on_reset) {
-- /*
-- * Modify cwp for window traps.
-- * on entering error mode.
-- */
-- if (trno == T_CLEAN_WINDOW) {
-- /* Increment CWP. */
-- P_CWP = INC_MOD_NWIN(P_CWP);
-- if (P_CWP != sp->current_window)
-- update_cur_win_regs(sp);
--
-- } else if (is_spill_trap(trno)) {
-- /* Increment CWP by 2 + CANSAVE. */
-- int count = 2 + sp->cansave;
-- while (count--) {
-- P_CWP = INC_MOD_NWIN(P_CWP);
-- }
-- if (P_CWP != sp->current_window)
-- update_cur_win_regs(sp);
--
-- } else if (is_fill_trap(trno)) {
-- /* Decrement CWP. */
-- P_CWP= DEC_MOD_NWIN(P_CWP);
-- /* Update cur_win_regs[]. */
-- if (P_CWP != sp->current_window)
-- update_cur_win_regs(sp);
-- }
--
-- printf("cpu : error mode caused by trap 0x%x (%s)\n", trno, trap_str);
-- printf("\tAt time of trap: pc 0x%llx, npc 0x%llx\n",
-- (uint64_t)trap_pc, (uint64_t)trap_npc);
-- stop_simulation(sp->cmd_ptr);
-- return;
-- }
--
-- /*
-- * Make the processor look like it halted and then was
-- * brought back to life by a watchdog reset.
-- */
-- error_tt = trno; /* save trno for below */
-- from_error_mode = TRUE;
--
-- /* make processor think it received
-- * a watchdog reset
-- */
-- trno = T_WATCHDOG_RESET;
-- }
--
-- if (is_reset_trap(trno)) {
-- execute_reset_trap(sp, trno, from_error_mode, error_tt, trap_pc, trap_npc);
-- return;
-- }
--
-- if (sp->trap_level == (sp->max_trap_level - 1) || sp->pstate.s.red) {
-- execute_red_mode_trap(sp, trno, trap_pc, trap_npc, p_globals);
-- return;
-- }
--
-- /*
-- * "Normal" trap processing.
-- */
--
-- tba_addr.l = sp->tba.l;
-- tba_addr.s.in_trap = (sp->trap_level > 0);
-- sp->trap_level++;
--
-- TRACEMOD_TTEXT( ("TRAP_LEVEL %d",(int)sp->trap_level) );
--
-- save_trap_state(sp, sp->trap_level, trap_pc, trap_npc);
--
-- /* Modify cwp for window traps. */
-- if (trno == T_CLEAN_WINDOW) {
--
-- /* Increment CWP. */
-- P_CWP = INC_MOD_NWIN(P_CWP);
-- if (P_CWP != sp->current_window)
-- update_cur_win_regs(sp); /* Update cur_win_regs[]. */
--
-- } else if (is_spill_trap(trno)) {
--
-- /* Increment CWP by 2 + CANSAVE. */
--
-- uint32_t cwp = P_CWP;
--
-- if (sp->cansave == 0) {
-- cwp = INC_MOD_NWIN(cwp);
-- P_CWP = INC_MOD_NWIN(cwp);
-- }
-- else {
-- int count = 2 + sp->cansave;
-- while (count--) {
-- P_CWP = INC_MOD_NWIN(P_CWP);
-- }
-- }
--
--
-- if (P_CWP != sp->current_window) {
-- update_cur_win_regs(sp); /* Update cur_win_regs[]. */
-- }
--
-- } else if (is_fill_trap(trno)) {
-- /* Decrement CWP. */
-- P_CWP= DEC_MOD_NWIN(P_CWP);
--
-- if (P_CWP != sp->current_window) {
-- update_cur_win_regs(sp); /* Update cur_win_regs[]. */
-- }
-- }
--
-- Old_val = sp->pstate.s.red;
-- sp->pstate.s.red = 0;
-- PSTATE_RED_CHANGED(sp->pstate.s.red);
-- sp->pstate.s.pef = 1;
-- sp->pstate.s.am = 0;
-- PSTATE_AM_CHANGED(sp->pstate.s.am);
-- sp->pstate.s.priv = 1;
-- sp->pstate.s.ie = 0;
-- sp->pstate.s.cle = sp->pstate.s.tle;
--
-- /* Activate the appropriate globals. */
-- switch (p_globals) {
-- case use_alternate_globals:
-- sp->pstate.s.ag = 1;
-- sp->pstate.s.vg = 0;
-- sp->pstate.s.mg = 0;
--
-- update_global_regs_il (sp, use_alternate_globals + 1);
--
-- break;
-- case use_mmu_globals:
-- sp->pstate.s.ag = 0;
-- sp->pstate.s.vg = 0;
-- sp->pstate.s.mg = 1;
--
-- update_global_regs_il (sp, use_mmu_globals + 1);
--
-- break;
-- case use_vector_globals:
-- sp->pstate.s.ag = 0;
-- sp->pstate.s.vg = 1;
-- sp->pstate.s.mg = 0;
--
-- update_global_regs_il (sp, use_vector_globals + 1);
--
-- break;
-- }
--
-- /* SL - 05/09/2001 */
-- /* repalced the call below by inline function to shorten trap handling */
--
--
-- /* update_global_regs(sp, sp->pstate); */
--
--
-- update_async_trap_pending(sp);
--
-- #ifdef ECC
-- update_error_trap_pending(sp);
-- #endif
--
-- FASTMEM_TL_PSTATE_CHANGE(sp);
--
-- sp->trap_type[sp->trap_level] = trno;
--
-- tba_addr.s.tt = sp->trap_type[sp->trap_level];
--
-- sp->pc = tba_addr.l;
-- sp->npc = tba_addr.l + 4;
-- sp->cti_executed = TRUE;
-- sp->cti_indx = TCC_T;
--
-- #if 0
-- {
-- Word tctxt;
-- (sp->dmmu->context_select)(sp->dmmu, DEFAULT_DATA_ASI, &tctxt);
-- sp->fm.current_ctxt = tctxt;
-- TRACEMOD_TTEXT( ("CONTEXT %d",(int)tctxt) );
-- }
-- #endif
--
-- }
#endif /* } */
#if 0 /* { */
--
-- /**************************************************************/
--
--
-- void
-- execute_reset_trap(FcpuT* sp, Word trno, Bool from_error_mode,
-- Word error_tt, LWord trap_pc, LWord trap_npc)
-- {
-- Byte Old_val;
--
-- switch (trno) {
--
-- case SS_trap_power_on_reset:
-- sp->trap_level = sp->max_trap_level;
-- TRACEMOD_TTEXT( ("TRAP_LEVEL %d",(int)sp->trap_level) );
-- sp->trap_type[sp->trap_level] = T_POWER_ON_RESET;
--
-- sp->pstate.s.mm = 0x0; /* TSO */
-- Old_val = sp->pstate.s.red;
-- sp->pstate.s.red = 1;
-- PSTATE_RED_CHANGED(sp->pstate.s.red);
-- sp->pstate.s.pef = 1;
-- sp->pstate.s.am = 0;
-- PSTATE_AM_CHANGED(sp->pstate.s.am);
-- sp->pstate.s.priv = 1;
-- sp->pstate.s.ie = 0;
-- sp->pstate.s.ag = 1;
-- sp->pstate.s.vg = 0;
-- sp->pstate.s.mg = 0;
-- sp->pstate.s.cle = sp->pstate.s.tle;
--
-- set_pc_for_red_mode_trap(sp, T_POWER_ON_RESET);
--
-- /* stuff for tick interrupts */
-- sp->tick.nonpriv_trap = 1; /* tick.npt = 1; */
-- sp->stick.nonpriv_trap = 1; /* stick.npt = 1; */
-- sp->tick.interrupt_enabled = 0; /* tick_cmpr.int_dis = 1; */
-- sp->stick.interrupt_enabled = 0; /* stick_cmpr.int_dis = 1; */
--
-- /* tick & stick scales created when cpu created ? */
-- assert(sp->tick.scale != 0.0);
--
-- ss_recompute_count_target(sp); /* changed tick & stick */
--
-- break;
--
-- case T_WATCHDOG_RESET:
-- if (++sp->trap_level > sp->max_trap_level) {
-- sp->trap_level = sp->max_trap_level;
-- }
-- TRACEMOD_TTEXT( ("TRAP_LEVEL %d",(int)sp->trap_level) );
--
-- save_trap_state(sp, sp->trap_level, trap_pc, trap_npc);
--
-- /*
-- * After saving state increment CWP .This is implementation
-- * specific to spitfire. Bug:1194954
-- */
--
-- if(from_error_mode){
-- /*
-- * Modify cwp for window traps.
-- * on entering error mode.
-- */
-- if (error_tt == T_CLEAN_WINDOW) {
-- P_CWP = INC_MOD_NWIN(P_CWP);
-- if (P_CWP != sp->current_window)
-- update_cur_win_regs(sp);
-- } else if (is_spill_trap(error_tt)) {
-- /* Increment CWP by 2 + CANSAVE. */
-- int count = 2 + sp->cansave;
-- while (count--) {
-- P_CWP = INC_MOD_NWIN(P_CWP);
-- }
-- if (P_CWP != sp->current_window)
-- update_cur_win_regs(sp);
-- } else if (is_fill_trap(error_tt)) {
-- /* Decrement CWP. */
-- P_CWP= DEC_MOD_NWIN(P_CWP);
-- /* Update cur_win_regs[]. */
-- if (P_CWP != sp->current_window)
-- update_cur_win_regs(sp);
-- }
-- }
-- sp->trap_type[sp->trap_level] = from_error_mode ? error_tt: trno;
--
-- sp->pstate.s.mm = 0x0; /* TSO */
-- Old_val = sp->pstate.s.red;
-- sp->pstate.s.red = 1;
-- PSTATE_RED_CHANGED(sp->pstate.s.red);
-- sp->pstate.s.pef = 1;
-- sp->pstate.s.am = 0;
-- PSTATE_AM_CHANGED(sp->pstate.s.am);
-- sp->pstate.s.priv = 1;
-- sp->pstate.s.ie = 0;
-- sp->pstate.s.ag = 1;
-- sp->pstate.s.vg = 0;
-- sp->pstate.s.mg = 0;
-- sp->pstate.s.cle = sp->pstate.s.tle;
--
-- set_pc_for_red_mode_trap(sp, T_WATCHDOG_RESET);
-- break;
--
-- case T_EXTERNAL_RESET:
-- if (++sp->trap_level > sp->max_trap_level) {
-- sp->trap_level = sp->max_trap_level;
-- }
-- TRACEMOD_TTEXT( ("TRAP_LEVEL %d",(int)sp->trap_level) );
--
-- save_trap_state(sp, sp->trap_level, trap_pc, trap_npc);
--
-- sp->trap_type[sp->trap_level] = from_error_mode ? error_tt: trno;
--
-- sp->pstate.s.mm = 0x0; /* TSO */
-- Old_val = sp->pstate.s.red;
-- sp->pstate.s.red = 1;
-- PSTATE_RED_CHANGED(sp->pstate.s.red);
-- sp->pstate.s.pef = 1;
-- sp->pstate.s.am = 0;
-- PSTATE_AM_CHANGED(sp->pstate.s.am);
-- sp->pstate.s.priv = 1;
-- sp->pstate.s.ie = 0;
-- sp->pstate.s.ag = 1;
-- sp->pstate.s.vg = 0;
-- sp->pstate.s.mg = 0;
-- sp->pstate.s.cle = sp->pstate.s.tle;
--
-- set_pc_for_red_mode_trap(sp, T_EXTERNAL_RESET);
-- break;
--
-- case T_SOFTWARE_RESET:
-- if (++sp->trap_level > sp->max_trap_level) {
-- sp->trap_level = sp->max_trap_level;
-- }
-- TRACEMOD_TTEXT( ("TRAP_LEVEL %d",(int)sp->trap_level) );
--
-- save_trap_state(sp, sp->trap_level, trap_pc, trap_npc);
--
-- sp->trap_type[sp->trap_level] = T_SOFTWARE_RESET;
--
-- sp->pstate.s.mm = 0x0; /* TSO */
-- Old_val = sp->pstate.s.red;
-- sp->pstate.s.red = 1;
-- PSTATE_RED_CHANGED(sp->pstate.s.red);
-- sp->pstate.s.pef = 1;
-- sp->pstate.s.am = 0;
-- PSTATE_AM_CHANGED(sp->pstate.s.am);
-- sp->pstate.s.priv = 1;
-- sp->pstate.s.ie = 0;
-- sp->pstate.s.ag = 1;
-- sp->pstate.s.vg = 0;
-- sp->pstate.s.mg = 0;
-- sp->pstate.s.cle = sp->pstate.s.tle;
--
-- set_pc_for_red_mode_trap(sp, T_SOFTWARE_RESET);
-- break;
--
-- default:
-- fatal("cpu: unknown reset trap 0x%x.\n", trno);
-- break;
-- }
--
-- update_global_regs(sp, sp->pstate);
-- update_async_trap_pending(sp);
--
-- #ifdef ECC
-- update_error_trap_pending(sp);
-- #endif
--
-- FASTMEM_TL_PSTATE_CHANGE(sp);
--
-- sp->cti_executed = TRUE;
-- sp->cti_indx = TCC_T;
--
-- /* setup for the fast memory stuff ... -ash */
--
--
-- /* fastmem_flush(&(sp->fm), 0, 0); */ /* flush all pages */
--
--
-- #if 0
-- { Word tctxt;
-- (sp->dmmu->context_select)(sp->dmmu, DEFAULT_DATA_ASI, &tctxt);
-- sp->fm.current_ctxt = tctxt;
-- TRACEMOD_TTEXT( ("CONTEXT %d",(int)tctxt) );
-- }
-- #endif
--
-- }
--
-- /*
-- * Called when a trap occurs in RED mode or a trap puts the processor
-- * into RED mode (when trap_level is max_trap_level less 1).
-- */
-- void
-- execute_red_mode_trap(FcpuT* sp, Word trno, LWord trap_pc,
-- LWord trap_npc, enum Trap_globals p_globals)
-- {
-- Byte Old_val;
--
-- sp->trap_level ++;
-- TRACEMOD_TTEXT( ("TRAP_LEVEL %d",(int)sp->trap_level) );
--
-- save_trap_state(sp, sp->trap_level, trap_pc, trap_npc);
--
-- sp->trap_type[sp->trap_level] = trno;
--
-- /* Modify cwp for window traps. */
-- if (trno == T_CLEAN_WINDOW) {
-- /* Increment CWP. */
-- P_CWP = INC_MOD_NWIN(P_CWP);
-- if (P_CWP != sp->current_window)
-- update_cur_win_regs(sp); /* Update cur_win_regs[]. */
-- } else if (is_spill_trap(trno)) {
-- /* Increment CWP by 2 + CANSAVE. */
-- int count = 2 + sp->cansave;
--
-- while (count--) {
-- P_CWP = INC_MOD_NWIN(P_CWP);
-- }
-- if (P_CWP != sp->current_window)
-- update_cur_win_regs(sp); /* Update cur_win_regs[]. */
-- } else if (is_fill_trap(trno)) {
-- /* Decrement CWP. */
-- P_CWP= DEC_MOD_NWIN(P_CWP);
-- if (P_CWP != sp->current_window)
-- update_cur_win_regs(sp); /* Update cur_win_regs[]. */
-- }
-- sp->pstate.s.mm = 0x0; /* TSO */
-- Old_val = sp->pstate.s.red;
-- sp->pstate.s.red = 1;
-- PSTATE_RED_CHANGED(sp->pstate.s.red);
-- sp->pstate.s.pef = 1;
-- sp->pstate.s.am = 0;
-- PSTATE_AM_CHANGED(sp->pstate.s.am);
-- sp->pstate.s.priv = 1;
-- sp->pstate.s.ie = 0;
-- sp->pstate.s.cle = sp->pstate.s.tle;
--
-- /* Activate the appropriate globals. */
-- switch (p_globals) {
-- case use_alternate_globals:
-- sp->pstate.s.ag = 1;
-- sp->pstate.s.vg = 0;
-- sp->pstate.s.mg = 0;
-- break;
-- case use_mmu_globals:
-- sp->pstate.s.ag = 0;
-- sp->pstate.s.vg = 0;
-- sp->pstate.s.mg = 1;
-- break;
-- case use_vector_globals:
-- sp->pstate.s.ag = 0;
-- sp->pstate.s.vg = 1;
-- sp->pstate.s.mg = 0;
-- break;
-- }
--
-- update_global_regs(sp, sp->pstate);
-- update_async_trap_pending(sp);
--
-- #ifdef ECC
-- update_error_trap_pending(sp);
-- #endif
--
-- FASTMEM_TL_PSTATE_CHANGE(sp);
--
-- set_pc_for_red_mode_trap(sp, T_RED_MODE_EXCEPTION);
-- sp->cti_executed = TRUE;
-- sp->cti_indx = TCC_T;
--
-- /* setup for the fast memory stuff ... -ash */
--
--
-- /* fastmem_flush(&(sp->fm), 0, 0); */ /* flush all pages */
--
-- #if 0
-- {
-- Word tctxt;
-- (sp->dmmu->context_select)(sp->dmmu, DEFAULT_DATA_ASI, &tctxt);
-- sp->fm.current_ctxt = tctxt;
-- TRACEMOD_TTEXT( ("CONTEXT %d",(int)tctxt) );
-- }
-- #endif
--
-- }
--
--
--
--
--
--
-- void
-- save_trap_state(FcpuT* sp, Byte level, LWord trap_pc, LWord trap_npc)
-- {
--
--
-- sp->tstate[level].s.ccr = sp->ccr.b;
-- sp->tstate[level].s.asi = sp->asi_reg;
-- sp->tstate[level].s.cwp = P_CWP;
-- sp->tstate[level].s.pstate = sp->pstate.w;
--
-- #if 0
-- LO_W(trap_pc) &= ~0x3; /* Blow away lower two bits */
-- LO_W(trap_npc) &= ~0x3;
-- #endif
--
-- sp->was_tpc_in_hole[level] = FALSE;
-- sp->was_tnpc_in_hole[level] = FALSE;
-- sp->orig_tnpc[level] = trap_npc;
--
-- if (sp->annul) {
-- sp->tpc[level] = trap_npc;
-- sp->tnpc[level] = trap_npc + 4;
-- sp->annul = FALSE;
-- } else {
-- sp->tpc[level] = trap_pc;
-- sp->tnpc[level] = trap_npc;
-- }
--
-- }
--
#endif /* } */
/*************************************************************
*
* ASI access support functions (and instruction impls)
*
*************************************************************/
static void ss_xdc_miss(simcpu_t * sp, uint64_t * regp, tvaddr_t addr, maccess_t op)
{
sparcv9_cpu_t * v9p;
ss_strand_t * nsp;
mem_flags_t mflags;
uint_t context_type;
v9p = (sparcv9_cpu_t *)(sp->specificp);
nsp = v9p->impl_specificp;
context_type = (v9p->tl>0) ? ss_ctx_nucleus : ss_ctx_primary;
mflags = MF_Normal;
if (V9_User != v9p->state) mflags |= MF_Has_Priv;
if (nsp->mmu_bypass) mflags |= MF_MMU_Bypass;
/* V9_ASI_IMPLICIT takes little endian from CLE */
if (v9p->pstate.cle) mflags |= MF_Little_Endian;
ASSERT(!IS_V9_MA_ATOMIC(op & MA_Op_Mask));
ASSERT(0LL==sp->intreg[Reg_sparcv9_g0]);
ss_memory_asi_access(sp, op, regp, mflags, SS_ASI_PRIMARY, context_type,
(1<<(op & MA_Size_Mask))-1, addr, 0);
ASSERT(0LL==sp->intreg[Reg_sparcv9_g0]);
}
/****************************************************************
*
* TLB code
*
****************************************************************/
void ss_tlb_init(ss_tlb_t * tlbp, uint_t nentries)
{
int i;
tlbp->freep = (tlb_entry_t *)0;
tlbp->nentries = nentries;
tlbp->tlb_entryp = Xcalloc(nentries, tlb_entry_t);
/* clear each entry and stack on the free list */
for (i=nentries-1; i>=0; i--) {
/* FIXME: give the tlb entry fields some default power on value ? */
ss_free_tlb_entry(tlbp, &(tlbp->tlb_entryp[i]));
}
/* Now init and clear the hash */
for (i=0; i<SS_TLB_HASH_ENTRIES; i++) {
tlb_hash_bucket_t * hbp;
hbp = &(tlbp->hash[i]);
hbp->ptr = NULL;
}
tlbp->shares = 0;
#if SS_TLB_REPLACE_RROBIN /* { */
tlbp->last_replaced = 0;
#endif /* } */
RW_lock_init(&tlbp->rwlock, NULL);
}
void ss_tlb_flush_shares(simcpu_t * sp, ss_tlb_t * tlbp, bool_t is_immu)
{
simcpu_t * xsp;
sparcv9_cpu_t * v9p;
ss_strand_t * nsp;
int i;
uint_t found = 0;
for (i = 0; i < simcpu_list.count; i++) {
xsp = LIST_ENTRY(simcpu_list, i);
v9p = (sparcv9_cpu_t *)(xsp->specificp);
nsp = v9p->impl_specificp;
if (nsp->itlbp == tlbp || nsp->dtlbp == tlbp) {
found++;
if (xsp != sp) {
if (is_immu)
xsp->xicache_trans_flush_pending = true;
else
xsp->xdcache_trans_flush_pending = true;
}
}
}
ASSERT(found == tlbp->shares);
}
void ss_tlb_scrub(simcpu_t * sp, ss_tlb_t * tlbp, bool_t is_immu)
{
tlb_entry_t * tep;
int i;
bool_t need_flush = false;
RW_wrlock(&tlbp->rwlock);
tep = &(tlbp->tlb_entryp[0]);
for (i=tlbp->nentries-1; i>=0; i--, tep++) {
if (tep->hashidx != -1) {
need_flush = true;
ss_tlb_unhash(tlbp, tep);
ss_free_tlb_entry(tlbp, tep);
}
}
#if SS_TLB_REPLACE_RROBIN /* { */
tlbp->last_replaced = 0;
#endif /* } */
RW_unlock(&tlbp->rwlock);
if (need_flush) {
if (is_immu)
sp->xicache_trans_flush_pending = true;
else
sp->xdcache_trans_flush_pending = true;
if (tlbp->shares > 1) {
ss_tlb_flush_shares(sp, tlbp, is_immu);
}
}
}
/*
* assumes you hold any appropriate locks when calling
*/
void ss_free_tlb_entry(ss_tlb_t * tlbp, tlb_entry_t * tep)
{
/* only write to fields that can't be read by software */
/* and ensure this entry is effectively marked "invalid" */
tep->match_shift = 0;
tep->flags = 0;
tep->match_context = SS_TLB_INVALID_CONTEXT;
tep->hashidx = -1;
#if defined(NIAGARA1)
tep->data &= ~(1ull << SUN4U_TTED_V_BIT);
#elif defined(ROCK)
tep->data &= ~((1ull << SUN4V_TTED_V_BIT) |
(1ull << ROCK_DATA_ACCESS_V1_BIT));
#else
tep->data &= ~(1ull << SUN4V_TTED_V_BIT);
#endif
/* assign to free list */
tep->nextp = tlbp->freep;
tlbp->freep = tep;
}
void ss_tlb_unhash(ss_tlb_t * tlbp, tlb_entry_t * tep)
{
tlb_entry_t ** bp, *p;
ASSERT( tep->hashidx != -1 );
/* We'll barf any panic if we can't find the entry on the list */
/* but this cant ever happen right ! :-) */
for (bp = &(tlbp->hash[tep->hashidx].ptr); (p=*bp)!=tep; bp=&(p->nextp));
*bp = p->nextp;
tep->hashidx = -1;
}
void ss_tlb_unfree(ss_tlb_t * tlbp, tlb_entry_t * tep)
{
tlb_entry_t ** bp, *p;
ASSERT( tep->hashidx == -1 );
/* We'll barf any panic if we can't find the entry on the list */
/* but this cant ever happen right ! :-) */
for (bp = &(tlbp->freep); (p=*bp)!=tep; bp=&(p->nextp));
*bp = p->nextp;
}
/*
* Niagara 1:
* - demap all demaps all entries that DO NOT have their locked bit set
* - demap page & context demaps matching entries even if their locked bits are set.
*
* Niagara 2:
* - removes the lock bit support
* - adds a demap all page operation, which demaps all pages with R=1 from the TLB.
*/
bool_t ss_demap(simcpu_t * sp, ss_demap_t op, ss_mmu_t * mmup, ss_tlb_t * tlbp, uint_t partid, bool_t is_real, uint_t context, tvaddr_t addr)
{
matchcontext_t match_context;
tlb_entry_t * tep;
uint_t i;
bool_t need_flush = false;
match_context = is_real ? SS_TLB_REAL_CONTEXT : context;
switch (op) {
case NA_demap_page:
DBGMMU( lprintf(sp->gid, "demap_page: part=%x ctx=%x va=0x%llx r:%u\n", partid, context, addr, is_real); );
break;
case NA_demap_all:
DBGMMU( lprintf(sp->gid, "demap_all: part=%x r:%u\n", partid, is_real); );
break;
#ifdef NIAGARA2
case NA_demap_all_page:
DBGMMU( lprintf(sp->gid, "demap_all_page: r:%u\n", is_real); );
break;
#endif
#ifdef NIAGARA1
case NA_demap_init:
DBGMMU( lprintf(sp->gid, "demap_init\n"); );
break;
#endif
case NA_demap_context:
/*
* remove TLB entries that match the specified context, partid and R=0
* (true for both N1 and N2, see to N1 PRM Rev 1.5 and N2 PM Rev 0.8)
*/
DBGMMU( lprintf(sp->gid, "demap_ctx: part=%x ctx=%x r:%u\n", partid, context, is_real); );
if (is_real) return false;
break;
#ifdef ROCK
case NA_demap_real:
DBGMMU( lprintf(sp->gid, "demap_real: part=%x r:%u\n", partid, is_real); );
/*
* This operation is supposed to remove all real TLB entries that match
* the partid, so we force match_context to SS_TLB_REAL_CONTEXT and
* treat this like a NA_demap_page and remove any entry which matches
* context and partid.
*/
match_context = SS_TLB_REAL_CONTEXT;
break;
#endif
default:
return false;
}
RW_wrlock(&tlbp->rwlock);
/*
* First lets look for potentially matching pages we may have to
* de-map first. We demap the old entry if it incorporates our new
* page, or vice-versa.
*/
tep = &(tlbp->tlb_entryp[0]);
for (i=tlbp->nentries; i>0; i--, tep++) {
/* ignore this entry if not in use - i.e. not on hash list */
if (tep->hashidx == -1) continue;
switch (op) {
#ifdef NIAGARA2
case NA_demap_all_page:
if (tep->is_real != is_real) break;
goto matching_pid;
#endif
case NA_demap_all:
#ifdef NIAGARA1
if (tep->flags & SS_TLB_FLAG_LOCKED) break;
#endif
goto matching_pid;
case NA_demap_page:
if (((tep->tag_pfn ^ addr)>>tep->match_shift)!=0LL) break;
goto matching_ctx;
#ifdef ROCK
case NA_demap_real:
/* fall through */
#endif
case NA_demap_context:
matching_ctx:
if (tep->match_context != match_context) break;
matching_pid:
if (tep->partid != partid) break;
/* fall thru */
#ifdef NIAGARA1
case NA_demap_init:
#endif
/* matching entry - put back on the free list */
need_flush = true;
ss_tlb_unhash(tlbp, tep);
ss_free_tlb_entry( tlbp, tep );
#if ERROR_INJECTION
DBGERR( lprintf(sp->gid, "ss_demap(): errorp->itep=%x"
" errorp->dtep=%x tep=%x\n",
sp->errorp->itep, sp->errorp->dtep, tep); );
tlb_entry_error_match(sp, mmup, tep);
#endif
break;
default:
fatal("Internal error: illegal demap op (0x%x)", op);
}
}
RW_unlock(&tlbp->rwlock);
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
if (need_flush) {
if (mmup->is_immu)
sp->xicache_trans_flush_pending = true;
else
sp->xdcache_trans_flush_pending = true;
if (tlbp->shares > 1) {
ss_tlb_flush_shares(sp, tlbp, mmup->is_immu);
}
}
#endif /* } */
return true;
}
#ifdef HW_TABLEWALK
/****************************************************************
*
* HW tablewalk code
*
****************************************************************/
/*
* This function implements the hardware tablewalk for Niagara 2 and Rock
* to service the reload request from the TLBs. The sequence of
* matching the TTEs and reloading the new TLB entry is as follows:
*
* - access all configured TSBs to find an TTE that matches the
* vpn (passed in as 'va') and the context of the request
*
* - translate the RPN of the matching TTE into a PPN
*
* - call ss_tlb_insert to load the matching TTE into TLB
*
* - return a specific trap code to indicate the completion status:
*
* a) trap_NONE:
*
* a match is found, and TLB reload is successful
*
* b) {instruction,data}_invalid_TSB_entry/IAE_unauth_access:
*
* a match is found, but an error occurred during
* the RPN-> PPN translation
*
* c) {instruction,data}_access_MMU_miss:
*
* tablewalk is enabled, but a match is not found
* or the TLB reload is not successful
*
* d) fast_{instruction,data}_access_MMU_miss:
*
* tablewalk on all TSBs are disabled
*
*/
ss_trap_type_t ss_hardware_tablewalk(simcpu_t *sp, ss_mmu_t * mmup, ss_tlb_t *tlbp, tvaddr_t va, uint_t context_type, uint_t *flags, uint64_t *pa_offset)
{
sparcv9_cpu_t *v9p = (sparcv9_cpu_t *)(sp->specificp);
ss_strand_t *nsp = v9p->impl_specificp;
ss_trap_type_t miss_trap_type = SS_trap_NONE;
ss_tsb_info_t *tsb_config_regp;
tvaddr_t tsb_base;
bool_t hwtw_enabled = false;
uint_t i, ctrl, context, context0, context1;
tvaddr_t va_22;
/*
* Figure out the context value. Note that the shared context
* value is not really used in HW TW other than for determining
* which context to insert the entry with (based on the
* use_context0/1 bits)
*/
switch (context_type) {
case ss_ctx_primary:
context0 = nsp->pri_context;
context1 = nsp->pri_context1;
break;
case ss_ctx_secondary:
context0 = nsp->sec_context;
context1 = nsp->sec_context1;
break;
case ss_ctx_nucleus:
context0 = SS_NUCLEUS_CONTEXT;
context1 = context0;
break;
default:
fatal("ss_hardware_tablewalk: Internal Error. Not expecting "
"context type 0x%x\n", context_type);
}
if (context0 == 0) {
tsb_config_regp = &nsp->mmu_zero_ctxt_tsb_config[0];
} else {
tsb_config_regp = &nsp->mmu_nonzero_ctxt_tsb_config[0];
}
va_22 = va >> 22;
/*
* walk through all four TSBs to find an tte entry that matches
* the va and the context of the request
*/
for (i=0; i<4; i++, tsb_config_regp++) {
uint_t tte_context, tte_ps, tte_idx, tte_ep;
uint64_t tte_entry[2];
tvaddr_t tte_va, tte_ra, tte_pa;
uint8_t *tte_addr;
/*
* first check if the tablewalk on this TSB is enabled
* (controled by the enable bit of tsb_config_reg)
*/
if (!tsb_config_regp->enable) continue;
hwtw_enabled = true;
/*
* calculate the TSB pointer based on the given VA and
* TSB configuration register.
*/
tte_addr = ss_make_tsb_pointer_int(va, tsb_config_regp);
/*
* fetch the tte entry from the memory (each taken two 8 bytes)
*/
if (!ss_hwtw_get_tte_entry(sp, tte_addr, &tte_entry[0])) {
/* Shouldn't happen under normal operation */
EXEC_WARNING(("ss_hardware_tablewalk: ss_hwtw_get_tte_entry() failed.\n"));
continue;
}
DBGMMU( lprintf(sp->gid, "ss_hardware_tablewalk check tsb%u pa=0x%llx: va>>22=0x%llx ctx=0x%x/0x%x tag=0x%llx data=0x%llx\n", i, tte_addr, va_22, context0, context1, tte_entry[0], tte_entry[1]););
/*
* validate the V bit, continue searching other TSBs if invalid
*/
if (!SUN4V_TTED_V(tte_entry[1])) continue;
/*
* sun4v Solaris uses the reserved bits to flag SW bugs,
* ignore this tte entry if any of these reserved bits are set
*/
if (SUN4V_TTET_RSVD(tte_entry[0])) continue;
/*
* check the fetched tte for a match
*/
tte_context = SUN4V_TTET_CTXT(tte_entry[0]);
tte_va = SUN4V_TTET_VA(tte_entry[0]);
tte_ep = SUN4V_TTED_EP(tte_entry[1]);
tte_ra = SUN4V_TTED_RA(tte_entry[1]);
tte_ps = SUN4V_TTED_PS(tte_entry[1]);
/*
* determine the context to check in terms of bit 62 and 61 of the tsb_config_reg:
* (see 13.11.11 of N2 PRM Rev 1.1 and section 18.5.4 of SPA 2.0 Draft 0.7.1)
*
* - requesting context is zero (nucleus), both use_context_0 and use_context_1 are
* ignored, but hwtw behaves as if these two bits are zero (so there is a context
* comparison, i.e. to check if tte_context == 0)
*
* - context ID-match mode (use_context_0==0 && use_context_1==0):
* context field of TTE is matched against the context of the request
*
* - context ID-ignore mode (use_context_0==1 || use_context_1==1):
* context field of TTE is ignored, load value of Context Register 0 or 1 to TLB
*/
if (context0 == SS_NUCLEUS_CONTEXT) {
if (tte_context != SS_NUCLEUS_CONTEXT) continue;
context = SS_NUCLEUS_CONTEXT;
} else {
if (tsb_config_regp->use_context_0) {
context = context0;
} else if (tsb_config_regp->use_context_1) {
context = context1;
} else /* use_context_0 == use_context_1 == 0 */ {
if (tte_context != context0 &&
tte_context != context1)
continue;
context = tte_context;
}
}
/*
* In both Niagara2 and Rock, it is okay to match if the
* page size of the tsb_config_reg does not match the page
* size of the tte.
* More specifically:
* if page size of TTE >= page size of TSB, then match OK
* if page size of TTE < page size of TSB, then no match
*
* The VA mask bits used in a comparison for a match
* are determined by the TSB page size.
*
*/
if (((tte_va ^ va_22) >> tsb_config_regp->tag_match_shift)==0 &&
tte_ps>=tsb_config_regp->page_size) {
/*
* a matching tte is found, check its EP bit for ITLB miss
*/
if (mmup->is_immu && (!tte_ep)) {
DBGMMU(lprintf(sp->gid,"ss_hardware_tablewalk: false hit on ITLB miss (EP bit unset): va=0x%llx\n", va); );
return (SS_trap_IAE_unauth_access);
}
/*
* the ra_not_pa bit determines if this TSB contains RAs or PAs:
* (if ra_not_pa is set, do RPN -> PPN translation, otherwise
* the RA is treated as PA)
*/
if (!tsb_config_regp->ra_not_pa) {
tte_pa = tte_ra;
} else {
/*
* setup trap type for error occurred in RPN->PPN translation
*/
if (mmup->is_immu)
miss_trap_type = SS_trap_instruction_invalid_TSB_entry;
else
miss_trap_type = SS_trap_data_invalid_TSB_entry;
/*
* See if we can find a conversion for this RA
*/
if (ss_hwtw_convert_ra_to_pa(tte_ra, &tte_pa, nsp, tte_ps)) {
/*
* RA -> PA conversion found.
* Reassemble the matching TTE data entry with the PPN translation
*/
tte_entry[1] &= ~SUN4V_PN_MASK;
tte_entry[1] |= tte_pa & SUN4V_PN_MASK;
/*
* set the trap flag to direct the call to tlb_reload
*/
miss_trap_type = SS_trap_NONE;
}
}
if (miss_trap_type != SS_trap_NONE) {
DBGMMU(lprintf(sp->gid, "ss_hardware_tablewalk: false hit (invalid tte): va=0x%llx tte_ra=0x%llx\n", va, tte_ra); );
return (miss_trap_type);
}
/*
* update tag access register (we use macros because this is somewhat
* chip-specific).
*/
UPDATE_MMU_TAG_ACCESS(mmup, va, context);
DBGMMU( lprintf(sp->gid, "ss_hardware_tablewalk hit: va=0x%llx data=0x%llx ctx=0x%x\n", va, tte_entry[1], context););
/*
* Insert this TTE into the TLB.
*/
miss_trap_type = ss_tlb_insert(sp, mmup, tlbp, nsp->partid, false, tte_entry[1], flags, pa_offset);
return (miss_trap_type);
}
}
#ifdef ROCK
/*
* Unlike N2, if we are in this routine, it means HWTW was enabled and so
* we will issue a "slow" MMU trap regardless of whether all four TSB
* config registers where valid or not.
*/
hwtw_enabled = true;
#endif
/*
* if we are here, either a match was not found or the tablewalk is disabled
*/
if (mmup->is_immu) {
if (hwtw_enabled)
miss_trap_type = SS_trap_instruction_access_MMU_miss;
else
miss_trap_type = SS_trap_fast_instruction_access_MMU_miss;
} else {
if (hwtw_enabled)
miss_trap_type = SS_trap_data_access_MMU_miss;
else
miss_trap_type = SS_trap_fast_data_access_MMU_miss;
}
return (miss_trap_type);
}
uint8_t *ss_hwtw_find_base(simcpu_t *sp, ss_tsb_info_t *tsb_config_reg)
{
domain_t *domainp;
config_proc_t *config_procp;
config_addr_t *capsrc;
uint8_t *srcbufp;
tpaddr_t extent;
config_procp = sp->config_procp;
domainp = config_procp->domainp;
capsrc = find_domain_address(domainp, tsb_config_reg->tsb_base);
if (capsrc == NULL) {
EXEC_WARNING(("@ pc=0x%llx : ss_hwtw_find_base: src address not valid",sp->pc));
return NULL;
}
/*
* try and get the buffer pointer
*/
extent = capsrc->config_devp->dev_typep->dev_cacheable(capsrc, DA_Load,
tsb_config_reg->tsb_base-capsrc->baseaddr, &srcbufp);
if (extent < (1ull << (13+tsb_config_reg->tsb_size))) {
EXEC_WARNING(("@ pc=0x%llx : ss_hwtw_find_base: src not large enough",sp->pc));
return NULL;
}
return srcbufp;
}
/*
* Get a tte entry from memory via a 128 bit attomic load
*/
static bool_t ss_hwtw_get_tte_entry(simcpu_t *sp, uint8_t * tte_addr, uint64_t *ttep)
{
/*
* do an atomic load of the TTE entry
*/
host_atomic_get128be((uint64_t *)tte_addr, ttep, &ttep[1]);
#if HOST_CPU_LITTLE_ENDIAN
*ttep = BSWAP_64(*ttep);
*(ttep + 1) = BSWAP_64(*(ttep + 1));
#endif
return true;
}
#endif /* HW_TABLEWALK */
/*
* XOR all bits to get a single bit result.
* Used for parity bit generation.
*/
uint64_t xor_bits(uint64_t n)
{
n = (n ^ (n >> 32)) & 0xffffffff;
n = (n ^ (n >> 16)) & 0xffff;
n = (n ^ (n >> 8)) & 0xff;
n = (n ^ (n >> 4)) & 0xf;
n = (n ^ (n >> 2)) & 0x3;
n = (n ^ (n >> 1)) & 0x1;
return n;
}
/****************************************************************
*
* L1/L2 cache code
*
****************************************************************/
void ss_l1_cache_init(ss_l1_cache_t * cachep, uint32_t cachesize, bool_t is_icache)
{
uint_t tagsize, datasize;
int i;
/*
* Allocate icache/dcache memory for data and tags
* Icache:
* 64bit diag tag per 32byte instn block
* 64bit diag data per 32bits instn data (instn + switch/parity bits)
* Dcache:
* 64bit tag per 16byte data block
* 64bit diag data mirrors cache data
*/
tagsize = is_icache ? cachesize/4 : cachesize/2;
datasize = is_icache ? cachesize*2 : cachesize;
cachep->tagp = Xmalloc(tagsize);
cachep->datap = Xmalloc(datasize);
/* fill with bad data for debug */
for (i=(tagsize/8)-1; i>=0; i--) {
cachep->tagp[i] = 0xbaadfeed;
}
for (i=(datasize/8)-1; i>=0; i--) {
cachep->datap[i] = 0xbaadfeed;
}
cachep->bist_ctl = 0;
cachep->assocdis = 0;
cachep->inst_mask = 0;
RW_lock_init(&cachep->rwlock, NULL);
}
/****************************************************************
*
* Misc debugging code
*
****************************************************************/
#if !defined(NDEBUG) /* { */
#endif /* } */
#if defined(NIAGARA1) || defined(NIAGARA2)
/*
* called in response to ~i or ~d
*/
void _ss_dump_tlbs(config_proc_t * config_procp, bool_t is_dtlb, bool_t lockit)
{
#if !defined(NDEBUG) /* { */
ss_proc_t * np;
uint_t i, pnum;
FILE * fop = stdout;
np = (ss_proc_t*)(config_procp->procp);
pnum = config_procp->proc_id;
if (is_dtlb) {
for (i=0; i<np->ndtlb; i++) {
fprintf(fop, "Processor %u Core %u D-TLB:\n",pnum,i);
ss_tlb_contents(fop, &(np->dtlbp[i]), lockit);
}
} else {
for (i=0; i<np->nitlb; i++) {
fprintf(fop, "Processor %u Core %u I-TLB:\n",pnum,i);
ss_tlb_contents(fop, &(np->itlbp[i]), lockit);
}
}
#endif /* } */
}
void ss_dump_tlbs(config_proc_t * config_procp, bool_t is_dtlb)
{
_ss_dump_tlbs(config_procp, is_dtlb, true);
}
void ss_dump_tlbs_nolock(config_proc_t * config_procp, bool_t is_dtlb)
{
_ss_dump_tlbs(config_procp, is_dtlb, false);
}
#endif
#if !defined(NDEBUG) /* { */
/* HACK !! */
#include <stdarg.h>
typedef enum {
PS_Left, PS_Center, PS_Right
} just_t;
void pspace(FILE * fop, uint_t space, just_t just, char * fmt, ...)
{
va_list args;
char buf[2048];
size_t len;
int i, before, after;
va_start(args, fmt);
vsprintf(buf, fmt, args);
va_end(args);
len = strlen(buf);
switch(just) {
case PS_Left:
before = 0;
after = space - len;
if (after<0) after = 0;
break;
case PS_Right:
after = 0;
before = space - len;
if (before<0) before = 0;
break;
case PS_Center:
before = space - len;
if (before<0) before = 0;
before >>=1;
after = space - (len + before);
if (after<0) after = 0;
break;
}
for (i=0; i<before; i++) fprintf(fop, " ");
fprintf(fop, "%s", buf);
for (i=0; i<after; i++) fprintf(fop, " ");
}
void ss_tlb_contents(FILE *fop, ss_tlb_t * tlbp, bool_t lockit)
{
uint_t i;
tlb_entry_t * tep;
pspace(fop, 6, PS_Left, "Entry");
pspace(fop, 8, PS_Right, "Part:");
pspace(fop, 8, PS_Left, "ctxt");
pspace(fop, 7, PS_Center, "shift");
pspace(fop, 20, PS_Center, "Tag");
pspace(fop, 20, PS_Center, "PA");
pspace(fop, 7, PS_Center, "Flags");
pspace(fop, 20, PS_Center, "RAW");
fprintf(fop, "\n");
/* grab lock as writer to ensure nothing changes */
if (lockit)
RW_wrlock(&tlbp->rwlock);
tep = &(tlbp->tlb_entryp[0]);
for (i=0; i<tlbp->nentries; i++) {
char * contextp;
uint_t sh;
tpaddr_t pa;
switch (tep->match_context) {
case SS_TLB_INVALID_CONTEXT:
goto no_output;
case SS_TLB_REAL_CONTEXT:
pspace(fop, 6, PS_Right, "%u : ", i);
pspace(fop, 8, PS_Right, "0x%x:", tep->partid);
pspace(fop, 8, PS_Left, "real");
break;
default:
pspace(fop, 6, PS_Right, "%u : ", i);
pspace(fop, 8, PS_Right, "0x%x:", tep->partid);
pspace(fop, 8, PS_Left, "0x%x", tep->tag_context);
break;
}
pspace(fop, 7, PS_Center, "%u", tep->match_shift);
sh = tep->match_shift;
pa = tep->tag_pfn + tep->pa_offset;
pspace(fop, 20, PS_Right, "0x%llx", tep->tag_pfn);
pspace(fop, 20, PS_Right, "0x%llx", pa);
#ifdef NIAGARA1
pspace(fop, 7, PS_Center, "%c%c%c%c%c",
(tep->flags & SS_TLB_FLAG_READ) ? 'R' : '.',
(tep->flags & SS_TLB_FLAG_WRITE) ? 'W' : '.',
(tep->flags & SS_TLB_FLAG_EXEC) ? 'X' : '.',
(tep->flags & SS_TLB_FLAG_PRIV) ? 'P' : '.',
(tep->flags & SS_TLB_FLAG_LOCKED) ? 'L' : '.' );
#else
pspace(fop, 7, PS_Center, "%c%c%c%c",
(tep->flags & SS_TLB_FLAG_READ) ? 'R' : '.',
(tep->flags & SS_TLB_FLAG_WRITE) ? 'W' : '.',
(tep->flags & SS_TLB_FLAG_EXEC) ? 'X' : '.',
(tep->flags & SS_TLB_FLAG_PRIV) ? 'P' : '.');
#endif
pspace(fop, 20, PS_Right, "0x%llx", tep->data);
next:;
fprintf(fop, "\n");
no_output:;
tep++;
}
if (lockit)
RW_unlock(&tlbp->rwlock);
}
#endif /* } */
/****************************************************************
*
* OLD STUFF
*
****************************************************************/
#if 0 /* { */
--
--void ss_xdc_load_miss(simcpu_t * sp, uint64_t * regp, xdcache_line_t * xclp, tvaddr_t va, bool_t issigned, int bytes)
--{
-- tpaddr_t pa, pa_tag;
-- uint8_t * bufp, * ptr;
-- tvaddr_t tag;
-- config_dev_t * cdp;
-- tpaddr_t extent;
-- uint64_t val;
-- sparcv9_cpu_t * v9p;
-- ss_strand_t * nsp;
-- int flags;
--
-- ASSERT( (bytes & (bytes-1)) == 0 ); /* must be power of 2 number of bytes */
--
-- v9p = (sparcv9_cpu_t *)(sp->specificp);
-- nsp = v9p->impl_specificp;
--
-- /* quick check of alignment */
-- if ((va & (bytes-1)) != 0) {
-- /* alignment error force a trap */
-- SET_DTLB_LOAD_FAULT( nsp, va );
-- MEMORY_ACCESS_TRAP();
-- v9p->post_precise_trap(sp, Sparcv9_trap_mem_address_not_aligned);
-- return;
-- }
--
-- /* Find the pa corresponding to the line we need */
-- tag = va & XDCACHE_TAG_MASK;
--
-- /* We assume that for Niagara, the TLB is off in Hyper priv mode */
-- /* FIXME: we should probably do this by swizzling a function pointer */
-- /* for this when we change mode, rather that having an if here ... fix later */
--
-- pa = va;
-- pa_tag = tag;
-- if (!nsp->mmu_bypass) {
-- int idx, context, partid;
-- ss_tlb_t * tlbp;
-- tlb_entry_t * tep;
-- int flags;
--
-- tlbp = nsp->dtlbp;
-- RW_rdlock(&tlbp->rwlock);
--
-- /* FIXME: need a current context variable, not a test here */
-- context = (v9p->tl>0) ? 0 : nsp->pri_context;
-- partid = nsp->partid;
--
-- /* FIXME: Need a better hash than this ! */
-- idx = va >> SS_MAX_PAGE_SIZE_BITS;
-- idx += context + partid;
--
-- idx &= SS_TLB_HASH_MASK;
--
-- /*
-- * So we search for a matching page using the info we have in the
-- * hash - while another thread might possibly be removing or
-- * inserting an entry into the same table.
-- */
--
--
-- for ( tep = tlbp->hash[idx].ptr; tep!=(tlb_entry_t*)0; tep = tep->nextp ) {
-- /* try and match the entry as appropriate */
-- if (((tep->tag_pfn ^ va)>>tep->match_shift)==0 && tep->match_context==context && tep->partid == partid) goto tlb_match;
-- }
-- RW_unlock(&tlbp->rwlock);
-- SET_DTLB_LOAD_FAULT( nsp, va );
-- MEMORY_ACCESS_TRAP();
-- v9p->post_precise_trap(sp, (sparcv9_trap_type_t)SS_trap_fast_data_access_MMU_miss);
-- return;
--
--tlb_match:;
-- /* we have a matching entry ... now all we have to worry about are the permissions */
-- flags = tep->flags;
-- pa += tep->pa_offset;
-- pa_tag += tep->pa_offset;
--
-- RW_unlock(&tlbp->rwlock);
--
-- if ((flags & SS_TLB_FLAG_PRIV) && v9p->state == V9_User) {
-- SET_DTLB_LOAD_FAULT( nsp, va );
-- MEMORY_ACCESS_TRAP();
-- v9p->post_precise_trap(sp, (sparcv9_trap_type_t)SS_trap_data_access_exception);
-- return;
-- }
-- }
--
-- /*
-- * OK - now go get the pointer to the line data
-- * ... start by finding the device that has the
-- * memory we need.
-- * optimise: by guessing at the last device found.
-- */
--
-- /* now find the device - looking in the cache first */
--
-- cdp = sp->xdc.miss_devp;
-- if (!(cdp && cdp->baseaddr<=pa && pa<cdp->topaddr)) {
-- domain_t * domainp;
-- config_proc_t * config_procp;
--
-- config_procp = sp->config_procp;
-- domainp = config_procp->domainp;
--
-- cdp = find_domain_device(domainp, pa);
-- if (cdp == (config_dev_t*)0) {
-- /* OK it's a bus error there was no backing store */
--
-- fatal("bus error - (@pc=0x%llx) load from va=0x%llx (cacheline va=0x%llx -> physical 0x%llx)", sp->pc, va, tag, pa_tag); /* FIXME */
-- }
-- }
--
-- /* try and get the buffer pointer */
--
-- extent = cdp->dev_typep->dev_cacheable(cdp, DA_Load, pa_tag - cdp->baseaddr, &bufp);
--
-- if (extent < XDCACHE_LINE_SIZE) {
-- /* Let device handle memory access operation */
-- /* bus error again ? or fill from multiple devices ? */
-- /* need to check validty for device here ... FIXME */
-- if (cdp->dev_typep->dev_cpu_load(cdp, pa - cdp->baseaddr, issigned, bytes, regp)) goto load_done;
--
-- MEMORY_ACCESS_TRAP();
-- v9p->post_precise_trap(sp, Sparcv9_trap_data_access_error); /* FIXME: right trap ? */
-- return;
-- }
--
-- /*
-- * For now only handle cacheable device memory
-- */
-- /* only cache if memory is cacheable */
-- sp->xdc.miss_devp = cdp; /* cache for next time */
--
-- /* fill in the line */
-- xclp->tag = tag;
-- xclp->offset = ((uint64_t)bufp) - tag;
--
-- /*
-- * Sigh now complete the load on behalf of the original
-- * load instruction
-- */
--
-- ptr = (uint8_t*)(xclp->offset + va);
--
-- if (issigned) {
-- switch(bytes) {
-- case 1: val = *(sint8_t*)ptr; break;
-- case 2: val = *(sint16_t*)ptr; break;
-- case 4: val = *(sint32_t*)ptr; break;
-- default: abort();
-- }
-- } else {
-- switch(bytes) {
-- case 1: val = *(uint8_t*)ptr; break;
-- case 2: val = *(uint16_t*)ptr; break;
-- case 4: val = *(uint32_t*)ptr; break;
-- case 8: val = *(uint64_t*)ptr; break;
-- default: abort();
-- }
-- }
--
-- *regp = val;
--
-- /*
-- * Finally go get the next instruction
-- */
--load_done:;
--
-- NEXT_INSTN(sp);
--}
--
--
--
--void ss_xdc_store_miss(simcpu_t * sp, uint64_t val, xdcache_line_t * xclp, tvaddr_t va, int bytes)
--{
-- tpaddr_t pa, pa_tag;
-- uint8_t * bufp, * ptr;
-- tvaddr_t tag;
-- config_dev_t * cdp;
-- tpaddr_t extent;
-- sparcv9_cpu_t * v9p;
-- ss_strand_t * nsp;
--
-- ASSERT( (bytes & (bytes-1)) == 0 ); /* must be power of 2 number of bytes */
--
-- v9p = (sparcv9_cpu_t *)(sp->specificp);
-- nsp = v9p->impl_specificp;
--
-- /* quick check of alignment */
-- if ((va & (bytes-1)) != 0) {
-- /* alignment error force a trap */
-- SET_DTLB_STORE_FAULT( nsp, va );
-- v9p->post_precise_trap(sp, Sparcv9_trap_mem_address_not_aligned);
-- return;
-- }
--
-- /* Find the pa corresponding to the line we need */
-- tag = va & XDCACHE_TAG_MASK;
--
-- /* We assume that for Niagara, the TLB is off in Hyper priv mode */
-- /* FIXME: we should probably do this by swizzling a function pointer */
-- /* for this when we change mode, rather that having an if here ... fix later */
--
-- pa = va;
-- pa_tag = tag;
-- if (!nsp->mmu_bypass) {
-- int idx, context, partid;
-- ss_tlb_t * tlbp;
-- tlb_entry_t * tep;
-- int flags;
--
-- tlbp = nsp->dtlbp;
-- RW_rdlock(&tlbp->rwlock);
--
-- /* FIXME: need a current context variable, not a test here */
-- context = (v9p->tl>0) ? 0 : nsp->pri_context;
-- partid = nsp->partid;
--
-- /* FIXME: Need a better hash than this ! */
-- idx = va >> SS_MAX_PAGE_SIZE_BITS;
-- idx += context + partid;
--
-- idx &= SS_TLB_HASH_MASK;
--
-- /*
-- * So we search for a matching page using the info we have in the
-- * hash - while another thread might possibly be removing or
-- * inserting an entry into the same table.
-- */
--
--
-- for ( tep = tlbp->hash[idx].ptr; tep!=(tlb_entry_t*)0; tep = tep->nextp ) {
-- /* try and match the entry as appropriate */
-- if (((tep->tag_pfn ^ va)>>tep->match_shift)==0 && tep->match_context==context && tep->partid == partid) goto tlb_match;
-- }
-- RW_unlock(&tlbp->rwlock);
-- SET_DTLB_STORE_FAULT( nsp, va );
-- MEMORY_ACCESS_TRAP();
-- v9p->post_precise_trap(sp, (sparcv9_trap_type_t)SS_trap_fast_data_access_MMU_miss);
-- return;
--
--tlb_match:;
-- /* we have a matching entry ... now all we have to worry about are the permissions */
-- flags = tep->flags;
-- pa += tep->pa_offset;
-- pa_tag += tep->pa_offset;
--
-- RW_unlock(&tlbp->rwlock);
--
-- /* privilege test apparently takes priority ... p.51 US-I PRM table 6-4 */
-- if ((flags & SS_TLB_FLAG_PRIV) && v9p->state == V9_User) {
-- SET_DTLB_STORE_FAULT( nsp, va );
-- MEMORY_ACCESS_TRAP();
-- v9p->post_precise_trap(sp, (sparcv9_trap_type_t)SS_trap_data_access_exception);
-- return;
-- }
--
-- if (!(flags & SS_TLB_FLAG_WRITE)) {
-- SET_DTLB_STORE_FAULT( nsp, va );
-- MEMORY_ACCESS_TRAP();
-- v9p->post_precise_trap(sp, (sparcv9_trap_type_t)SS_trap_fast_data_access_protection);
-- return;
-- }
-- }
--
--
-- /*
-- * OK - now go get the pointer to the line data
-- * ... start by finding the device that has the
-- * memory we need.
-- * optimise: by guessing at the last device found.
-- */
--
-- /* now find the device - looking in the cache first */
--
-- cdp = sp->xdc.miss_devp;
-- if (!(cdp && cdp->baseaddr<=pa && pa<cdp->topaddr)) {
-- domain_t * domainp;
-- config_proc_t * config_procp;
--
-- config_procp = sp->config_procp;
-- domainp = config_procp->domainp;
--
-- cdp = find_domain_device(domainp, pa);
-- if (cdp == (config_dev_t*)0) {
-- /* OK it's a bus error there was no backing store */
--
-- fatal("bus error - (@pc=0x%llx) store to va=0x%llx (cacheline va=0x%llx -> physical 0x%llx)", sp->pc, va, tag, pa); /* FIXME */
-- }
-- }
--
-- /* try and get the buffer pointer */
--
-- extent = cdp->dev_typep->dev_cacheable(cdp, DA_Store, pa_tag - cdp->baseaddr, &bufp);
--
-- if (extent < XDCACHE_LINE_SIZE) {
-- /* Let device handle memory access operation */
-- /* bus error again ? or fill from multiple devices ? */
-- /* need to check validty for device here ... FIXME */
-- if (cdp->dev_typep->dev_cpu_store(cdp, pa - cdp->baseaddr, bytes, val)) goto store_done;
--
-- MEMORY_ACCESS_TRAP();
-- v9p->post_precise_trap(sp, Sparcv9_trap_data_access_error); /* FIXME: right trap ? */
-- return;
-- }
--
-- /*
-- * For now only handle cacheable device memory
-- */
-- /* only cache if memory is cacheable */
-- sp->xdc.miss_devp = cdp; /* cache for next time */
--
-- /* fill in the line */
-- xclp->tag = tag;
-- xclp->offset = ((uint64_t)bufp) - tag;
--
-- /*
-- * Sigh now complete the store on behalf of the original
-- * store instruction
-- */
--
-- ptr = (uint8_t*)(xclp->offset + va);
--
-- switch(bytes) {
-- case 1: *(uint8_t*)ptr = val; break;
-- case 2: *(uint16_t*)ptr = val; break;
-- case 4: *(uint32_t*)ptr = val; break;
-- case 8: *(uint64_t*)ptr = val; break;
-- default: abort();
-- }
--
--
-- /*
-- * Finally go get the next instruction
-- */
--store_done:;
--
-- NEXT_INSTN(sp);
--}
--
--
#endif /* } */
/*
* called in response to ~n
*/
void ss_dump_instruction_counts(config_proc_t * cp)
{
#if !defined(NDEBUG) /* { */
ss_proc_t * np;
sparcv9_cpu_t * sv9p;
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
ss_strand_t * nsp;
#endif /* } */
simcpu_t * sp;
uint64_t hi_cycle = 0;
uint64_t lo_cycle = 0xFFFFFFFFFFFFFFFF;
uint64_t now;
uint_t j;
uint_t ncpus;
np = (ss_proc_t*)(cp->procp);
ncpus = np->nstrands;
if (ncpus > 1)
lprintf(-1, "==== strand counts proc %d ====\n", cp->proc_id);
for (j=0; j<ncpus; j++) {
sv9p = np->strand[j];
sp = sv9p->simp;
now = sp->cycle;
if (now > hi_cycle)
hi_cycle = now;
if (now < lo_cycle)
lo_cycle = now;
lprintf(sp->gid, "@ pc=0x%llx tl=%d: cycle[%llu] instr[%llu] "
"cycle_target[%lld] \n",
sp->pc, sv9p->tl, sp->cycle, ICOUNT(sp),
sp->cycle_target);
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
nsp = sv9p->impl_specificp;
if (nsp->pcr != 0 || nsp->pic0 != 0 || nsp->pic1 != 0) {
lprintf(sp->gid, "%%pcr=0x%llx %%pic0=0x%x "
"%%pic1=0x%x\n",
nsp->pcr, nsp->pic0, nsp->pic1);
}
#endif /* } */
}
if (ncpus > 1)
lprintf(sp->gid, "hi_cycle - lo_cycle=%llu\n", hi_cycle - lo_cycle);
#endif /* } */
}
/*
* This function is called upon each update to the running status register.
* It performs a bit test (each bit corresponding to a virtual core or strand)
* and park or unpark the corresponding strand depending on the bit value.
*/
void
ss_change_exec_state(ss_proc_t *npp, uint64_t running_status)
{
uint_t idx, ncpu, vcore_id;
ncpu = npp->nstrands;
for (idx = 0; idx < ncpu; idx++) {
sparcv9_cpu_t *tv9p;
simcpu_t *tsp;
ss_strand_t *tnsp;
uint_t unparked;
tnsp = &(npp->ss_strandp[idx]);
tv9p = npp->strand[idx];
tsp = tv9p->simp;
vcore_id = tnsp->vcore_id;
unparked = ((running_status >> vcore_id) & 0x1);
if (unparked) {
#if defined(NIAGARA1) /* { */
SET_THREAD_STS_SFSM(npp, tnsp, THREAD_STS_TSTATE_RUN);
#endif /* } NIAGARA1 */
if (PARKED(tsp))
simcore_cpu_state_unpark(tsp);
} else {
#if defined(NIAGARA1) /* { */
SET_THREAD_STS_SFSM(npp, tnsp, THREAD_STS_TSTATE_IDLE);
#endif /* } NIAGARA1 */
if (!PARKED(tsp))
simcore_cpu_state_park(tsp);
}
}
}
/*
* Given a PA from hypervisor, lookup where in simualted
* memory that PA resides so we can read an address
* local to the simulator.
*/
uint64_t* lookup_sim_addr(simcpu_t *sp, uint64_t *sim_addr)
{
uint64_t extent;
config_addr_t *cap;
domain_t *domainp;
config_proc_t *config_procp;
uint64_t *bufp;
config_procp = sp->config_procp;
domainp = config_procp->domainp;
/*
* Lookup which memory device owns this address
* XXX FIXME: We know it's in memory, so we could cache it here
*/
cap = find_domain_address(domainp, (tpaddr_t)sim_addr);
if (cap == NULL) {
/* it's a bus error there was no backing store */
EXEC_WARNING(("bus error - (@pc=0x%llx, icount=%llu) access to addr=0x%llx ",
sp->pc, ICOUNT(sp), sim_addr));
abort();
}
/*
* Use the device function to read the memory address
*/
extent = cap->config_devp->dev_typep->dev_cacheable(cap, DA_Load,
(uint64_t)sim_addr - cap->baseaddr, (uint8_t **)&bufp);
ASSERT(extent != 0);
return(bufp);
}
/*
* Get the virtual core Id (or CPU Id) of the calling strand.
*/
uint_t ss_get_cpuid(simcpu_t *sp)
{
sparcv9_cpu_t *v9p;
ss_strand_t *nsp;
v9p = (sparcv9_cpu_t *)(sp->specificp);
nsp = v9p->impl_specificp;
return (nsp->vcore_id);
}
/*
* Being used when switching to SST mode to simulate CPU's behaviour
* of trashing all registers.
*/
void ss_trash_regs(sparcv9_cpu_t * v9p, uint64_t val)
{
simcpu_t * sp;
uint_t idx;
sp = v9p->simp;
/* trash globals */
for (idx = 0; idx < v9p->nglobals * 8; idx++){
/* need to leave %g0 alone */
if (idx % 8)
v9p->globalsp[idx] = val;
}
/* trash ins and locals */
for (idx = 0; idx < (v9p->nwins * 2 * V9_REG_GROUP); idx++)
v9p->winsp[idx] = val;
/* trash floating point regs */
for (idx = 0; idx < NFP_64; idx++)
sp->fpreg.s64[idx] = val;
/* trash what's in the working register file */
for (idx = 1; idx < NINT; idx++)
sp->intreg[idx] = val;
}
/*
* Macro to create symbols for the debug_hook functions
*/
#define ADD_DBG_DEFS( func ) extern void dbg_##func##(simcpu_t *sp, uint32_t rawi);\
extern void dbg_##func##_parse(void); \
extern void dbg_##func##_dump(void)
ADD_DBG_DEFS(trace);
ADD_DBG_DEFS(coverage);
ADD_DBG_DEFS(debug_log);
ADD_DBG_DEFS(lockstep);
/*
* Parse the debug_hook directive.
* The debug_hook directive in the conf file should look like this:
*
* directive_name debug_hook .....
* -------------- ----------
* eg:
* debug_hook trace ..... ;
* \________________________/ \____________/
* we parse this much here debug_hook_parse()
* parses the rest if any
*
* We parse the 'debug_hook' as the name of the function the user
* is requesting. We then use this name to locate the following functions:
* debug_hook - execloop calls this for each instn
* debug_hook_parse - we call this to parse the rest of the directive
* debug_hook_dump - dumbserial calls this when ~l is input on
* the console
*
*/
static void parse_debug_hook(ss_proc_t *procp)
{
lexer_tok_t tok;
char hookname[BUFSIZ];
char hookname_parse[BUFSIZ];
char hookname_dump[BUFSIZ];
void (*hookp)(uint_t);
void (*hook_parsep)(void);
void (*hook_dumpp)(uint_t);
tok = lex_get_token(); /* parse debug_hook function name */
sprintf(hookname, "%s", lex.strp);
sprintf(hookname_parse, "%s_parse", hookname); /* debug_hook_parse() */
sprintf(hookname_dump, "%s_dump", hookname); /* debug_hook_dump() */
if (streq(hookname, "trace")) {
hookp = (void(*)(uint_t))dbg_trace;
hook_parsep = (void(*)(void))dbg_trace_parse;
hook_dumpp = (void(*)(uint_t))dbg_trace_dump;
} else if (streq(hookname, "coverage")) {
hookp = (void(*)(uint_t))dbg_coverage;
hook_parsep = (void(*)(void))dbg_coverage_parse;
hook_dumpp = (void(*)(uint_t))dbg_coverage_dump;
} else if (streq(hookname, "debug_log")) {
hookp = (void(*)(uint_t))dbg_debug_log;
hook_parsep = (void(*)(void))dbg_debug_log_parse;
hook_dumpp = (void(*)(uint_t))dbg_debug_log_dump;
} else if (streq(hookname, "lockstep")) {
hookp = (void(*)(uint_t))dbg_lockstep;
hook_parsep = (void(*)(void))dbg_lockstep_parse;
hook_dumpp = (void(*)(uint_t))dbg_lockstep_dump;
} else {
lex_fatal("Unsupported debug_hook %s", hookname);
}
procp->config_procp->proc_typep->debug_hookp = (void*)hookp;
procp->config_procp->proc_typep->debug_hook_dumpp = (void*)hook_dumpp;
/*
* Now call the parse function to continue parsing the
* debug_hook directive.
*/
hook_parsep();
lex_get(T_S_Colon);
return;
}
#define PRI_CTX_IS_NUCLEUS(_nsp) ((_nsp)->pri_context == SS_NUCLEUS_CONTEXT)
void
xcache_set_tagstate(simcpu_t * sp)
{
ss_strand_t * nsp;
sparcv9_cpu_t * v9p;
uint32_t newstate;
v9p = (sparcv9_cpu_t *)(sp->specificp);
nsp = v9p->impl_specificp;
if (nsp->mmu_bypass)
newstate = XCACHE_TAGSTATE_PHYS;
else
if (v9p->state == V9_User) {
newstate = (v9p->tl == 0 && !PRI_CTX_IS_NUCLEUS(nsp)) ?
XCACHE_TAGSTATE_TL0_U : XCACHE_TAGSTATE_TLN_U;
}
else {
newstate = (v9p->tl == 0 && !PRI_CTX_IS_NUCLEUS(nsp)) ?
XCACHE_TAGSTATE_TL0 : XCACHE_TAGSTATE_TLN;
}
DBGXCACHE( lprintf(sp->gid, "xcache_set_tagstate: %u -> %u "
"pc=0x%llx [cycle=0x%llx]\n",
sp->tagstate >> _XCACHE_TAGSTATE_SHIFT,
newstate >> _XCACHE_TAGSTATE_SHIFT, sp->pc, sp->cycle); );
if (sp->tagstate != newstate)
sp->exec_loop_reset = true;
sp->tagstate = newstate;
}
void
ss_rdlock(RW_lock_t *lp)
{
RW_lock_t l, n, v;
again:
while ((l = *lp) < 0)
;
n = l + 1;
v = host_cas64((uint64_t *)lp, l, n);
if (v != l)
goto again;
}
void
ss_wrlock(RW_lock_t *lp)
{
RW_lock_t l, n, v;
n = -1;
again:
while ((l = *lp) != 0)
;
v = host_cas64((uint64_t *)lp, l, n);
if (v != l)
goto again;
}
void
ss_unlock(RW_lock_t *lp)
{
RW_lock_t l, n, v;
again:
l = *lp;
if (l > 0) {
n = l - 1;
} else
if (l < 0) {
n = 0;
} else
fatal("lock error");
v = host_cas64((uint64_t *)lp, l, n);
if (v != l)
goto again;
}
/*
* Memory images are written out in 2 formats. Binary format and
* text format. With the binary format, a partial legion config
* file is also dumped which contains the memory directives for
* loading in the various memory binary images.
*
* Memory images are stored in a text file with the following format:
*
* @addr
* 8d41400010800036 0100000000000000 0000000000000000 0000000000000000
* 8d41400010800036 0100000000000000 0000000000000000 0000000000000000
* 8d4140008c21a020 1080002601000000 108004e601000000 0000000000000000
* 1080052901000000 0000000000000000 0000000000000000 0000000000000000
* 1080056b01000000 0000000000000000 0000000000000000 0000000000000000
* 108005aa01000000 0000000000000000 0000000000000000 0000000000000000
* 108005e901000000 0000000000000000 0000000000000000 0000000000000000
* 03004000c221a000 c221a004c221a008 c221a00c10800240 0100000082102018
* @addr//+size
*
* where :
* @addr is the starting address of a chunk of memory followed by
* a mimimum of 256 bytes of non-zero data.
*
* If a range of memory is all zeroes, it is recorded as:
* @addr//+size
*/
void
save_memory_segment_axis(config_dev_t *cdp, FILE *filep)
{
uint64_t *legion_mem_basep;
uint64_t data_chunk[32];
uint64_t off = 0;
uint64_t foo_addr = 0;
uint64_t skip_cnt = 0;
int i;
#define MEM_IMAGE_DATA_SIZE 32
/* Get the base address of the Guest Mem segment */
if (cdp->dev_typep->dev_cacheable(cdp->addrp, DA_Load, 0, (uint8_t**)&legion_mem_basep) == 0)
fatal("Failed to locate base address for memory segment @ 0x%llx\n", cdp);
fprintf(filep, "\n//Memory from %llx to %llx range %llx - %p",
cdp->addrp->baseaddr,
cdp->addrp->topaddr,
cdp->addrp->range,
cdp);
/*
* process memory in 256 byte chunks
*/
while (off <= (cdp->addrp->range - 256)) {
bool_t has_data = false;
uint64_t start_addr;
uint64_t legion_addr;
start_addr = cdp->addrp->baseaddr + off;
legion_addr = start_addr;
/*
* Scan a 256 Byte chunk of memory at a time
* and mark it if it has any non-zero data.
*/
for (i = 0; i < MEM_IMAGE_DATA_SIZE; i++) {
data_chunk[i] = *legion_mem_basep++;
/*
* apply any memory patch here
*/
if (options.save_restore.mem_patch.addr != 0x0) {
if (legion_addr == options.save_restore.mem_patch.addr) {
printf("Patching addr (0x%llx) from 0x%llx to 0x%llx\n",
legion_addr, data_chunk[i],
options.save_restore.mem_patch.val);
data_chunk[i] = options.save_restore.mem_patch.val;
}
}
off += 8;
legion_addr += 8;
if (data_chunk[i] != 0ULL) {
/*
* There's non-zero data in this chunk
* so we flag this so we can print the
* contents of the 256byte chunk.
*/
has_data = true;
}
}
/*
* If memory chunk is non-zero, print it
* otherwise, keep a track of zero chunks
* so we can print a summary before the next
* non-zero chunk.
*/
if (has_data) {
/* print addr for first chunk of memory */
if (start_addr == cdp->addrp->baseaddr) {
fprintf(filep, "\n@%llx\n", start_addr);
}
/*
* We've got data to print, lets check if we have
* skipped over any zero chunks of memory and print a
* summary of these first.
*/
if (skip_cnt != 0) {
fprintf(filep, "\n@%llx//+%llx\n",
foo_addr, skip_cnt * 256);
skip_cnt = 0;
foo_addr = 0x0ULL;
/*
* Now, print the starting @addr of the new
* non-zero chunk
*/
fprintf(filep, "\n@%llx\n", start_addr);
}
/* Print the 256 bytes of data */
for (i=0; i< MEM_IMAGE_DATA_SIZE; i++) {
fprintf(filep, "%016llx ", data_chunk[i]);
if ((i & 0x3) == 0x3)
fprintf(filep, "\n");
}
} else {
/* chunk was all zeros so, just keep a count */
if (skip_cnt == 0) {
/* save start address of zero chunk */
foo_addr = start_addr;
}
skip_cnt++;
}
}
/* Before we finish, check if we have any zero chunks to print */
if (skip_cnt != 0) {
fprintf(filep, "\n@%llx//+%llx\n",
foo_addr, skip_cnt * 256);
}
}
void
save_memory_segment_binary(config_dev_t *cdp, FILE *filep)
{
uint64_t *legion_mem_basep;
uint64_t *datap;
uint64_t size = cdp->addrp->range;
uint64_t start_addr = cdp->addrp->baseaddr;
uint64_t end_addr = cdp->addrp->baseaddr + size;
char filename[MAXPATHLEN];
int fd = 0;
static int file_cnt = 0;
mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO;
/*
* Open new file
* mmap in new file
* bcopy from legion memory to new file
* unmap file
*/
/* Get the base address of the Guest Mem segment */
if (cdp->dev_typep->dev_cacheable(cdp->addrp, DA_Load, 0, (uint8_t**)&legion_mem_basep) == 0)
fatal("Failed to locate base address for memory segment @ 0x%llx\n", cdp);
sprintf(filename, "mem.image.%d", file_cnt++);
lprintf(-1, "Writing %s basep=0x%llx, range=0x%llx (0x%llx)\n",
filename, start_addr, size, legion_mem_basep);
/* if file already exists, delete it */
if (file_exists(filename))
unlink(filename);
/* Open new output file and set size */
if ((fd = open(filename, O_RDWR | O_CREAT, mode)) == -1)
fatal("Error opening %s for writing\n", filename);
/* Set the size of the file by seeking to the end and write NULL */
if (lseek(fd, size-1, SEEK_SET) < 0)
fatal("Could not create file %s of size [0x%llx]",
filename, size);
if (write(fd, "", 1) != 1)
fatal("Could not write to file %s of size [0x%llx]",
filename, size);
/* mmap file file */
datap = (uint64_t*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (datap == MAP_FAILED)
fatal("Failed to mmap %s\n", filename);
/* bcopy from legion memory segment to newly mmaped file */
bcopy(legion_mem_basep, datap, size);
/*
* apply any memory patch to the newly created binary
*/
if (options.save_restore.mem_patch.addr != 0x0) {
uint64_t *patch_addr;
uint64_t offset;
if ((start_addr <= options.save_restore.mem_patch.addr) &&
(end_addr >= options.save_restore.mem_patch.addr)) {
offset = options.save_restore.mem_patch.addr - start_addr;
patch_addr = (uint64_t *)((uint64_t)datap + (uint64_t)offset);
printf("Patching addr (0x%llx) from 0x%llx to 0x%llx\n",
options.save_restore.mem_patch.addr, *patch_addr,
options.save_restore.mem_patch.val);
/* apply patch */
*patch_addr = options.save_restore.mem_patch.val;
}
}
if (munmap((void*)datap, size) != 0)
fatal("Error munmapping file %s\n", filename);
/* write out legion memory directive to load this back in */
fprintf(filep, "\ndevice \"memory\" 0x%llx +0x%llx {\n" \
" load +0x0 \"%s\"; \n" \
"}\n",
start_addr, size, filename);
close(fd);
}
bool_t
ss_save_state(simcpu_t *sp)
{
domain_t *domainp = sp->config_procp->domainp;
config_dev_t *cdp = NULL;
FILE *mem_image_filep = 0;
FILE *config_filep = 0;
if (options.save_restore.legion_format) {
/*
* For legion, we dump out memory images in binary
* format and then print a partial legion config file
* called restore.conf .
* This file contains the memory directives for loading
* these binary memory images back into legion.
*/
lprintf(sp->gid, "ss_save_state about to save system memory " \
"in binary format\nWait for Legion to save each memory " \
"image and print a Completed message.\n");
if ((config_filep = fopen("restore.conf", "w")) == NULL)
fatal("Error opening %s for writing\n", "restore.conf");
/* Locate the all memory segments, save them out in binary format */
for (cdp = domainp->device.listp; cdp!=(config_dev_t*)0; cdp=cdp->nextp) {
if (streq(cdp->dev_typep->dev_type_namep, "memory")) {
save_memory_segment_binary(cdp, config_filep);
}
}
fclose(config_filep);
} else {
/*
* For axis, we dump out memory images in txt format
*/
lprintf(sp->gid, "ss_save_state about to save system " \
"memory to %s in Axis format\n",
options.save_restore.filenamep);
if ((mem_image_filep =
fopen(options.save_restore.filenamep, "w")) == NULL) {
fatal("Error opening %s for writing\n",
options.save_restore.filenamep);
}
/* Locate the all memory segments, save them to text format */
for (cdp = domainp->device.listp; cdp!=(config_dev_t*)0; cdp=cdp->nextp) {
if (streq(cdp->dev_typep->dev_type_namep, "memory")) {
save_memory_segment_axis(cdp, mem_image_filep);
}
}
fclose(mem_image_filep);
}
return (true);
}