* ========== 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
* 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 <string.h> /* memcpy/memset */
#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.
#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
* [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))
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
);
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
,
* Global array for calculating page size shift bits
* The sun4v page size is 4 bits
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 };
#error "No valid processor defined."
uint64_t ss_ext_signal(config_proc_t
* config_procp
, ext_sig_t sigtype
,
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(
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
#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 { \
_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 ]); ); \
#define SET_ERROR_CHECK(_sp) do { \
if (_sp->error_enabled) \
(_newstate == _sp->error_priv) ? true : false; \
#define SET_ERROR_CHECK(_sp) do { } while (0)
#define V9_STATE_CHANGE(_isp, _iv9p, _inewstate) do { \
sparcv9_cpu_t * _v9p = (_iv9p); \
sparcv9_state_t _newstate = (_inewstate); \
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); \
#define V9_PSTATE_AM_CHANGED(_v9p) do { \
#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 { \
(_nsp)->pic0 += (_new - (_nsp)->pic0_sample_base); \
(_nsp)->pic0_sample_base = _new; \
(_nsp)->pic1 += (ICOUNT(_sp) - (_nsp)->pic1_sample_base); \
(_nsp)->pic1_sample_base = _new; \
#define RESET_PIC_SAMPLE_BASES( _sp, _nsp, _v9p ) do { \
(_nsp)->pic0_sample_base = RAW_TICK(_v9p); \
(_nsp)->pic1_sample_base = ICOUNT(_sp); \
ss_proc_t
* ss_proc_alloc(config_proc_t
* config_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;
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;
procp
->proc_debug
.perf_cycle_gap
= PERF_CYCLE_GAP
;
procp
->proc_debug
.exit_at
= 0x0ULL
;
void extract_error_op(error_conf_t
* errorconfp
)
errorconfp
->op_namep
= strdup(lex
.strp
);
if (streq(lex
.strp
,"ifetch"))
else if (streq(lex
.strp
,"ld"))
else if (streq(lex
.strp
,"asi_ld"))
else if (streq(lex
.strp
,"st"))
else if (streq(lex
.strp
,"any"))
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
;
lex_fatal("unknown error type parsing error config");
void dump_errconf(error_conf_t
* ep
) {
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
);
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
;
lex_fatal("unexpected end of file parsing cpu");
if (tok
== T_R_Brace
) break;
lex_fatal("token expected in SunSPARC processor specification");
if (streq(lex
.strp
,"cycle")) {
errorconfp
->cycle
= parse_number_assign();
if (streq(lex
.strp
,"type")) {
extract_error_type(errorconfp
);
if (streq(lex
.strp
,"op")) {
extract_error_op(errorconfp
);
if (streq(lex
.strp
,"priv")) {
extract_error_priv(errorconfp
);
lex_fatal("unknown token %s in error specification",
/* 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
;
error_conf_t
* new_errconf(error_op_t op
, error_type_t type
) {
ep
= Xcalloc(1, error_conf_t
);
ep
->op_namep
= strdup("unknown");
ep
->type_namep
= ep
->op_namep
;
ep
->priv_namep
= ep
->op_namep
;
error_conf_t
* find_errconf(simcpu_t
* sp
, error_op_t op
, error_type_t type
) {
ep
= sp
->errorp
->errlistp
;
if ((ep
->op
& op
) && (ep
->type
& type
)) break;
* 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
)) {
return (sp
->errorp
->errlistp
? true : false);
void clear_errflags(simcpu_t
* sp
) {
sp
->error_enabled
= 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
)
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;
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
);
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");
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);
if (streq(lex
.strp
,"ver")) {
procp
->ver
= parse_number_assign() &
(0xFFFFFFFFFFULL
<<SS_VER_MASK_OFFSET
);
while (tok
!= T_S_Colon
) {
if (streq(lex
.strp
,"mask")) {
if (streq(lex
.strp
,"impl")) {
if (streq(lex
.strp
,"manuf")) {
printf("\nUnknown ver option %s\n", lex
.strp
);
if (streq(lex
.strp
,"clkfreq")) {
procp
->clkfreq
= parse_number_assign();
if (streq(lex
.strp
,"core_mask")) {
core_mask
= parse_number_assign();
if (streq(lex
.strp
,"cores")) {
ncores
= parse_number_assign();
if (streq(lex
.strp
, "vthreads") || streq(lex
.strp
, "strands")) {
nvthreads
= parse_number_assign();
if (streq(lex
.strp
,"nwins")) {
procp
->nwins
= parse_number_assign();
if (streq(lex
.strp
,"nglobals")) {
procp
->nglobals
= parse_number_assign();
if (streq(lex
.strp
,"itlb")) {
ss_parse_tlb(&procp
->itlbspec
);
if (streq(lex
.strp
,"dtlb")) {
ss_parse_tlb(&procp
->dtlbspec
);
if (streq(lex
.strp
,"nofpu")) {
if (streq(lex
.strp
, "crypto_synchronous")) {
procp
->crypto_synchronous
= !!parse_number_assign();
#if ERROR_INJECTION /* { */
if (streq(lex
.strp
,"error")) {
#endif /* ERROR_INJECTION } */
#if PERFORMANCE_CHECK /* { */
if (streq(lex
.strp
,"exit_at")) {
procp
->proc_debug
.exit_at
= parse_number_assign();
if (streq(lex
.strp
,"perf_cycle_gap")) {
procp
->proc_debug
.perf_cycle_gap
= parse_number_assign();
#endif /* PERFORMANCE_CHECK } */
if (streq(lex
.strp
, "debug_hook")) {
lex_fatal("Cannot have more than one debug_hook definition in conf file");
debug_hook_defined
= true;
#if ERROR_TRAP_GEN /* { */
if (streq(lex
.strp
,"error_asi")) {
ss_error_asi_parse(procp
, false);
if (streq(lex
.strp
,"error_event")) {
ss_error_event_parse(procp
, false);
if (streq(lex
.strp
,"error_reload_file_name")) {
ss_error_parse_filename(procp
);
#endif /* ERROR_TRAP_GEN } */
if (ss_parse_proc_entry(procp
, domainp
) == true) {
/* Handled processor specific token */
lex_fatal("unknown token %s in cpu specification", lex
.strp
);
lex_unget(); /* put the r brace back for the caller ! FIXME */
dump_errconf(domainp
->errlistp
);
* Round out with values that didn't get set
if (procp
->clkfreq
== 0) lex_fatal("clkfreq not specified in processor definition");
/* nvthreads and ncores are for backward compatibility */
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
;
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");
procp
->ver
= SS_MAKE_VER_UPPER40(manuf
, impl
, mask
);
procp
->ver
|= SS_MAKE_VER_LOWER24(procp
->nglobals
, procp
->maxtl
,
* 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 */
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")) {
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")) {
lex_fatal("unknown token %s in cpu TLB specification", lex
.strp
);
ss_check_vahole(simcpu_t
* sp
, tvaddr_t pc
)
#if /* defined(NIAGARA1) || */ defined(NIAGARA2) /* { */
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
);
init_tick_counter_scale(tick_counter_t
*tcp
, uint64_t freq
)
/* We convert gethrtime() to the simulated tick_counter frequency. */
tcp
->scale
= (double)freq
/ (double)NANOSEC
;
tcp
->scale_recip
= 1.0 / tcp
->scale
;
base_hrtime
= RAW_HRTIME
;
for (n
= 0; m
!= 0; m
&= m
-1)
for (n
= 0; m
!= 0; m
>>= STRANDSPERCORE
)
* 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 idx
, core
, vthread
;
uint64_t core_avail
= 0, core_running
= 0;
ss_tlb_t
* dtlbp
, * itlbp
, * tlbp
;
sparcv9_cpu_t
* tick_v9p
= NULL
;
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
->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
;
ASSERT(ncpus
<= STRANDS_PER_CHIP
);
* Rock has only one Unified (IMMU and DMMU) TLB per chip.
pnp
->tlbp
= Xcalloc(1, ss_tlb_t
);
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_write_dis
= true;
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 /* { */
pnp
->mod_arith_p
= (mod_arith_t
*) Xcalloc(ncores
, mod_arith_t
);
(void) pthread_mutex_init(&pnp
->mod_arith_p
->lock
, NULL
);
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 /* INTERNAL_BUILD } */
/* Initialised to zeros by default */
pnp
->sparc_power_mgmtp
= Xcalloc(ncores
, sparc_power_mgmt_t
);
#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;
pthread_mutex_init(&pnp
->ucr_lock
, NULL
);
pthread_mutex_init(&pnp
->tw_lock
, NULL
);
#if ERROR_TRAP_GEN /* { */
ss_error_trap_proc_init(config_procp
);
#endif /* } ERROR_TRAP_GEN */
#if defined(ROCK) /* { */
tmp_core_mask
= pnp
->core_mask
;
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)
/* 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) /* { */
for (vthread
=0; vthread
< STRANDSPERCORE
; vthread
++) {
/* skip missing strands */
if ((tmp_core_mask
& (1u << vthread
)) == 0)
/* Need something here to determine which
* scheduler wheel the simcpu_t ends up on.
v9p
= sparcv9_cpu_alloc(domainp
,
pnp
->nwins
, pnp
->nglobals
, pnp
->maxtl
,
* Tie to SunSPARC CPU specific info
nsp
= &(pnp
->ss_strandp
[idx
]);
EXEC_WARNING(("ss_init: core: %u strand: %u idx: %u nsp: %p sp: %p", core
, vthread
, idx
, nsp
, sp
));
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
->asi_access
= ss_asi_access
;
v9p
->post_precise_trap
= ss_post_precise_trap
;
* Initialize shared tick/stick register pointers.
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
init_tick_counter_scale(&tick_v9p
->_tick
,
/* STICK is an alias of TICK */
v9p
->stick
= v9p
->tick
= &tick_v9p
->_tick
;
init_tick_counter_scale(&tick_v9p
->_tick
,
init_tick_counter_scale(&tick_v9p
->_stick
,
v9p
->tick
= &tick_v9p
->_tick
;
v9p
->stick
= &tick_v9p
->_stick
;
#error "No valid processor defined."
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
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.
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
nsp
->strand_reg
[SSR_HSCRATCHPAD_INDEX
] =
(pnp
->fake_chip_id
<< RK_CHIP_ID_SHIFT
);
DBGSCRATCH( lprintf(sp
->gid
, "HSCRATCH0 init 0x%llx\n",
nsp
->strand_reg
[SSR_HSCRATCHPAD_INDEX
]); );
* 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;
* 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;
* 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 is disabled by default in Legion. This is
* enabled by hypervisor, dumbreset (reset.s)
/* Floating Point Arithmetic Enable */
pthread_mutex_init(&nsp
->ifucr_lock
, NULL
);
#error "No valid processor defined."
#if ERROR_INJECTION /* { */
sp
->error_priv
= pnp
->pend_errlistp
->priv
;
#if PERFORMANCE_CHECK /* { */
/* FIXME: HACK TO GO AWAY */
sp
->last_hrtime
= gethrtime();
sp
->perf_target
= pnp
->proc_debug
.perf_cycle_gap
;
* Per-thread performance counter
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
RESET_PIC_SAMPLE_BASES(sp
, nsp
, v9p
);
* 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
* for Niagara 2, CPU's initial state is determined
* by the initial value of asi_core_running_status
if (((1ull << vcore_id
) & core_running
) == 0) {
DBG( lprintf(sp
->gid
, "cpu0x%llx (%s)\n",
RUNNABLE(sp
) ?"Unparked":"Parked"); );
#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
* 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
* Its like 'Highlander' - there can be only one !
SET_THREAD_STS_SFSM(pnp
, nsp
, THREAD_STS_TSTATE_RUN
);
SET_THREAD_STS_SFSM(pnp
, nsp
, THREAD_STS_TSTATE_IDLE
);
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"); );
#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
);
ss_reset_trap( sp
, SS_trap_power_on_reset
);
/* cleanup after "execution" */
sp
->config_procp
->proc_typep
->exec_cleanup(sp
);
pnp
->core_avail
= core_avail
;
* 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
* - cmp_regs.core_running_status stores
* 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 */
pthread_mutex_init(&pnp
->tick_en_lock
, NULL
);
#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
;
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;
pthread_mutex_init(&pnp
->thread_sts_lock
, NULL
);
#if defined(NIAGARA2) || defined(ROCK) /* { */
pthread_mutex_init(&pnp
->cmp_lock
, NULL
);
/*************************************************************
* 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
)
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
);
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("entries %u;\n", procp
->itlbspec
.nentries
);
if (procp
->itlbspec
.parity
)
pi("entries %u;\n", procp
->dtlbspec
.nentries
);
if (procp
->dtlbspec
.parity
)
#if defined(ROCK) /* Rock has a unified L2 TLB also */
pi("entries %u;\n", procp
->tlbspec
.nentries
);
if (procp
->tlbspec
.parity
)
bool_t
ss_dev_mem_access(config_proc_t
*config_procp
, tpaddr_t addr
, uint8_t *datap
,
uint64_t size
, dev_access_t type
)
config_addr_t
*config_addrp
;
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
));
* For now only handle cacheable device memory
extent
= config_addrp
->config_devp
->dev_typep
->dev_cacheable(config_addrp
, type
,
addr
-config_addrp
->baseaddr
, &bufp
);
EXEC_WARNING(("ss_dev_mem_access: physical address 0x%llx out of range 0x%llx",
* Do memory read/write between physical memory (pointed by 'bufp')
* and device memory (pointed by 'datap')
memcpy(datap
, bufp
, size
);
memcpy(bufp
, datap
, size
);
*------------------------------------------------------------
* 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
)
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
->config_procp
= config_procp
;
ndp
->strandp
= np
->strand
[0];
void ss_dbgr_detach(void * ptr
)
ss_dbgr_t
* ndp
= (ss_dbgr_t
*)ptr
;
/* Clean up SunSPARC specific debugger state FIXME */
* 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
;
ndp
->config_procp
->procp
,
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
;
ndp
->config_procp
->procp
,
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
;
ndp
->config_procp
->procp
,
ss_proc_t
*procp
= (ss_proc_t
*)procp_param
;
* We keep going until either there is no
* translation available, or the target memory
* *valid_lenp tells us how much succeeded
for (tx_count
= 0LL; tx_count
< tx_size
; tx_count
+= size
) {
if (size
>= (1LL<<30)) size
= 1LL<<30;
/* First translate if necessary vtop */
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
size
= ss_pmem_op(domainp
, procp
, strandp
, memop
, pa
, bufp
, size
);
/* returns the amount successfully handled - 0 if failed */
* 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 */
*vlenp
= 0x10000-(va
& 0xffff); /* round to end of 64K page ;-) */
static tpaddr_t
ss_pmem_op(
config_addr_t
*config_addrp
;
* 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
/* operate on the cacheable state */
memcpy( bufp
, cbp
, size
);
memcpy( cbp
, bufp
, size
);
*------------------------------------------------------------
*------------------------------------------------------------
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
);
/*------------------------------------------------------------*
*------------------------------------------------------------*/
* 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
)
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 */
* 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
/* 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 ... */
v9p
->post_precise_trap(sp
, (sparcv9_trap_type_t
)SS_trap_interrupt_vector_trap
);
* 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
);
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) {
for (i
=15; i
> v9p
->pil
; i
--) {
if ((v9p
->softint
>>i
)&1LL) {
v9p
->post_precise_trap(sp
, Sparcv9_trap_interrupt_level_1
+i
-1);
fatal("Internal error: softint triggered, but not found (softint=0x%llx, pil=0x%x)",
(uint64_t)v9p
->softint
, v9p
->pil
);
/* All disrupting traps are blockable in hpriv mode */
if (v9p
->pstate
.int_enabled
) {
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
);
if (v9p
->hstick_cmpr
.pending
) {
v9p
->post_precise_trap(sp
, (sparcv9_trap_type_t
)SS_trap_hstick_match
);
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
v9p
->post_precise_trap(sp
, (sparcv9_trap_type_t
)SS_trap_interrupt_vector_trap
);
if (v9p
->pstate
.int_enabled
) FIXME_WARNING(("Interrupts enabled in RED state not implemented!"));
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
)
* This is the temporary callback function for when we get a cycle count
* 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
)
v9p
= (sparcv9_cpu_t
*)(sp
->specificp
);
nsp
= v9p
->impl_specificp
;
npp
= (ss_proc_t
*)(sp
->config_procp
->procp
);
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
* Check for Performance Counter match
if (nsp
->pcr
& SS_PCR_UT_ST
) {
uint32_t old_pic0
, old_pic1
;
UPDATE_PIC(sp
, nsp
, v9p
);
/* 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
) {
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
); );
#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 (!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;
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;
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;
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)) {
* 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);
"options.save_restore.trigger_icount reached. trigger=0x%llx, icount=0x%llx\n",
dp
= sp
->config_procp
->domainp
;
for (i
=0; i
<dp
->procs
.count
; i
++) {
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
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
) {
npp
->pend_errlistp
= ep
->nextp
;
if (sp
->errorp
->errlistp
)
ep
->nextp
= sp
->errorp
->errlistp
;
sp
->errorp
->errlistp
= ep
;
pthread_mutex_unlock(&npp
->err_lock
);
sp
->error_enabled
= true;
sp
->error_check
= (v9p
->state
== sp
->error_priv
) ? true : false;
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
) {
gap
= hrt
- sp
->last_hrtime
;
mips
= (1.0e3
*(double)(ICOUNT(sp
) - sp
->prev_icount
)) / (double)gap
;
amips
=(1.0e3
*(double)ICOUNT(sp
)) / (double)sp
->total_hrtime
;
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
);
/* 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
));
sp
->perf_target
= now
+ npp
->proc_debug
.perf_cycle_gap
;
ASSERT(sp
->perf_target
> sp
->cycle
);
sp
->perf_target
+= npp
->proc_debug
.perf_cycle_gap
;
#endif /* PERFORMANCE_CHECK } */
ss_recomp_cycle_target(sp
);
/*------------------------------------------------------------*
* Privileged and status registers accessed here ...
*------------------------------------------------------------*/
uint64_t ss_read_pstate(sparcv9_cpu_t
* v9p
)
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;
static uint64_t ss_read_hpstate(sparcv9_cpu_t
* v9p
)
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;
hpstate
|= (1LL<<NA_HPSTATE_ENB_BIT
);
/* Nothing extra for NIAGARA2 */
ss_strand_t
* ssr
= (ss_strand_t
*)v9p
->impl_specificp
;
hpstate
|= ssr
->tick_npt
? (1LL<<HPSTATE_TNPT_BIT
) : 0;
#error "No valid processor defined."
* 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
)
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;
#endif /* FP_DECODE_DISABLED */
v9p
->pstate
.mm
= (val
>> V9_PSTATE_MM_BITS
) & V9_PSTATE_MM_MASK
; /* Ignore effects */
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
)
bool_t flag_icflush
= false;
bool_t flag_dcflush
= false;
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;
IMPL_WARNING(("HPSTATE.IBE not implemented (pc=0x%llx)", sp
->pc
));
IMPL_WARNING(("HPSTATE.TLZ not implemented (pc=0x%llx)", sp
->pc
));
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
));
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
;
#error "No valid processor defined."
if (v9p
->hpstate
.hpriv
) {
new_state
= V9_HyperPriv
;
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
)
ss_proc_t
* npp
= (ss_proc_t
*)sp
->config_procp
->procp
;
v9p
= (sparcv9_cpu_t
*)(sp
->specificp
);
val
= v9p
->tick
->offset
+ RAW_TICK(v9p
);
val
&= MASK64(62, 2); /* Low bits are clear on Niagara */
uint64_t ss_read_stick(simcpu_t
* sp
)
ss_proc_t
* npp
= (ss_proc_t
*)sp
->config_procp
->procp
;
v9p
= (sparcv9_cpu_t
*)(sp
->specificp
);
val
= v9p
->stick
->offset
+ RAW_STICK(v9p
);
val
&= MASK64(62, 2); /* Low bits are clear on Niagara */
val
= v9p
->stick
->offset
;
#define CALC_TARG(_tcmp) (options.walltime ? \
(TICK_TO_HRT((_tcmp)->compare - (_tcmp)->counter->offset, \
(_tcmp)->counter->scale_recip)) : \
((_tcmp)->compare - (_tcmp)->counter->offset))
#define CALC_TARG(_tcmp) ((_tcmp)->compare - \
(_tcmp)->counter->offset)
#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)) |
void ss_tick_cmpr_write(simcpu_t
* sp
, tick_compare_t
*tcmp
, uint64_t val
)
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
) {
* 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;
/* "Don't do match compare" */
* 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
)
npp
= (ss_proc_t
*)(sp
->config_procp
->procp
);
v9p
= (sparcv9_cpu_t
*)(sp
->specificp
);
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
);
* 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;
v9p
->tick_cmpr
.target
= 0ull;
if (v9p
->stick_cmpr
.compare
== v9p
->stick_cmpr
.counter
->offset
)
v9p
->stick_cmpr
.target
= sp
->cycle
+ 1;
v9p
->stick_cmpr
.target
= 0ull;
if (v9p
->hstick_cmpr
.compare
== v9p
->hstick_cmpr
.counter
->offset
)
v9p
->hstick_cmpr
.target
= sp
->cycle
+ 1;
v9p
->hstick_cmpr
.target
= 0ull;
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
);
#if ERROR_INJECTION /* { */
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
ss_strand_t
*nsp
= v9p
->impl_specificp
;
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
#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
);
* 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 (sp
->perf_target
< sp
->cycle_target
)
sp
->cycle_target
= sp
->perf_target
;
#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
);
error_cycle
= npp
->pend_errlistp
->cycle
;
pthread_mutex_unlock(&npp
->err_lock
);
if ((error_cycle
> now
) && (error_cycle
< sp
->cycle_target
)) {
sp
->cycle_target
= error_cycle
;
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",
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
)
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
;
val
&= 0x7fffffffffffffffLL
;
v9p
->tick
->offset
= val
- RAW_TICK(v9p
);
ss_recomp_tick_target(sp
);
void ss_write_stick(simcpu_t
* sp
, uint64_t val
)
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
;
val
&= 0x7fffffffffffffffLL
;
v9p
->stick
->offset
= val
- RAW_STICK(v9p
);
v9p
->stick
->offset
= val
;
ss_recomp_tick_target(sp
);
static void ss_read_state_reg(simcpu_t
* sp
, uint_t rdest
, uint_t state_reg
)
v9p
= (sparcv9_cpu_t
*)(sp
->specificp
);
nsp
= v9p
->impl_specificp
;
v9p
->post_precise_trap(sp
,
Sparcv9_trap_illegal_instruction
);
if (v9p
->state
== V9_User
&& v9p
->_tick
.non_priv_trap
) {
v9p
->post_precise_trap(sp
, Sparcv9_trap_privileged_action
);
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
if (v9p
->_tick
.non_priv_trap
) val
|= 1ULL<<63;
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
); );
val
= sp
->pc
; /* Use a macro - FIXME */
if (v9p
->pstate
.addr_mask
) val
&= MASK64(31,0); /* FIXME: SV9_ID125 */
/* 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 */
DBGFPRS( lprintf(sp
->gid
, "ss_read_state_reg: %%fprs=%x @ pc=0x%llx\n", val
, sp
->pc
); );
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
if (v9p
->state
== V9_User
) {
v9p
->post_precise_trap(sp
, Sparcv9_trap_privileged_opcode
);
DBGPIC( lprintf(sp
->gid
, "ss_read_state_reg: (rd %%pcr @ pc=0x%llx, val=0x%llx)\n",
nsp
->pcr
&= ~SS_PCR_CLEAR_ON_READ
;
* Need to create a proper pic value containing [pic1][pic0] where each pic
* 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
);
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
); );
v9p
->post_precise_trap(sp
,
Sparcv9_trap_illegal_instruction
);
v9p
->post_precise_trap(sp
, Sparcv9_trap_fp_disabled
);
if (v9p
->state
== V9_User
) {
v9p
->post_precise_trap(sp
, Sparcv9_trap_privileged_opcode
);
val
= ((v9p
->stick_cmpr
.pending
) ? (1LL<<16) : 0LL) |
((v9p
->tick_cmpr
.pending
) ? 1LL : 0LL );
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
case 0x17: /* tick_cmpr */
if (v9p
->state
== V9_User
) {
v9p
->post_precise_trap(sp
, Sparcv9_trap_privileged_opcode
);
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
); );
if (v9p
->state
== V9_User
&& v9p
->_stick
.non_priv_trap
) {
v9p
->post_precise_trap(sp
, Sparcv9_trap_privileged_action
);
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
if (v9p
->_stick
.non_priv_trap
) val
|= 1ULL<<63;
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
); );
case 0x19: /* stick_cmpr */
if (v9p
->state
== V9_User
) {
v9p
->post_precise_trap(sp
, Sparcv9_trap_privileged_opcode
);
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
); );
case 0x1a: /* strand_sts_reg */
v9p
->post_precise_trap(sp
, Sparcv9_trap_privileged_opcode
);
/* If a strand is reading it, it must be active... */
IMPL_WARNING(("ss_read_state_reg: (rd %%tsr @ pc=0x%llx) deprecated access to strand status register", sp
->pc
));
npp
= (ss_proc_t
*)(sp
->config_procp
->procp
);
i
= nsp
->core
* STRANDSPERCORE
;
val
<<= THREAD_STS_TSTATE_BITS
;
val
|= npp
->sfsm_state
[i
++];
val
<<= THREAD_STS_TSTATE_SHIFT
;
val
|= (nsp
->vcore_id
<< THREAD_STS_SHIFT
);
val
|= THREAD_STS_SPEC_EN
;
val
|= THREAD_STS_ACTIVE
;
"%%tsr read: pc=0x%llx, o7=0x%llx, val=0x%llx\n",
sp
->pc
, sp
->intreg
[Reg_sparcv9_o7
], val
); );
DBGTM( lprintf(sp
->gid
, "%%cps read: "
"pc=0x%llx, o7=0x%llx, val=0x%llx\n",
sp
->pc
, sp
->intreg
[Reg_sparcv9_o7
], val
); );
/* No unimplemented registers currently. */
FIXME_WARNING(("ss_read_state_reg: (rd) Unimplemented register 0x%x @ pc=0x%llx", state_reg
, sp
->pc
));
v9p
->post_precise_trap(sp
, Sparcv9_trap_illegal_instruction
);
if (rdest
!= Reg_sparcv9_g0
) sp
->intreg
[rdest
] = val
; /* Use a macro - FIXME */
static void ss_write_state_reg(simcpu_t
* sp
, uint_t state_reg
, uint64_t val
)
v9p
= (sparcv9_cpu_t
*)(sp
->specificp
);
nsp
= (ss_strand_t
*)v9p
->impl_specificp
;
EXEC_WARNING(("TM ERROR: TM active in 'ss_write_state_reg' function pc=0x%llx.", sp
->pc
));
nsp
->tm_fail(sp
, V9_CPS_INSTR
);
v9p
->post_precise_trap(sp
,
Sparcv9_trap_illegal_instruction
);
sp
->v9_y
= val
& MASK64(31,0);
sp
->v9_ccr
= val
& V9_CCR_MASK
;
sp
->v9_asi
= val
& V9_ASI_MASK
;
#if defined(NIAGARA1) || defined(NIAGARA2)
v9p
->post_precise_trap(sp
, Sparcv9_trap_privileged_opcode
);
v9p
->post_precise_trap(sp
, Sparcv9_trap_illegal_instruction
);
/* On niagara stick is an alias for tick */
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
)); );
v9p
->post_precise_trap(sp
, Sparcv9_trap_illegal_instruction
);
#error "No valid processor defined."
/* 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.
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;
#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;
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
if (v9p
->state
== V9_User
) {
v9p
->post_precise_trap(sp
, Sparcv9_trap_privileged_opcode
);
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
* 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
);
ss_recomp_cycle_target(sp
); /* catch next overflow event */
/* Stopping counting? Collect last samples. */
if ((nsp
->pcr
& SS_PCR_UT_ST
) != 0)
UPDATE_PIC(sp
, nsp
, v9p
);
if (SS_PCR_TEST_OVF_PENDING(nsp
->pcr
)) {
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
); );
if (v9p
->state
== V9_User
&& (nsp
->pcr
& SS_PCR_PRIV
) != 0) {
v9p
->post_precise_trap(sp
, Sparcv9_trap_privileged_action
);
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 */
v9p
->post_precise_trap(sp
,
Sparcv9_trap_illegal_instruction
);
v9p
->post_precise_trap(sp
, Sparcv9_trap_fp_disabled
);
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
;
case 0x14: /* set softint register */
if (v9p
->state
== V9_User
) {
v9p
->post_precise_trap(sp
, Sparcv9_trap_privileged_opcode
);
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
); );
case 0x15: /* clear softint register */
if (v9p
->state
== V9_User
) {
v9p
->post_precise_trap(sp
, Sparcv9_trap_privileged_opcode
);
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
); );
if (v9p
->state
== V9_User
) {
v9p
->post_precise_trap(sp
, Sparcv9_trap_privileged_opcode
);
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, "
sp
->pc
, sp
->intreg
[Reg_sparcv9_o7
], v9p
->softint
,
v9p
->stick_cmpr
.pending
, v9p
->tick_cmpr
.pending
); );
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
case 0x17: /* tick_cmpr */
if (v9p
->state
== V9_User
) {
v9p
->post_precise_trap(sp
, Sparcv9_trap_privileged_opcode
);
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
);
#if defined(NIAGARA1) || defined(NIAGARA2)
v9p
->post_precise_trap(sp
, Sparcv9_trap_privileged_opcode
);
v9p
->post_precise_trap(sp
, Sparcv9_trap_illegal_instruction
);
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
)); );
v9p
->post_precise_trap(sp
, Sparcv9_trap_illegal_instruction
);
#error "No valid processor defined."
case 0x19: /* stick_cmpr */
if (v9p
->state
== V9_User
) {
v9p
->post_precise_trap(sp
, Sparcv9_trap_privileged_opcode
);
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
);
case 0x1a: /* strand_sts_reg */
v9p
->post_precise_trap(sp
, Sparcv9_trap_privileged_opcode
);
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
));
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
));
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
;
/* No unimplemented registers currently. */
FIXME_WARNING(("ss_write_state_reg: (wr) Unimplemented register 0x%x @ pc=0x%llx", state_reg
, sp
->pc
));
v9p
->post_precise_trap(sp
, Sparcv9_trap_illegal_instruction
);
static void ss_read_priv_reg(simcpu_t
* sp
, uint_t rdest
, uint_t priv_reg
)
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
);
if (v9p
->tl
== 0) goto illegal_instruction
;
val
= N_TPC(v9p
, v9p
->tl
);
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
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
));
if (v9p
->tl
== 0) goto illegal_instruction
;
val
= N_TNPC(v9p
, v9p
->tl
);
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
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
));
if (v9p
->tl
== 0) goto illegal_instruction
;
val
= N_TSTATE(v9p
, v9p
->tl
);
if (v9p
->tl
== 0) goto illegal_instruction
;
val
= N_TT(v9p
, v9p
->tl
);
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
if (v9p
->_tick
.non_priv_trap
) val
|= 1ULL<<63;
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
); );
val
= ss_read_pstate(v9p
);
case 0xb: /* canrestore */
val
= (v9p
->wstate_normal
<< V9_WSTATE_NORMAL_BITS
) |
(v9p
->wstate_other
<< V9_WSTATE_OTHER_BITS
);
v9p
->post_precise_trap(sp
, Sparcv9_trap_illegal_instruction
);
if (rdest
!= Reg_sparcv9_g0
) sp
->intreg
[rdest
] = val
;
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
);
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
);
EXEC_WARNING(("TM ERROR: TM active in 'ss_write_priv_reg' function pc=0x%llx.", sp
->pc
));
nsp
->tm_fail(sp
, V9_CPS_INSTR
);
if (v9p
->tl
== 0) goto illegal_instruction
;
if (v9p
->pstate
.addr_mask
) EXEC_WARNING(("writing to tpc with pstate.am=1"));
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
N_TPC(v9p
, v9p
->tl
) = val
; /* FIXME: SV9_ID125 issue ? */
if (v9p
->tl
== 0) goto illegal_instruction
;
if (v9p
->pstate
.addr_mask
) EXEC_WARNING(("writing to tnpc with pstate.am=1"));
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
N_TNPC(v9p
, v9p
->tl
) = val
; /* FIXME: SV9_ID125 issue ? */
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
) );
if (v9p
->tl
== 0) goto illegal_instruction
;
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
););
DBGTSTACK(log_printf(sp
->gid
, "Post-TT write - TT unchanged\n"););
DBGTSTACK( log_unlock(); );
#if defined(NIAGARA1) || defined(NIAGARA2)
goto illegal_instruction
;
/* On niagara stick is an alias for tick */
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
)); );
goto illegal_instruction
;
#error "No valid processor defined."
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
v9p
->tba
= val
& V9_TBA_MASK
;
ss_write_pstate(sp
, val
); /* like to change states */
break; /* pc updated as usual */
case 7: /* tl - saturates */
if (v9p
->state
== V9_Priv
) {
EXEC_WARNING(("privileged write to %%tl of %u saturated to %u (%%pc=0x%llx)", val
, Q_MAXPTL
, sp
->pc
));
/* Unsafe raising of %tl, trash values */
/* bogus values must pass the VA48_ASSERT */
N_TPC(v9p
, val
) = 0xffffbeefdeadbeecull
;
N_TNPC(v9p
, val
) = 0xffffbeefdeadbeecull
;
((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 */
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
;
v9p
->pil
= val
& V9_PIL_MASK
;
case 9: /* cwp - saturates */
#if 0 /* this is expected */
EXEC_WARNING(("write to %%cwp of %u saturated to %u (%%pc=0x%llx)",
sparcv9_active_window(sp
, v9p
->cwp
);
case 10: /* cansave - saturates */
EXEC_WARNING(("write to %%cansave of %u saturated to %u (%%pc=0x%llx)",
case 11: /* canrestore - saturates */
EXEC_WARNING(("write to %%canrestore of %u saturated to %u (%%pc=0x%llx)",
case 12: /* cleanwin - saturates */
EXEC_WARNING(("write to %%cleanwin of %u saturated to %u(%%pc=0x%llx)",
case 13: /* otherwin - saturates */
EXEC_WARNING(("write to %%otherwin of %u saturated to %u (%%pc=0x%llx)",
v9p
->wstate_normal
= (val
>> V9_WSTATE_NORMAL_BITS
) & V9_WSTATE_MASK
;
v9p
->wstate_other
= (val
>> V9_WSTATE_OTHER_BITS
) & V9_WSTATE_MASK
;
case V9_User
: abort(); /* shouldn't get here */
EXEC_WARNING(("privileged write to %%gl of %u saturated to %u (%%pc=0x%llx)", val
, Q_MAXPGL
, sp
->pc
));
if (val
>= v9p
->nglobals
) {
EXEC_WARNING(("hyperprivileged write to %%gl of %u saturated to %u (%%pc=0x%llx)", val
, v9p
->nglobals
- 1, sp
->pc
));
/* Unsafe raising of %gl, trash values */
if (v9p
->state
== V9_Priv
&& val
> v9p
->gl
) {
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 */
sparcv9_active_globals(sp
, v9p
->gl
);
v9p
->post_precise_trap(sp
, Sparcv9_trap_illegal_instruction
);
static void ss_read_hyp_priv_reg(simcpu_t
* sp
, uint_t rdest
, uint_t priv_reg
)
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
;
val
= ss_read_hpstate(v9p
);
if (v9p
->tl
== 0) goto illegal_instruction
;
val
= N_HTSTATE(v9p
, v9p
->tl
);
val
= v9p
->hstick_cmpr
.pending
? 1 : 0;
IMPL_WARNING(("ss_read_hyp_priv_reg: (rdhpr %%halt, <reg> @ pc=0x%llx) halt not implemented", sp
->pc
));
#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
); );
v9p
->post_precise_trap(sp
, Sparcv9_trap_illegal_instruction
);
if (rdest
!= Reg_sparcv9_g0
) sp
->intreg
[rdest
] = val
;
static void ss_write_hyp_priv_reg(simcpu_t
* sp
, uint_t priv_reg
, uint64_t val
)
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
;
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
);
ss_write_hpstate(sp
, false, val
);
if (v9p
->tl
== 0) goto illegal_instruction
;
#define SS_HTSTATE_MASK 0x0425
#define SS_HTSTATE_MASK 0x0425
#define SS_HTSTATE_MASK 0xf025
#error "No processor defined"
N_HTSTATE(v9p
, v9p
->tl
) = val
& SS_HTSTATE_MASK
;
v9p
->hstick_cmpr
.pending
= 1;
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
); );
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
v9p
->htba
= val
& V9_HTBA_MASK
;
/* read only register ! */
goto illegal_instruction
;
IMPL_WARNING(("ss_write_hyp_priv_reg: (wrhpr <reg>, %%halt @ pc=0x%llx) halt not implemented", sp
->pc
));
#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
);
v9p
->post_precise_trap(sp
, Sparcv9_trap_illegal_instruction
);
/*************************************************************/
void ss_done_retry(simcpu_t
* sp
, bool_t is_done
)
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
);
v9p
->post_precise_trap(sp
, Sparcv9_trap_illegal_instruction
);
new_pc
= N_TPC(v9p
, v9p
->tl
); /* retry */
new_npc
= N_TNPC(v9p
, v9p
->tl
);
new_pc
= N_TNPC(v9p
, v9p
->tl
); /* done */
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"));
/* setup the new pc and 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
;
warning("restoring cwp with value too large from TSTATE (%u) ; saturating",win
);
sparcv9_active_window(sp
, v9p
->cwp
);
gl
= (val
>> V9_TSTATE_GL_SHIFT
) & V9_TSTATE_GL_MASK
;
case V9_User
: abort(); /* shouldn't get here */
EXEC_WARNING(("Restoring gl with value too large from TSTATE (%u) ; saturating to %u", gl
, Q_MAXPGL
));
if (gl
>= v9p
->nglobals
) {
EXEC_WARNING(("Restoring gl with value too large from TSTATE (%u) ; saturating to %u", gl
, v9p
->nglobals
-1));
sparcv9_active_globals(sp
, v9p
->gl
);
ss_write_pstate( sp
, (val
>> V9_TSTATE_PSTATE_SHIFT
) & V9_TSTATE_PSTATE_MASK
);
/* FIXME: care not to overwrite the ENB bit in HPSTATE if we ever implement it */
ss_write_hpstate( sp
, true, N_HTSTATE(v9p
, v9p
->tl
) );
/*************************************************************/
void ss_jpriv(simcpu_t
* sp
, tvaddr_t addr
)
v9p
= (sparcv9_cpu_t
*)(sp
->specificp
);
nsp
= v9p
->impl_specificp
;
if (v9p
->state
!= V9_HyperPriv
) {
v9p
->post_precise_trap(sp
, Sparcv9_trap_illegal_instruction
);
v9p
->post_precise_trap(sp
, Sparcv9_trap_mem_address_not_aligned
);
/* setup the new pc and npc */
/* clear HPSTATE.hpriv */
v9p
->hpstate
.hpriv
= false;
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
* 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_state_t new_state
;
ss_trap_type_t RED_tt
= 0;
ss_trap_type_t tt
; /* the trap we deliver */
ss_trap_type_t actual_tt
; /* the trap that occurred */
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
) {
/* Only clear if we already detected it was not clear
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"); );
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
->intreg
[Reg_sparcv9_o5
] == 0x12) {
/* CPU yield hcall - yield this CPU's quantum */
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
->intreg
[Reg_sparcv9_o6
],
sp
->intreg
[Reg_sparcv9_o7
]);
lprintf(sp
->gid
, "hcall hyper-fast:"
"(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_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
->intreg
[Reg_sparcv9_o6
],
sp
->intreg
[Reg_sparcv9_o7
]); );
* 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
:
ss_reset_trap(sp
, actual_tt
);
case N2_trap_control_word_queue_interrupt
:
case N2_trap_modular_arithmetic_interrupt
:
* 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
));
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
:
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
,
* strand as the trap occurred.
ASSERT(oldtl
<= v9p
->maxtl
);
if (oldtl
== v9p
->maxtl
) {
RED_tt
= SS_trap_watchdog_reset
;
if (v9p
->tl
== v9p
->maxtl
)
RED_tt
= SS_trap_RED_state_exception
;
if (v9p
->pstate
.addr_mask
) {
N_TPC( v9p
, tl
) = sp
->pc
& MASK64(31,0);
N_TNPC( v9p
, tl
) = sp
->npc
& MASK64(31,0);
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
;
((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
);
log_printf(sp
->gid
, "Precise trap\n");
/* Now for window traps there are some window reg adjustments
/* Modify cwp for window traps. */
/* Even for hypervisor versions ? */
if (actual_tt
== SS_trap_clean_window
) {
v9p
->cwp
= INC_MOD(v9p
->cwp
, v9p
->nwins
);
sparcv9_active_window(sp
, v9p
->cwp
);
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
);
if ( actual_tt
>= SS_trap_fill_0_normal
&& actual_tt
<=(SS_trap_fill_7_other
+3)) {
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;
#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 ( ss_trap_list
[actual_tt
].from_user
) {
fatal("Hardware should not be able to generate TT=0x%x from user mode", actual_tt
);
if (tl
<=Q_MAXPTL
) goto priv_trap_setup
;
goto hyperpriv_trap_setup
;
goto hyperpriv_trap_setup
;
switch ( ss_trap_list
[actual_tt
].from_priv
) {
fatal("Hardware should not be able to generate TT=0x%x from privileged mode", actual_tt
);
if (tl
<=Q_MAXPTL
) goto priv_trap_setup
;
goto hyperpriv_trap_setup
;
goto hyperpriv_trap_setup
;
switch ( ss_trap_list
[actual_tt
].from_hyperpriv
) {
fatal("Hardware should not be able to generate TT=0x%x from hyper-privileged mode", actual_tt
);
fatal("Hardware cant generate a trap 0x%x to priv mode from hyper-priv mode", actual_tt
);
if (RED_tt
!= 0) goto REDstate_trap_setup
;
goto hyperpriv_trap_setup
;
RED_tt
= SS_trap_RED_state_exception
;
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
,
v9p
->had_RED_trap
= true;
goto REDstate_trap_setup
;
abort(); /* internal error if we get here ! */
* OK setup for trap into privileged mode
new_trap_pc
= v9p
->tba
| (oldtl
!= 0 ? (1<<14) : 0) | (tt
<< 5);
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;
/* adjust and saturate GL appropriately */
ASSERT( v9p
->gl
<= Q_MAXPGL
); /* internal error if otherwise */
if (v9p
->gl
< Q_MAXPGL
) {
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
);
* OK setup for trap into hyper-privileged mode
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;
v9p
->hpstate
.tlz
= false;
v9p
->hpstate
.hpriv
= true;
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
v9p
->hpstate
.ibe
= false; /* not supported ? FIXME */
ASSERT( v9p
->gl
<= (v9p
->nglobals
-1) );
if (v9p
->gl
< (v9p
->nglobals
-1)) {
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
;
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
.tlz
= false;
v9p
->hpstate
.hpriv
= true;
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
v9p
->hpstate
.ibe
= false; /* not supported ? FIXME */
ASSERT( v9p
->gl
<= (v9p
->nglobals
-1) );
if (v9p
->gl
< (v9p
->nglobals
-1)) {
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
);
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() */
if (actual_tt
== SS_trap_legion_save_state
) {
new_trap_pc
= options
.save_restore
.trap_pc
;
"ss_take_exception : SS_trap_legion_save_state : divert to %%pc 0x%llx\n",
#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
)
v9p
= (sparcv9_cpu_t
*)(sp
->specificp
);
nsp
= v9p
->impl_specificp
;
npp
= (ss_proc_t
*)(sp
->config_procp
->procp
);
case SS_trap_power_on_reset
:
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
.int_enabled
= false;
v9p
->hpstate
.hpriv
= true;
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
V9_STATE_CHANGE(sp
, v9p
, V9_RED
);
/* stuff for tick interrupts */
v9p
->_tick
.non_priv_trap
= true; /* tick.npt = 1; */
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
);
case SS_trap_externally_initiated_reset
:
if (tl
>v9p
->maxtl
) tl
= v9p
->maxtl
;
N_TPC( v9p
, tl
) = sp
->pc
;
N_TNPC( v9p
, tl
) = sp
->npc
;
((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
.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
.hpriv
= true;
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
/* v9p->softint = 0; FIXME: this what happens on Niagara ? */
V9_STATE_CHANGE(sp
, v9p
, V9_RED
);
-- 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
-- * 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
;
-- 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
)) {
-- 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
;
-- PSTATE_RED_CHANGED(sp
->pstate
.s
.red
);
-- PSTATE_AM_CHANGED(sp
->pstate
.s
.am
);
-- sp
->pstate
.s
.priv
= 1;
-- sp
->pstate
.s
.cle
= sp
->pstate
.s
.tle
;
-- set_pc_for_red_mode_trap(sp
, T_WATCHDOG_RESET
);
-- 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
;
-- PSTATE_RED_CHANGED(sp
->pstate
.s
.red
);
-- PSTATE_AM_CHANGED(sp
->pstate
.s
.am
);
-- sp
->pstate
.s
.priv
= 1;
-- sp
->pstate
.s
.cle
= sp
->pstate
.s
.tle
;
-- set_pc_for_red_mode_trap(sp
, T_SOFTWARE_RESET
);
fatal("cpu: unknown reset trap 0x%x.\n", tt
);
/* setup the mmu_bypass to reflect hpstate */
/* V9_PSTATE_CHANGED(v9p); V9_HPSTATE_CHANGED(sp, v9p); */
sp
->pc
= npp
->rstv_addr
| (tt
<<5);
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 */
#endif /* FP_DECODE_DISABLED */
ss_check_interrupts(sp
); /* because of state change */
static void ss_post_precise_trap(simcpu_t
* sp
, sparcv9_trap_type_t code
)
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
;
/* 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_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 */
nsp
->tm_fail(sp
, V9_CPS_PRECISE
);
return; /* Return without handling the trap */
tt
= (ss_trap_type_t
)code
;
* post this trap if it has higher priority than the
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;
-- /* 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
)
-- Bool from_error_mode
= FALSE
;
-- 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
) {
-- /* 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
) {
-- 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
;
-- P_CWP
= INC_MOD_NWIN(P_CWP
);
-- if (P_CWP
!= sp
->current_window
)
-- update_cur_win_regs(sp
);
-- } else if (is_fill_trap(trno
)) {
-- 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
);
-- * 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
-- trno
= T_WATCHDOG_RESET
;
-- if (is_reset_trap(trno
)) {
-- execute_reset_trap(sp
, trno
, from_error_mode
, error_tt
, trap_pc
, trap_npc
);
-- 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
);
-- * "Normal" trap processing.
-- tba_addr
.l
= sp
->tba
.l
;
-- tba_addr
.s
.in_trap
= (sp
->trap_level
> 0);
-- 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
) {
-- 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. */
-- if (sp
->cansave
== 0) {
-- cwp
= INC_MOD_NWIN(cwp
);
-- P_CWP
= INC_MOD_NWIN(cwp
);
-- int count
= 2 + sp
->cansave
;
-- 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
)) {
-- 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
;
-- PSTATE_RED_CHANGED(sp
->pstate
.s
.red
);
-- PSTATE_AM_CHANGED(sp
->pstate
.s
.am
);
-- sp
->pstate
.s
.priv
= 1;
-- sp
->pstate
.s
.cle
= sp
->pstate
.s
.tle
;
-- /* Activate the appropriate globals. */
-- case use_alternate_globals
:
-- update_global_regs_il (sp
, use_alternate_globals
+ 1);
-- update_global_regs_il (sp
, use_mmu_globals
+ 1);
-- case use_vector_globals
:
-- update_global_regs_il (sp
, use_vector_globals
+ 1);
-- /* repalced the call below by inline function to shorten trap handling */
-- /* update_global_regs(sp, sp->pstate); */
-- update_async_trap_pending(sp
);
-- update_error_trap_pending(sp
);
-- FASTMEM_TL_PSTATE_CHANGE(sp
);
-- sp
->trap_type
[sp
->trap_level
] = trno
;
-- tba_addr
.s
.tt
= sp
->trap_type
[sp
->trap_level
];
-- sp
->npc
= tba_addr
.l
+ 4;
-- sp
->cti_executed
= TRUE
;
-- (sp
->dmmu
->context_select
)(sp
->dmmu
, DEFAULT_DATA_ASI
, &tctxt
);
-- sp
->fm
.current_ctxt
= tctxt
;
-- TRACEMOD_TTEXT( ("CONTEXT %d",(int)tctxt
) );
-- /**************************************************************/
-- execute_reset_trap(FcpuT
* sp
, Word trno
, Bool from_error_mode
,
-- Word error_tt
, LWord trap_pc
, LWord trap_npc
)
-- 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
;
-- PSTATE_RED_CHANGED(sp
->pstate
.s
.red
);
-- PSTATE_AM_CHANGED(sp
->pstate
.s
.am
);
-- sp
->pstate
.s
.priv
= 1;
-- 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 */
-- 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
-- * 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
;
-- 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
)) {
-- 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
;
-- PSTATE_RED_CHANGED(sp
->pstate
.s
.red
);
-- PSTATE_AM_CHANGED(sp
->pstate
.s
.am
);
-- sp
->pstate
.s
.priv
= 1;
-- sp
->pstate
.s
.cle
= sp
->pstate
.s
.tle
;
-- set_pc_for_red_mode_trap(sp
, T_WATCHDOG_RESET
);
-- 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
;
-- PSTATE_RED_CHANGED(sp
->pstate
.s
.red
);
-- PSTATE_AM_CHANGED(sp
->pstate
.s
.am
);
-- sp
->pstate
.s
.priv
= 1;
-- sp
->pstate
.s
.cle
= sp
->pstate
.s
.tle
;
-- set_pc_for_red_mode_trap(sp
, T_EXTERNAL_RESET
);
-- 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
;
-- PSTATE_RED_CHANGED(sp
->pstate
.s
.red
);
-- PSTATE_AM_CHANGED(sp
->pstate
.s
.am
);
-- sp
->pstate
.s
.priv
= 1;
-- sp
->pstate
.s
.cle
= sp
->pstate
.s
.tle
;
-- set_pc_for_red_mode_trap(sp
, T_SOFTWARE_RESET
);
-- fatal("cpu: unknown reset trap 0x%x.\n", trno
);
-- update_global_regs(sp
, sp
->pstate
);
-- update_async_trap_pending(sp
);
-- update_error_trap_pending(sp
);
-- FASTMEM_TL_PSTATE_CHANGE(sp
);
-- sp
->cti_executed
= TRUE
;
-- /* setup for the fast memory stuff ... -ash */
-- /* fastmem_flush(&(sp->fm), 0, 0); */ /* flush all pages */
-- (sp
->dmmu
->context_select
)(sp
->dmmu
, DEFAULT_DATA_ASI
, &tctxt
);
-- sp
->fm
.current_ctxt
= tctxt
;
-- TRACEMOD_TTEXT( ("CONTEXT %d",(int)tctxt
) );
-- * 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).
-- execute_red_mode_trap(FcpuT
* sp
, Word trno
, LWord trap_pc
,
-- LWord trap_npc
, enum Trap_globals p_globals
)
-- 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
) {
-- 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
;
-- 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
)) {
-- 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
;
-- PSTATE_RED_CHANGED(sp
->pstate
.s
.red
);
-- PSTATE_AM_CHANGED(sp
->pstate
.s
.am
);
-- sp
->pstate
.s
.priv
= 1;
-- sp
->pstate
.s
.cle
= sp
->pstate
.s
.tle
;
-- /* Activate the appropriate globals. */
-- case use_alternate_globals
:
-- case use_vector_globals
:
-- update_global_regs(sp
, sp
->pstate
);
-- update_async_trap_pending(sp
);
-- update_error_trap_pending(sp
);
-- FASTMEM_TL_PSTATE_CHANGE(sp
);
-- set_pc_for_red_mode_trap(sp
, T_RED_MODE_EXCEPTION
);
-- sp
->cti_executed
= TRUE
;
-- /* setup for the fast memory stuff ... -ash */
-- /* fastmem_flush(&(sp->fm), 0, 0); */ /* flush all pages */
-- (sp
->dmmu
->context_select
)(sp
->dmmu
, DEFAULT_DATA_ASI
, &tctxt
);
-- sp
->fm
.current_ctxt
= tctxt
;
-- TRACEMOD_TTEXT( ("CONTEXT %d",(int)tctxt
) );
-- 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
;
-- LO_W(trap_pc
) &= ~0x3; /* Blow away lower two bits */
-- LO_W(trap_npc
) &= ~0x3;
-- sp
->was_tpc_in_hole
[level
] = FALSE
;
-- sp
->was_tnpc_in_hole
[level
] = FALSE
;
-- sp
->orig_tnpc
[level
] = trap_npc
;
-- sp
->tpc
[level
] = trap_npc
;
-- sp
->tnpc
[level
] = trap_npc
+ 4;
-- sp
->tpc
[level
] = trap_pc
;
-- sp
->tnpc
[level
] = trap_npc
;
/*************************************************************
* ASI access support functions (and instruction impls)
*************************************************************/
static void ss_xdc_miss(simcpu_t
* sp
, uint64_t * regp
, tvaddr_t addr
, maccess_t op
)
v9p
= (sparcv9_cpu_t
*)(sp
->specificp
);
nsp
= v9p
->impl_specificp
;
context_type
= (v9p
->tl
>0) ? ss_ctx_nucleus
: ss_ctx_primary
;
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
]);
/****************************************************************
****************************************************************/
void ss_tlb_init(ss_tlb_t
* tlbp
, uint_t nentries
)
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
++) {
#if SS_TLB_REPLACE_RROBIN /* { */
RW_lock_init(&tlbp
->rwlock
, NULL
);
void ss_tlb_flush_shares(simcpu_t
* sp
, ss_tlb_t
* tlbp
, bool_t is_immu
)
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
) {
xsp
->xicache_trans_flush_pending
= true;
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
)
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) {
ss_tlb_unhash(tlbp
, tep
);
ss_free_tlb_entry(tlbp
, tep
);
#if SS_TLB_REPLACE_RROBIN /* { */
RW_unlock(&tlbp
->rwlock
);
sp
->xicache_trans_flush_pending
= true;
sp
->xdcache_trans_flush_pending
= true;
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_context
= SS_TLB_INVALID_CONTEXT
;
tep
->data
&= ~(1ull << SUN4U_TTED_V_BIT
);
tep
->data
&= ~((1ull << SUN4V_TTED_V_BIT
) |
(1ull << ROCK_DATA_ACCESS_V1_BIT
));
tep
->data
&= ~(1ull << SUN4V_TTED_V_BIT
);
/* assign to free list */
tep
->nextp
= tlbp
->freep
;
void ss_tlb_unhash(ss_tlb_t
* tlbp
, tlb_entry_t
* tep
)
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
));
void ss_tlb_unfree(ss_tlb_t
* tlbp
, tlb_entry_t
* tep
)
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
));
* - 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.
* - 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
;
bool_t need_flush
= false;
match_context
= is_real
? SS_TLB_REAL_CONTEXT
: context
;
DBGMMU( lprintf(sp
->gid
, "demap_page: part=%x ctx=%x va=0x%llx r:%u\n", partid
, context
, addr
, is_real
); );
DBGMMU( lprintf(sp
->gid
, "demap_all: part=%x r:%u\n", partid
, is_real
); );
DBGMMU( lprintf(sp
->gid
, "demap_all_page: r:%u\n", is_real
); );
DBGMMU( lprintf(sp
->gid
, "demap_init\n"); );
* 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;
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
match_context
= SS_TLB_REAL_CONTEXT
;
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
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;
if (tep
->is_real
!= is_real
) break;
if (tep
->flags
& SS_TLB_FLAG_LOCKED
) break;
if (((tep
->tag_pfn
^ addr
)>>tep
->match_shift
)!=0LL) break;
if (tep
->match_context
!= match_context
) break;
if (tep
->partid
!= partid
) break;
/* matching entry - put back on the free list */
ss_tlb_unhash(tlbp
, tep
);
ss_free_tlb_entry( tlbp
, tep
);
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
);
fatal("Internal error: illegal demap op (0x%x)", op
);
RW_unlock(&tlbp
->rwlock
);
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
sp
->xicache_trans_flush_pending
= true;
sp
->xdcache_trans_flush_pending
= true;
ss_tlb_flush_shares(sp
, tlbp
, mmup
->is_immu
);
/****************************************************************
****************************************************************/
* 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 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
;
bool_t hwtw_enabled
= false;
uint_t i
, ctrl
, context
, context0
, context1
;
* 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
context0
= nsp
->pri_context
;
context1
= nsp
->pri_context1
;
context0
= nsp
->sec_context
;
context1
= nsp
->sec_context1
;
context0
= SS_NUCLEUS_CONTEXT
;
fatal("ss_hardware_tablewalk: Internal Error. Not expecting "
"context type 0x%x\n", context_type
);
tsb_config_regp
= &nsp
->mmu_zero_ctxt_tsb_config
[0];
tsb_config_regp
= &nsp
->mmu_nonzero_ctxt_tsb_config
[0];
* 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
;
tvaddr_t tte_va
, tte_ra
, tte_pa
;
* 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;
* 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"));
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
;
if (tsb_config_regp
->use_context_0
) {
} else if (tsb_config_regp
->use_context_1
) {
} else /* use_context_0 == use_context_1 == 0 */ {
if (tte_context
!= context0
&&
* In both Niagara2 and Rock, it is okay to match if the
* page size of the tsb_config_reg does not match the page
* 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
) {
* setup trap type for error occurred in RPN->PPN translation
miss_trap_type
= SS_trap_instruction_invalid_TSB_entry
;
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
); );
* update tag access register (we use macros because this is somewhat
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
);
* 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.
* if we are here, either a match was not found or the tablewalk is disabled
miss_trap_type
= SS_trap_instruction_access_MMU_miss
;
miss_trap_type
= SS_trap_fast_instruction_access_MMU_miss
;
miss_trap_type
= SS_trap_data_access_MMU_miss
;
miss_trap_type
= SS_trap_fast_data_access_MMU_miss
;
uint8_t *ss_hwtw_find_base(simcpu_t
*sp
, ss_tsb_info_t
*tsb_config_reg
)
config_proc_t
*config_procp
;
config_procp
= sp
->config_procp
;
domainp
= config_procp
->domainp
;
capsrc
= find_domain_address(domainp
, tsb_config_reg
->tsb_base
);
EXEC_WARNING(("@ pc=0x%llx : ss_hwtw_find_base: src address not valid",sp
->pc
));
* 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
));
* 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
+ 1) = BSWAP_64(*(ttep
+ 1));
#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;
/****************************************************************
****************************************************************/
void ss_l1_cache_init(ss_l1_cache_t
* cachep
, uint32_t cachesize
, bool_t is_icache
)
uint_t tagsize
, datasize
;
* Allocate icache/dcache memory for data and tags
* 64bit diag tag per 32byte instn block
* 64bit diag data per 32bits instn data (instn + switch/parity bits)
* 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;
RW_lock_init(&cachep
->rwlock
, NULL
);
/****************************************************************
****************************************************************/
#if !defined(NDEBUG) /* { */
#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) /* { */
np
= (ss_proc_t
*)(config_procp
->procp
);
pnum
= config_procp
->proc_id
;
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
);
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
);
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);
#if !defined(NDEBUG) /* { */
PS_Left
, PS_Center
, PS_Right
void pspace(FILE * fop
, uint_t space
, just_t just
, char * fmt
, ...)
vsprintf(buf
, fmt
, args
);
if (before
<0) before
= 0;
if (before
<0) before
= 0;
after
= space
- (len
+ before
);
for (i
=0; i
<before
; i
++) fprintf(fop
, " ");
for (i
=0; i
<after
; i
++) fprintf(fop
, " ");
void ss_tlb_contents(FILE *fop
, ss_tlb_t
* tlbp
, bool_t lockit
)
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");
/* grab lock as writer to ensure nothing changes */
RW_wrlock(&tlbp
->rwlock
);
tep
= &(tlbp
->tlb_entryp
[0]);
for (i
=0; i
<tlbp
->nentries
; i
++) {
switch (tep
->match_context
) {
case SS_TLB_INVALID_CONTEXT
:
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");
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
);
pspace(fop
, 7, PS_Center
, "%u", 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
);
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' : '.' );
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' : '.');
pspace(fop
, 20, PS_Right
, "0x%llx", tep
->data
);
RW_unlock(&tlbp
->rwlock
);
/****************************************************************
****************************************************************/
--void ss_xdc_load_miss(simcpu_t
* sp
, uint64_t * regp
, xdcache_line_t
* xclp
, tvaddr_t va
, bool_t issigned
, int bytes
)
-- uint8_t * bufp
, * ptr
;
-- 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
);
-- v9p
->post_precise_trap(sp
, Sparcv9_trap_mem_address_not_aligned
);
-- /* 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 */
-- if (!nsp
->mmu_bypass
) {
-- int idx
, context
, partid
;
-- RW_rdlock(&tlbp
->rwlock
);
-- /* FIXME: need a current context variable, not a test here */
-- context
= (v9p
->tl
>0) ? 0 : nsp
->pri_context
;
-- /* 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
);
-- v9p
->post_precise_trap(sp
, (sparcv9_trap_type_t
)SS_trap_fast_data_access_MMU_miss
);
-- /* we have a matching entry ... now all we have to worry about are the permissions */
-- 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
);
-- v9p
->post_precise_trap(sp
, (sparcv9_trap_type_t
)SS_trap_data_access_exception
);
-- * OK - now go get the pointer to the line data
-- * ... start by finding the device that has the
-- * 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
)) {
-- 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
;
-- v9p
->post_precise_trap(sp
, Sparcv9_trap_data_access_error
); /* FIXME: right trap ? */
-- * 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
->offset
= ((uint64_t)bufp
) - tag
;
-- * Sigh now complete the load on behalf of the original
-- ptr
= (uint8_t*)(xclp
->offset
+ va
);
-- case 1: val
= *(sint8_t
*)ptr
; break;
-- case 2: val
= *(sint16_t
*)ptr
; break;
-- case 4: val
= *(sint32_t
*)ptr
; break;
-- 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;
-- * Finally go get the next instruction
--void ss_xdc_store_miss(simcpu_t
* sp
, uint64_t val
, xdcache_line_t
* xclp
, tvaddr_t va
, int bytes
)
-- uint8_t * bufp
, * ptr
;
-- 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
);
-- /* 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 */
-- if (!nsp
->mmu_bypass
) {
-- int idx
, context
, partid
;
-- RW_rdlock(&tlbp
->rwlock
);
-- /* FIXME: need a current context variable, not a test here */
-- context
= (v9p
->tl
>0) ? 0 : nsp
->pri_context
;
-- /* 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
);
-- v9p
->post_precise_trap(sp
, (sparcv9_trap_type_t
)SS_trap_fast_data_access_MMU_miss
);
-- /* we have a matching entry ... now all we have to worry about are the permissions */
-- 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
);
-- v9p
->post_precise_trap(sp
, (sparcv9_trap_type_t
)SS_trap_data_access_exception
);
-- if (!(flags
& SS_TLB_FLAG_WRITE
)) {
-- SET_DTLB_STORE_FAULT( nsp
, va
);
-- v9p
->post_precise_trap(sp
, (sparcv9_trap_type_t
)SS_trap_fast_data_access_protection
);
-- * OK - now go get the pointer to the line data
-- * ... start by finding the device that has the
-- * 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
)) {
-- 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
;
-- v9p
->post_precise_trap(sp
, Sparcv9_trap_data_access_error
); /* FIXME: right trap ? */
-- * 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
->offset
= ((uint64_t)bufp
) - tag
;
-- * Sigh now complete the store on behalf of the original
-- ptr
= (uint8_t*)(xclp
->offset
+ va
);
-- 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;
-- * Finally go get the next instruction
* called in response to ~n
void ss_dump_instruction_counts(config_proc_t
* cp
)
#if !defined(NDEBUG) /* { */
#if defined(NIAGARA1) || defined(NIAGARA2) /* { */
uint64_t lo_cycle
= 0xFFFFFFFFFFFFFFFF;
np
= (ss_proc_t
*)(cp
->procp
);
lprintf(-1, "==== strand counts proc %d ====\n", cp
->proc_id
);
for (j
=0; j
<ncpus
; j
++) {
lprintf(sp
->gid
, "@ pc=0x%llx tl=%d: cycle[%llu] instr[%llu] "
sp
->pc
, sv9p
->tl
, sp
->cycle
, ICOUNT(sp
),
#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 "
nsp
->pcr
, nsp
->pic0
, nsp
->pic1
);
lprintf(sp
->gid
, "hi_cycle - lo_cycle=%llu\n", hi_cycle
- lo_cycle
);
* 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.
ss_change_exec_state(ss_proc_t
*npp
, uint64_t running_status
)
uint_t idx
, ncpu
, vcore_id
;
for (idx
= 0; idx
< ncpu
; idx
++) {
tnsp
= &(npp
->ss_strandp
[idx
]);
vcore_id
= tnsp
->vcore_id
;
unparked
= ((running_status
>> vcore_id
) & 0x1);
#if defined(NIAGARA1) /* { */
SET_THREAD_STS_SFSM(npp
, tnsp
, THREAD_STS_TSTATE_RUN
);
simcore_cpu_state_unpark(tsp
);
#if defined(NIAGARA1) /* { */
SET_THREAD_STS_SFSM(npp
, tnsp
, THREAD_STS_TSTATE_IDLE
);
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
)
config_proc_t
*config_procp
;
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
);
/* 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
));
* 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
);
* Get the virtual core Id (or CPU Id) of the calling strand.
uint_t
ss_get_cpuid(simcpu_t
*sp
)
v9p
= (sparcv9_cpu_t
*)(sp
->specificp
);
nsp
= v9p
->impl_specificp
;
* 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
)
for (idx
= 0; idx
< v9p
->nglobals
* 8; idx
++){
/* need to leave %g0 alone */
v9p
->globalsp
[idx
] = val
;
/* trash ins and locals */
for (idx
= 0; idx
< (v9p
->nwins
* 2 * V9_REG_GROUP
); idx
++)
/* 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
++)
* 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)
* Parse the debug_hook directive.
* The debug_hook directive in the conf file should look like this:
* directive_name debug_hook .....
* -------------- ----------
* debug_hook trace ..... ;
* \________________________/ \____________/
* we parse this much here debug_hook_parse()
* 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
static void parse_debug_hook(ss_proc_t
*procp
)
char hookname_parse
[BUFSIZ
];
char hookname_dump
[BUFSIZ
];
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
;
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
#define PRI_CTX_IS_NUCLEUS(_nsp) ((_nsp)->pri_context == SS_NUCLEUS_CONTEXT)
xcache_set_tagstate(simcpu_t
* sp
)
v9p
= (sparcv9_cpu_t
*)(sp
->specificp
);
nsp
= v9p
->impl_specificp
;
newstate
= XCACHE_TAGSTATE_PHYS
;
if (v9p
->state
== V9_User
) {
newstate
= (v9p
->tl
== 0 && !PRI_CTX_IS_NUCLEUS(nsp
)) ?
XCACHE_TAGSTATE_TL0_U
: XCACHE_TAGSTATE_TLN_U
;
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;
v
= host_cas64((uint64_t *)lp
, l
, n
);
v
= host_cas64((uint64_t *)lp
, l
, n
);
v
= host_cas64((uint64_t *)lp
, l
, n
);
* 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:
* 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 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:
save_memory_segment_axis(config_dev_t
*cdp
, FILE *filep
)
uint64_t *legion_mem_basep
;
#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",
* process memory in 256 byte chunks
while (off
<= (cdp
->addrp
->range
- 256)) {
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
;
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.
* If memory chunk is non-zero, print it
* otherwise, keep a track of zero chunks
* so we can print a summary before the next
/* 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.
fprintf(filep
, "\n@%llx//+%llx\n",
foo_addr
, skip_cnt
* 256);
* Now, print the starting @addr of the new
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
]);
/* chunk was all zeros so, just keep a count */
/* save start address of zero chunk */
/* Before we finish, check if we have any zero chunks to print */
fprintf(filep
, "\n@%llx//+%llx\n",
foo_addr
, skip_cnt
* 256);
save_memory_segment_binary(config_dev_t
*cdp
, FILE *filep
)
uint64_t *legion_mem_basep
;
uint64_t size
= cdp
->addrp
->range
;
uint64_t start_addr
= cdp
->addrp
->baseaddr
;
uint64_t end_addr
= cdp
->addrp
->baseaddr
+ size
;
char filename
[MAXPATHLEN
];
mode_t mode
= S_IRWXU
| S_IRWXG
| S_IRWXO
;
* bcopy from legion memory to new 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
))
/* 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]",
if (write(fd
, "", 1) != 1)
fatal("Could not write to file %s of size [0x%llx]",
datap
= (uint64_t*)mmap(NULL
, size
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, fd
, 0);
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) {
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
);
*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" \
start_addr
, size
, filename
);
ss_save_state(simcpu_t
*sp
)
domain_t
*domainp
= sp
->config_procp
->domainp
;
config_dev_t
*cdp
= NULL
;
FILE *mem_image_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
* 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
);
* 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
);
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
);