aboutsummaryrefslogtreecommitdiff
path: root/target
diff options
context:
space:
mode:
Diffstat (limited to 'target')
-rw-r--r--target/sparc/asi.h1
-rw-r--r--target/sparc/cpu.c13
-rw-r--r--target/sparc/cpu.h105
-rw-r--r--target/sparc/helper.h1
-rw-r--r--target/sparc/int64_helper.c43
-rw-r--r--target/sparc/ldst_helper.c387
-rw-r--r--target/sparc/machine.c4
-rw-r--r--target/sparc/mmu_helper.c20
-rw-r--r--target/sparc/translate.c64
-rw-r--r--target/sparc/win_helper.c46
10 files changed, 534 insertions, 150 deletions
diff --git a/target/sparc/asi.h b/target/sparc/asi.h
index c9a1849..d8d6284 100644
--- a/target/sparc/asi.h
+++ b/target/sparc/asi.h
@@ -211,6 +211,7 @@
#define ASI_AFSR 0x4c /* Async fault status register */
#define ASI_AFAR 0x4d /* Async fault address register */
#define ASI_EC_TAG_DATA 0x4e /* E-cache tag/valid ram diag acc */
+#define ASI_HYP_SCRATCHPAD 0x4f /* (4V) Hypervisor scratchpad */
#define ASI_IMMU 0x50 /* Insn-MMU main register space */
#define ASI_IMMU_TSB_8KB_PTR 0x51 /* Insn-MMU 8KB TSB pointer reg */
#define ASI_IMMU_TSB_64KB_PTR 0x52 /* Insn-MMU 64KB TSB pointer reg */
diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c
index d6583f1..d606eb5 100644
--- a/target/sparc/cpu.c
+++ b/target/sparc/cpu.c
@@ -57,9 +57,13 @@ static void sparc_cpu_reset(CPUState *s)
env->psrps = 1;
#endif
#ifdef TARGET_SPARC64
- env->pstate = PS_PRIV|PS_RED|PS_PEF|PS_AG;
+ env->pstate = PS_PRIV | PS_RED | PS_PEF;
+ if (!cpu_has_hypervisor(env)) {
+ env->pstate |= PS_AG;
+ }
env->hpstate = cpu_has_hypervisor(env) ? HS_PRIV : 0;
env->tl = env->maxtl;
+ env->gl = 2;
cpu_tsptr(env)->tt = TT_POWER_ON_RESET;
env->lsu = 0;
#else
@@ -744,14 +748,17 @@ void sparc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
cpu_print_cc(f, cpu_fprintf, cpu_get_ccr(env) << PSR_CARRY_SHIFT);
cpu_fprintf(f, " xcc: ");
cpu_print_cc(f, cpu_fprintf, cpu_get_ccr(env) << (PSR_CARRY_SHIFT - 4));
- cpu_fprintf(f, ") asi: %02x tl: %d pil: %x\n", env->asi, env->tl,
- env->psrpil);
+ cpu_fprintf(f, ") asi: %02x tl: %d pil: %x gl: %d\n", env->asi, env->tl,
+ env->psrpil, env->gl);
+ cpu_fprintf(f, "tbr: " TARGET_FMT_lx " hpstate: " TARGET_FMT_lx " htba: "
+ TARGET_FMT_lx "\n", env->tbr, env->hpstate, env->htba);
cpu_fprintf(f, "cansave: %d canrestore: %d otherwin: %d wstate: %d "
"cleanwin: %d cwp: %d\n",
env->cansave, env->canrestore, env->otherwin, env->wstate,
env->cleanwin, env->nwindows - 1 - env->cwp);
cpu_fprintf(f, "fsr: " TARGET_FMT_lx " y: " TARGET_FMT_lx " fprs: "
TARGET_FMT_lx "\n", env->fsr, env->y, env->fprs);
+
#else
cpu_fprintf(f, "psr: %08x (icc: ", cpu_get_psr(env));
cpu_print_cc(f, cpu_fprintf, cpu_get_psr(env));
diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h
index 601c018..95a36a4 100644
--- a/target/sparc/cpu.h
+++ b/target/sparc/cpu.h
@@ -68,6 +68,8 @@
#define TT_DATA_ACCESS 0x32
#define TT_UNALIGNED 0x34
#define TT_PRIV_ACT 0x37
+#define TT_INSN_REAL_TRANSLATION_MISS 0x3e
+#define TT_DATA_REAL_TRANSLATION_MISS 0x3f
#define TT_EXTINT 0x40
#define TT_IVEC 0x60
#define TT_TMISS 0x64
@@ -77,6 +79,7 @@
#define TT_FILL 0xc0
#define TT_WOTHER (1 << 5)
#define TT_TRAP 0x100
+#define TT_HTRAP 0x180
#endif
#define PSR_NEG_SHIFT 23
@@ -227,7 +230,7 @@ enum {
#if !defined(TARGET_SPARC64)
#define NB_MMU_MODES 3
#else
-#define NB_MMU_MODES 7
+#define NB_MMU_MODES 6
typedef struct trap_state {
uint64_t tpc;
uint64_t tnpc;
@@ -302,21 +305,42 @@ enum {
#define TTE_W_OK_BIT (1ULL << 1)
#define TTE_GLOBAL_BIT (1ULL << 0)
+#define TTE_NFO_BIT_UA2005 (1ULL << 62)
+#define TTE_USED_BIT_UA2005 (1ULL << 47)
+#define TTE_LOCKED_BIT_UA2005 (1ULL << 61)
+#define TTE_SIDEEFFECT_BIT_UA2005 (1ULL << 11)
+#define TTE_PRIV_BIT_UA2005 (1ULL << 8)
+#define TTE_W_OK_BIT_UA2005 (1ULL << 6)
+
#define TTE_IS_VALID(tte) ((tte) & TTE_VALID_BIT)
#define TTE_IS_NFO(tte) ((tte) & TTE_NFO_BIT)
#define TTE_IS_USED(tte) ((tte) & TTE_USED_BIT)
#define TTE_IS_LOCKED(tte) ((tte) & TTE_LOCKED_BIT)
#define TTE_IS_SIDEEFFECT(tte) ((tte) & TTE_SIDEEFFECT_BIT)
+#define TTE_IS_SIDEEFFECT_UA2005(tte) ((tte) & TTE_SIDEEFFECT_BIT_UA2005)
#define TTE_IS_PRIV(tte) ((tte) & TTE_PRIV_BIT)
#define TTE_IS_W_OK(tte) ((tte) & TTE_W_OK_BIT)
+
+#define TTE_IS_NFO_UA2005(tte) ((tte) & TTE_NFO_BIT_UA2005)
+#define TTE_IS_USED_UA2005(tte) ((tte) & TTE_USED_BIT_UA2005)
+#define TTE_IS_LOCKED_UA2005(tte) ((tte) & TTE_LOCKED_BIT_UA2005)
+#define TTE_IS_SIDEEFFECT_UA2005(tte) ((tte) & TTE_SIDEEFFECT_BIT_UA2005)
+#define TTE_IS_PRIV_UA2005(tte) ((tte) & TTE_PRIV_BIT_UA2005)
+#define TTE_IS_W_OK_UA2005(tte) ((tte) & TTE_W_OK_BIT_UA2005)
+
#define TTE_IS_GLOBAL(tte) ((tte) & TTE_GLOBAL_BIT)
#define TTE_SET_USED(tte) ((tte) |= TTE_USED_BIT)
#define TTE_SET_UNUSED(tte) ((tte) &= ~TTE_USED_BIT)
#define TTE_PGSIZE(tte) (((tte) >> 61) & 3ULL)
+#define TTE_PGSIZE_UA2005(tte) ((tte) & 7ULL)
#define TTE_PA(tte) ((tte) & 0x1ffffffe000ULL)
+/* UltraSPARC T1 specific */
+#define TLB_UST1_IS_REAL_BIT (1ULL << 9) /* Real translation entry */
+#define TLB_UST1_IS_SUN4V_BIT (1ULL << 10) /* sun4u/sun4v TTE format switch */
+
#define SFSR_NF_BIT (1ULL << 24) /* JPS1 NoFault */
#define SFSR_TM_BIT (1ULL << 15) /* JPS1 TLB Miss */
#define SFSR_FT_VA_IMMU_BIT (1ULL << 13) /* USIIi VA out of range (IMMU) */
@@ -360,6 +384,9 @@ enum {
#define CACHE_CTRL_FD (1 << 22) /* Flush Data cache (Write only) */
#define CACHE_CTRL_DS (1 << 23) /* Data cache snoop enable */
+#define CONVERT_BIT(X, SRC, DST) \
+ (SRC > DST ? (X) / (SRC / DST) & (DST) : ((X) & SRC) * (DST / SRC))
+
typedef struct SparcTLBEntry {
uint64_t tag;
uint64_t tte;
@@ -380,7 +407,24 @@ struct CPUTimer
typedef struct CPUTimer CPUTimer;
typedef struct CPUSPARCState CPUSPARCState;
-
+#if defined(TARGET_SPARC64)
+typedef union {
+ uint64_t mmuregs[16];
+ struct {
+ uint64_t tsb_tag_target;
+ uint64_t mmu_primary_context;
+ uint64_t mmu_secondary_context;
+ uint64_t sfsr;
+ uint64_t sfar;
+ uint64_t tsb;
+ uint64_t tag_access;
+ uint64_t virtual_watchpoint;
+ uint64_t physical_watchpoint;
+ uint64_t sun4v_ctx_config[2];
+ uint64_t sun4v_tsb_pointers[4];
+ };
+} SparcV9MMU;
+#endif
struct CPUSPARCState {
target_ulong gregs[8]; /* general registers */
target_ulong *regwptr; /* pointer to current register window */
@@ -433,31 +477,8 @@ struct CPUSPARCState {
uint64_t lsu;
#define DMMU_E 0x8
#define IMMU_E 0x4
- //typedef struct SparcMMU
- union {
- uint64_t immuregs[16];
- struct {
- uint64_t tsb_tag_target;
- uint64_t unused_mmu_primary_context; // use DMMU
- uint64_t unused_mmu_secondary_context; // use DMMU
- uint64_t sfsr;
- uint64_t sfar;
- uint64_t tsb;
- uint64_t tag_access;
- } immu;
- };
- union {
- uint64_t dmmuregs[16];
- struct {
- uint64_t tsb_tag_target;
- uint64_t mmu_primary_context;
- uint64_t mmu_secondary_context;
- uint64_t sfsr;
- uint64_t sfar;
- uint64_t tsb;
- uint64_t tag_access;
- } dmmu;
- };
+ SparcV9MMU immu;
+ SparcV9MMU dmmu;
SparcTLBEntry itlb[64];
SparcTLBEntry dtlb[64];
uint32_t mmu_version;
@@ -487,6 +508,7 @@ struct CPUSPARCState {
uint64_t bgregs[8]; /* backup for normal global registers */
uint64_t igregs[8]; /* interrupt general registers */
uint64_t mgregs[8]; /* mmu general registers */
+ uint64_t glregs[8 * MAXTL_MAX];
uint64_t fprs;
uint64_t tick_cmpr, stick_cmpr;
CPUTimer *tick, *stick;
@@ -496,6 +518,7 @@ struct CPUSPARCState {
uint32_t gl; // UA2005
/* UA 2005 hyperprivileged registers */
uint64_t hpstate, htstate[MAXTL_MAX], hintp, htba, hver, hstick_cmpr, ssr;
+ uint64_t scratch[8];
CPUTimer *hstick; // UA 2005
/* Interrupt vector registers */
uint64_t ivec_status;
@@ -586,6 +609,7 @@ void cpu_put_ccr(CPUSPARCState *env1, target_ulong val);
target_ulong cpu_get_cwp64(CPUSPARCState *env1);
void cpu_put_cwp64(CPUSPARCState *env1, int cwp);
void cpu_change_pstate(CPUSPARCState *env1, uint32_t new_pstate);
+void cpu_gl_switch_gregs(CPUSPARCState *env, uint32_t new_gl);
#endif
int cpu_cwp_inc(CPUSPARCState *env1, int cwp);
int cpu_cwp_dec(CPUSPARCState *env1, int cwp);
@@ -645,8 +669,7 @@ int cpu_sparc_signal_handler(int host_signum, void *pinfo, void *puc);
#define MMU_KERNEL_IDX 2
#define MMU_KERNEL_SECONDARY_IDX 3
#define MMU_NUCLEUS_IDX 4
-#define MMU_HYPV_IDX 5
-#define MMU_PHYS_IDX 6
+#define MMU_PHYS_IDX 5
#else
#define MMU_USER_IDX 0
#define MMU_KERNEL_IDX 1
@@ -668,6 +691,11 @@ static inline int cpu_supervisor_mode(CPUSPARCState *env1)
{
return env1->pstate & PS_PRIV;
}
+#else
+static inline int cpu_supervisor_mode(CPUSPARCState *env1)
+{
+ return env1->psrs;
+}
#endif
static inline int cpu_mmu_index(CPUSPARCState *env, bool ifetch)
@@ -686,10 +714,10 @@ static inline int cpu_mmu_index(CPUSPARCState *env, bool ifetch)
? (env->lsu & IMMU_E) == 0 || (env->pstate & PS_RED) != 0
: (env->lsu & DMMU_E) == 0) {
return MMU_PHYS_IDX;
+ } else if (cpu_hypervisor_mode(env)) {
+ return MMU_PHYS_IDX;
} else if (env->tl > 0) {
return MMU_NUCLEUS_IDX;
- } else if (cpu_hypervisor_mode(env)) {
- return MMU_HYPV_IDX;
} else if (cpu_supervisor_mode(env)) {
return MMU_KERNEL_IDX;
} else {
@@ -704,8 +732,9 @@ static inline int cpu_interrupts_enabled(CPUSPARCState *env1)
if (env1->psret != 0)
return 1;
#else
- if (env1->pstate & PS_IE)
+ if ((env1->pstate & PS_IE) && !cpu_hypervisor_mode(env1)) {
return 1;
+ }
#endif
return 0;
@@ -734,6 +763,8 @@ trap_state* cpu_tsptr(CPUSPARCState* env);
#define TB_FLAG_MMU_MASK 7
#define TB_FLAG_FPU_ENABLED (1 << 4)
#define TB_FLAG_AM_ENABLED (1 << 5)
+#define TB_FLAG_SUPER (1 << 6)
+#define TB_FLAG_HYPER (1 << 7)
#define TB_FLAG_ASI_SHIFT 24
static inline void cpu_get_tb_cpu_state(CPUSPARCState *env, target_ulong *pc,
@@ -743,7 +774,17 @@ static inline void cpu_get_tb_cpu_state(CPUSPARCState *env, target_ulong *pc,
*pc = env->pc;
*cs_base = env->npc;
flags = cpu_mmu_index(env, false);
+#ifndef CONFIG_USER_ONLY
+ if (cpu_supervisor_mode(env)) {
+ flags |= TB_FLAG_SUPER;
+ }
+#endif
#ifdef TARGET_SPARC64
+#ifndef CONFIG_USER_ONLY
+ if (cpu_hypervisor_mode(env)) {
+ flags |= TB_FLAG_HYPER;
+ }
+#endif
if (env->pstate & PS_AM) {
flags |= TB_FLAG_AM_ENABLED;
}
diff --git a/target/sparc/helper.h b/target/sparc/helper.h
index 3ef38b9..b8f1e78 100644
--- a/target/sparc/helper.h
+++ b/target/sparc/helper.h
@@ -5,6 +5,7 @@ DEF_HELPER_1(rdpsr, tl, env)
DEF_HELPER_1(power_down, void, env)
#else
DEF_HELPER_FLAGS_2(wrpil, TCG_CALL_NO_RWG, void, env, tl)
+DEF_HELPER_2(wrgl, void, env, tl)
DEF_HELPER_2(wrpstate, void, env, tl)
DEF_HELPER_1(done, void, env)
DEF_HELPER_1(retry, void, env)
diff --git a/target/sparc/int64_helper.c b/target/sparc/int64_helper.c
index 29360fa..605747c 100644
--- a/target/sparc/int64_helper.c
+++ b/target/sparc/int64_helper.c
@@ -78,8 +78,10 @@ void sparc_cpu_do_interrupt(CPUState *cs)
static int count;
const char *name;
- if (intno < 0 || intno >= 0x180) {
+ if (intno < 0 || intno >= 0x1ff) {
name = "Unknown";
+ } else if (intno >= 0x180) {
+ name = "Hyperprivileged Trap Instruction";
} else if (intno >= 0x100) {
name = "Trap Instruction";
} else if (intno >= 0xc0) {
@@ -135,16 +137,42 @@ void sparc_cpu_do_interrupt(CPUState *cs)
tsptr->tnpc = env->npc;
tsptr->tt = intno;
+ if (cpu_has_hypervisor(env)) {
+ env->htstate[env->tl] = env->hpstate;
+ /* XXX OpenSPARC T1 - UltraSPARC T3 have MAXPTL=2
+ but this may change in the future */
+ if (env->tl > 2) {
+ env->hpstate |= HS_PRIV;
+ }
+ }
+
+ if (env->def->features & CPU_FEATURE_GL) {
+ tsptr->tstate |= (env->gl & 7ULL) << 40;
+ cpu_gl_switch_gregs(env, env->gl + 1);
+ env->gl++;
+ }
+
switch (intno) {
case TT_IVEC:
- cpu_change_pstate(env, PS_PEF | PS_PRIV | PS_IG);
+ if (!cpu_has_hypervisor(env)) {
+ cpu_change_pstate(env, PS_PEF | PS_PRIV | PS_IG);
+ }
break;
case TT_TFAULT:
case TT_DFAULT:
case TT_TMISS ... TT_TMISS + 3:
case TT_DMISS ... TT_DMISS + 3:
case TT_DPROT ... TT_DPROT + 3:
- cpu_change_pstate(env, PS_PEF | PS_PRIV | PS_MG);
+ if (cpu_has_hypervisor(env)) {
+ env->hpstate |= HS_PRIV;
+ env->pstate = PS_PEF | PS_PRIV;
+ } else {
+ cpu_change_pstate(env, PS_PEF | PS_PRIV | PS_MG);
+ }
+ break;
+ case TT_INSN_REAL_TRANSLATION_MISS ... TT_DATA_REAL_TRANSLATION_MISS:
+ case TT_HTRAP ... TT_HTRAP + 127:
+ env->hpstate |= HS_PRIV;
break;
default:
cpu_change_pstate(env, PS_PEF | PS_PRIV | PS_AG);
@@ -158,8 +186,13 @@ void sparc_cpu_do_interrupt(CPUState *cs)
} else if ((intno & 0x1c0) == TT_FILL) {
cpu_set_cwp(env, cpu_cwp_inc(env, env->cwp + 1));
}
- env->pc = env->tbr & ~0x7fffULL;
- env->pc |= ((env->tl > 1) ? 1 << 14 : 0) | (intno << 5);
+
+ if (cpu_hypervisor_mode(env)) {
+ env->pc = (env->htba & ~0x3fffULL) | (intno << 5);
+ } else {
+ env->pc = env->tbr & ~0x7fffULL;
+ env->pc |= ((env->tl > 1) ? 1 << 14 : 0) | (intno << 5);
+ }
env->npc = env->pc + 4;
cs->exception_index = -1;
}
diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c
index a0171f7..2c05d6a 100644
--- a/target/sparc/ldst_helper.c
+++ b/target/sparc/ldst_helper.c
@@ -70,44 +70,47 @@
#define QT1 (env->qt1)
#if defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY)
-/* Calculates TSB pointer value for fault page size 8k or 64k */
-static uint64_t ultrasparc_tsb_pointer(uint64_t tsb_register,
- uint64_t tag_access_register,
- int page_size)
+/* Calculates TSB pointer value for fault page size
+ * UltraSPARC IIi has fixed sizes (8k or 64k) for the page pointers
+ * UA2005 holds the page size configuration in mmu_ctx registers */
+static uint64_t ultrasparc_tsb_pointer(CPUSPARCState *env,
+ const SparcV9MMU *mmu, const int idx)
{
- uint64_t tsb_base = tsb_register & ~0x1fffULL;
+ uint64_t tsb_register;
+ int page_size;
+ if (cpu_has_hypervisor(env)) {
+ int tsb_index = 0;
+ int ctx = mmu->tag_access & 0x1fffULL;
+ uint64_t ctx_register = mmu->sun4v_ctx_config[ctx ? 1 : 0];
+ tsb_index = idx;
+ tsb_index |= ctx ? 2 : 0;
+ page_size = idx ? ctx_register >> 8 : ctx_register;
+ page_size &= 7;
+ tsb_register = mmu->sun4v_tsb_pointers[tsb_index];
+ } else {
+ page_size = idx;
+ tsb_register = mmu->tsb;
+ }
int tsb_split = (tsb_register & 0x1000ULL) ? 1 : 0;
int tsb_size = tsb_register & 0xf;
- /* discard lower 13 bits which hold tag access context */
- uint64_t tag_access_va = tag_access_register & ~0x1fffULL;
-
- /* now reorder bits */
- uint64_t tsb_base_mask = ~0x1fffULL;
- uint64_t va = tag_access_va;
+ uint64_t tsb_base_mask = (~0x1fffULL) << tsb_size;
- /* move va bits to correct position */
- if (page_size == 8*1024) {
- va >>= 9;
- } else if (page_size == 64*1024) {
- va >>= 12;
- }
-
- if (tsb_size) {
- tsb_base_mask <<= tsb_size;
- }
+ /* move va bits to correct position,
+ * the context bits will be masked out later */
+ uint64_t va = mmu->tag_access >> (3 * page_size + 9);
/* calculate tsb_base mask and adjust va if split is in use */
if (tsb_split) {
- if (page_size == 8*1024) {
+ if (idx == 0) {
va &= ~(1ULL << (13 + tsb_size));
- } else if (page_size == 64*1024) {
+ } else {
va |= (1ULL << (13 + tsb_size));
}
tsb_base_mask <<= 1;
}
- return ((tsb_base & tsb_base_mask) | (va & ~tsb_base_mask)) & ~0xfULL;
+ return ((tsb_register & tsb_base_mask) | (va & ~tsb_base_mask)) & ~0xfULL;
}
/* Calculates tag target register value by reordering bits
@@ -127,9 +130,8 @@ static void replace_tlb_entry(SparcTLBEntry *tlb,
if (TTE_IS_VALID(tlb->tte)) {
CPUState *cs = CPU(sparc_env_get_cpu(env1));
- mask = 0xffffffffffffe000ULL;
- mask <<= 3 * ((tlb->tte >> 61) & 3);
- size = ~mask + 1;
+ size = 8192ULL << 3 * TTE_PGSIZE(tlb->tte);
+ mask = 1ULL + ~size;
va = tlb->tag & mask;
@@ -202,12 +204,56 @@ static void demap_tlb(SparcTLBEntry *tlb, target_ulong demap_addr,
}
}
+static uint64_t sun4v_tte_to_sun4u(CPUSPARCState *env, uint64_t tag,
+ uint64_t sun4v_tte)
+{
+ uint64_t sun4u_tte;
+ if (!(cpu_has_hypervisor(env) && (tag & TLB_UST1_IS_SUN4V_BIT))) {
+ /* is already in the sun4u format */
+ return sun4v_tte;
+ }
+ sun4u_tte = TTE_PA(sun4v_tte) | (sun4v_tte & TTE_VALID_BIT);
+ sun4u_tte |= (sun4v_tte & 3ULL) << 61; /* TTE_PGSIZE */
+ sun4u_tte |= CONVERT_BIT(sun4v_tte, TTE_NFO_BIT_UA2005, TTE_NFO_BIT);
+ sun4u_tte |= CONVERT_BIT(sun4v_tte, TTE_USED_BIT_UA2005, TTE_USED_BIT);
+ sun4u_tte |= CONVERT_BIT(sun4v_tte, TTE_W_OK_BIT_UA2005, TTE_W_OK_BIT);
+ sun4u_tte |= CONVERT_BIT(sun4v_tte, TTE_SIDEEFFECT_BIT_UA2005,
+ TTE_SIDEEFFECT_BIT);
+ sun4u_tte |= CONVERT_BIT(sun4v_tte, TTE_PRIV_BIT_UA2005, TTE_PRIV_BIT);
+ sun4u_tte |= CONVERT_BIT(sun4v_tte, TTE_LOCKED_BIT_UA2005, TTE_LOCKED_BIT);
+ return sun4u_tte;
+}
+
static void replace_tlb_1bit_lru(SparcTLBEntry *tlb,
uint64_t tlb_tag, uint64_t tlb_tte,
- const char *strmmu, CPUSPARCState *env1)
+ const char *strmmu, CPUSPARCState *env1,
+ uint64_t addr)
{
unsigned int i, replace_used;
+ tlb_tte = sun4v_tte_to_sun4u(env1, addr, tlb_tte);
+ if (cpu_has_hypervisor(env1)) {
+ uint64_t new_vaddr = tlb_tag & ~0x1fffULL;
+ uint64_t new_size = 8192ULL << 3 * TTE_PGSIZE(tlb_tte);
+ uint32_t new_ctx = tlb_tag & 0x1fffU;
+ for (i = 0; i < 64; i++) {
+ uint32_t ctx = tlb[i].tag & 0x1fffU;
+ /* check if new mapping overlaps an existing one */
+ if (new_ctx == ctx) {
+ uint64_t vaddr = tlb[i].tag & ~0x1fffULL;
+ uint64_t size = 8192ULL << 3 * TTE_PGSIZE(tlb[i].tte);
+ if (new_vaddr == vaddr
+ || (new_vaddr < vaddr + size
+ && vaddr < new_vaddr + new_size)) {
+ DPRINTF_MMU("auto demap entry [%d] %lx->%lx\n", i, vaddr,
+ new_vaddr);
+ replace_tlb_entry(&tlb[i], tlb_tag, tlb_tte, env1);
+ return;
+ }
+ }
+
+ }
+ }
/* Try replacing invalid entry */
for (i = 0; i < 64; i++) {
if (!TTE_IS_VALID(tlb[i].tte)) {
@@ -247,9 +293,11 @@ static void replace_tlb_1bit_lru(SparcTLBEntry *tlb,
}
#ifdef DEBUG_MMU
- DPRINTF_MMU("%s lru replacement failed: no entries available\n", strmmu);
+ DPRINTF_MMU("%s lru replacement: no free entries available, "
+ "replacing the last one\n", strmmu);
#endif
- /* error state? */
+ /* corner case: the last entry is replaced anyway */
+ replace_tlb_entry(&tlb[63], tlb_tag, tlb_tte, env1);
}
#endif
@@ -294,6 +342,22 @@ static inline target_ulong asi_address_mask(CPUSPARCState *env,
}
return addr;
}
+
+#ifndef CONFIG_USER_ONLY
+static inline void do_check_asi(CPUSPARCState *env, int asi, uintptr_t ra)
+{
+ /* ASIs >= 0x80 are user mode.
+ * ASIs >= 0x30 are hyper mode (or super if hyper is not available).
+ * ASIs <= 0x2f are super mode.
+ */
+ if (asi < 0x80
+ && !cpu_hypervisor_mode(env)
+ && (!cpu_supervisor_mode(env)
+ || (asi >= 0x30 && cpu_has_hypervisor(env)))) {
+ cpu_raise_exception_ra(env, TT_PRIV_ACT, ra);
+ }
+}
+#endif /* !CONFIG_USER_ONLY */
#endif
static void do_check_align(CPUSPARCState *env, target_ulong addr,
@@ -1119,13 +1183,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr,
asi &= 0xff;
- if ((asi < 0x80 && (env->pstate & PS_PRIV) == 0)
- || (cpu_has_hypervisor(env)
- && asi >= 0x30 && asi < 0x80
- && !(env->hpstate & HS_PRIV))) {
- cpu_raise_exception_ra(env, TT_PRIV_ACT, GETPC());
- }
-
+ do_check_asi(env, asi, GETPC());
do_check_align(env, addr, size - 1, GETPC());
addr = asi_address_mask(env, asi, addr);
@@ -1220,30 +1278,39 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr,
case ASI_IMMU: /* I-MMU regs */
{
int reg = (addr >> 3) & 0xf;
-
- if (reg == 0) {
- /* I-TSB Tag Target register */
+ switch (reg) {
+ case 0:
+ /* 0x00 I-TSB Tag Target register */
ret = ultrasparc_tag_target(env->immu.tag_access);
- } else {
- ret = env->immuregs[reg];
+ break;
+ case 3: /* SFSR */
+ ret = env->immu.sfsr;
+ break;
+ case 5: /* TSB access */
+ ret = env->immu.tsb;
+ break;
+ case 6:
+ /* 0x30 I-TSB Tag Access register */
+ ret = env->immu.tag_access;
+ break;
+ default:
+ cpu_unassigned_access(cs, addr, false, false, 1, size);
+ ret = 0;
}
-
break;
}
case ASI_IMMU_TSB_8KB_PTR: /* I-MMU 8k TSB pointer */
{
/* env->immuregs[5] holds I-MMU TSB register value
env->immuregs[6] holds I-MMU Tag Access register value */
- ret = ultrasparc_tsb_pointer(env->immu.tsb, env->immu.tag_access,
- 8*1024);
+ ret = ultrasparc_tsb_pointer(env, &env->immu, 0);
break;
}
case ASI_IMMU_TSB_64KB_PTR: /* I-MMU 64k TSB pointer */
{
/* env->immuregs[5] holds I-MMU TSB register value
env->immuregs[6] holds I-MMU Tag Access register value */
- ret = ultrasparc_tsb_pointer(env->immu.tsb, env->immu.tag_access,
- 64*1024);
+ ret = ultrasparc_tsb_pointer(env, &env->immu, 1);
break;
}
case ASI_ITLB_DATA_ACCESS: /* I-MMU data access */
@@ -1263,12 +1330,38 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr,
case ASI_DMMU: /* D-MMU regs */
{
int reg = (addr >> 3) & 0xf;
-
- if (reg == 0) {
- /* D-TSB Tag Target register */
+ switch (reg) {
+ case 0:
+ /* 0x00 D-TSB Tag Target register */
ret = ultrasparc_tag_target(env->dmmu.tag_access);
- } else {
- ret = env->dmmuregs[reg];
+ break;
+ case 1: /* 0x08 Primary Context */
+ ret = env->dmmu.mmu_primary_context;
+ break;
+ case 2: /* 0x10 Secondary Context */
+ ret = env->dmmu.mmu_secondary_context;
+ break;
+ case 3: /* SFSR */
+ ret = env->dmmu.sfsr;
+ break;
+ case 4: /* 0x20 SFAR */
+ ret = env->dmmu.sfar;
+ break;
+ case 5: /* 0x28 TSB access */
+ ret = env->dmmu.tsb;
+ break;
+ case 6: /* 0x30 D-TSB Tag Access register */
+ ret = env->dmmu.tag_access;
+ break;
+ case 7:
+ ret = env->dmmu.virtual_watchpoint;
+ break;
+ case 8:
+ ret = env->dmmu.physical_watchpoint;
+ break;
+ default:
+ cpu_unassigned_access(cs, addr, false, false, 1, size);
+ ret = 0;
}
break;
}
@@ -1276,16 +1369,14 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr,
{
/* env->dmmuregs[5] holds D-MMU TSB register value
env->dmmuregs[6] holds D-MMU Tag Access register value */
- ret = ultrasparc_tsb_pointer(env->dmmu.tsb, env->dmmu.tag_access,
- 8*1024);
+ ret = ultrasparc_tsb_pointer(env, &env->dmmu, 0);
break;
}
case ASI_DMMU_TSB_64KB_PTR: /* D-MMU 64k TSB pointer */
{
/* env->dmmuregs[5] holds D-MMU TSB register value
env->dmmuregs[6] holds D-MMU Tag Access register value */
- ret = ultrasparc_tsb_pointer(env->dmmu.tsb, env->dmmu.tag_access,
- 64*1024);
+ ret = ultrasparc_tsb_pointer(env, &env->dmmu, 1);
break;
}
case ASI_DTLB_DATA_ACCESS: /* D-MMU data access */
@@ -1315,6 +1406,30 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr,
}
break;
}
+ case ASI_SCRATCHPAD: /* UA2005 privileged scratchpad */
+ if (unlikely((addr >= 0x20) && (addr < 0x30))) {
+ /* Hyperprivileged access only */
+ cpu_unassigned_access(cs, addr, false, false, 1, size);
+ }
+ /* fall through */
+ case ASI_HYP_SCRATCHPAD: /* UA2005 hyperprivileged scratchpad */
+ {
+ unsigned int i = (addr >> 3) & 0x7;
+ ret = env->scratch[i];
+ break;
+ }
+ case ASI_MMU: /* UA2005 Context ID registers */
+ switch ((addr >> 3) & 0x3) {
+ case 1:
+ ret = env->dmmu.mmu_primary_context;
+ break;
+ case 2:
+ ret = env->dmmu.mmu_secondary_context;
+ break;
+ default:
+ cpu_unassigned_access(cs, addr, true, false, 1, size);
+ }
+ break;
case ASI_DCACHE_DATA: /* D-cache data */
case ASI_DCACHE_TAG: /* D-cache tag access */
case ASI_ESTATE_ERROR_EN: /* E-cache error enable */
@@ -1375,13 +1490,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
asi &= 0xff;
- if ((asi < 0x80 && (env->pstate & PS_PRIV) == 0)
- || (cpu_has_hypervisor(env)
- && asi >= 0x30 && asi < 0x80
- && !(env->hpstate & HS_PRIV))) {
- cpu_raise_exception_ra(env, TT_PRIV_ACT, GETPC());
- }
-
+ do_check_asi(env, asi, GETPC());
do_check_align(env, addr, size - 1, GETPC());
addr = asi_address_mask(env, asi, addr);
@@ -1417,7 +1526,67 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
case ASI_TWINX_SL: /* Secondary, twinx, LE */
/* These are always handled inline. */
g_assert_not_reached();
-
+ /* these ASIs have different functions on UltraSPARC-IIIi
+ * and UA2005 CPUs. Use the explicit numbers to avoid confusion
+ */
+ case 0x31:
+ case 0x32:
+ case 0x39:
+ case 0x3a:
+ if (cpu_has_hypervisor(env)) {
+ /* UA2005
+ * ASI_DMMU_CTX_ZERO_TSB_BASE_PS0
+ * ASI_DMMU_CTX_ZERO_TSB_BASE_PS1
+ * ASI_DMMU_CTX_NONZERO_TSB_BASE_PS0
+ * ASI_DMMU_CTX_NONZERO_TSB_BASE_PS1
+ */
+ int idx = ((asi & 2) >> 1) | ((asi & 8) >> 2);
+ env->dmmu.sun4v_tsb_pointers[idx] = val;
+ } else {
+ helper_raise_exception(env, TT_ILL_INSN);
+ }
+ break;
+ case 0x33:
+ case 0x3b:
+ if (cpu_has_hypervisor(env)) {
+ /* UA2005
+ * ASI_DMMU_CTX_ZERO_CONFIG
+ * ASI_DMMU_CTX_NONZERO_CONFIG
+ */
+ env->dmmu.sun4v_ctx_config[(asi & 8) >> 3] = val;
+ } else {
+ helper_raise_exception(env, TT_ILL_INSN);
+ }
+ break;
+ case 0x35:
+ case 0x36:
+ case 0x3d:
+ case 0x3e:
+ if (cpu_has_hypervisor(env)) {
+ /* UA2005
+ * ASI_IMMU_CTX_ZERO_TSB_BASE_PS0
+ * ASI_IMMU_CTX_ZERO_TSB_BASE_PS1
+ * ASI_IMMU_CTX_NONZERO_TSB_BASE_PS0
+ * ASI_IMMU_CTX_NONZERO_TSB_BASE_PS1
+ */
+ int idx = ((asi & 2) >> 1) | ((asi & 8) >> 2);
+ env->immu.sun4v_tsb_pointers[idx] = val;
+ } else {
+ helper_raise_exception(env, TT_ILL_INSN);
+ }
+ break;
+ case 0x37:
+ case 0x3f:
+ if (cpu_has_hypervisor(env)) {
+ /* UA2005
+ * ASI_IMMU_CTX_ZERO_CONFIG
+ * ASI_IMMU_CTX_NONZERO_CONFIG
+ */
+ env->immu.sun4v_ctx_config[(asi & 8) >> 3] = val;
+ } else {
+ helper_raise_exception(env, TT_ILL_INSN);
+ }
+ break;
case ASI_UPA_CONFIG: /* UPA config */
/* XXX */
return;
@@ -1429,7 +1598,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
int reg = (addr >> 3) & 0xf;
uint64_t oldreg;
- oldreg = env->immuregs[reg];
+ oldreg = env->immu.mmuregs[reg];
switch (reg) {
case 0: /* RO */
return;
@@ -1456,10 +1625,11 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
case 8:
return;
default:
+ cpu_unassigned_access(cs, addr, true, false, 1, size);
break;
}
- if (oldreg != env->immuregs[reg]) {
+ if (oldreg != env->immu.mmuregs[reg]) {
DPRINTF_MMU("immu change reg[%d]: 0x%016" PRIx64 " -> 0x%016"
PRIx64 "\n", reg, oldreg, env->immuregs[reg]);
}
@@ -1469,7 +1639,11 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
return;
}
case ASI_ITLB_DATA_IN: /* I-MMU data in */
- replace_tlb_1bit_lru(env->itlb, env->immu.tag_access, val, "immu", env);
+ /* ignore real translation entries */
+ if (!(addr & TLB_UST1_IS_REAL_BIT)) {
+ replace_tlb_1bit_lru(env->itlb, env->immu.tag_access,
+ val, "immu", env, addr);
+ }
return;
case ASI_ITLB_DATA_ACCESS: /* I-MMU data access */
{
@@ -1477,8 +1651,11 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
unsigned int i = (addr >> 3) & 0x3f;
- replace_tlb_entry(&env->itlb[i], env->immu.tag_access, val, env);
-
+ /* ignore real translation entries */
+ if (!(addr & TLB_UST1_IS_REAL_BIT)) {
+ replace_tlb_entry(&env->itlb[i], env->immu.tag_access,
+ sun4v_tte_to_sun4u(env, addr, val), env);
+ }
#ifdef DEBUG_MMU
DPRINTF_MMU("immu data access replaced entry [%i]\n", i);
dump_mmu(stdout, fprintf, env);
@@ -1493,7 +1670,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
int reg = (addr >> 3) & 0xf;
uint64_t oldreg;
- oldreg = env->dmmuregs[reg];
+ oldreg = env->dmmu.mmuregs[reg];
switch (reg) {
case 0: /* RO */
case 4:
@@ -1526,13 +1703,17 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
env->dmmu.tag_access = val;
break;
case 7: /* Virtual Watchpoint */
+ env->dmmu.virtual_watchpoint = val;
+ break;
case 8: /* Physical Watchpoint */
+ env->dmmu.physical_watchpoint = val;
+ break;
default:
- env->dmmuregs[reg] = val;
+ cpu_unassigned_access(cs, addr, true, false, 1, size);
break;
}
- if (oldreg != env->dmmuregs[reg]) {
+ if (oldreg != env->dmmu.mmuregs[reg]) {
DPRINTF_MMU("dmmu change reg[%d]: 0x%016" PRIx64 " -> 0x%016"
PRIx64 "\n", reg, oldreg, env->dmmuregs[reg]);
}
@@ -1542,14 +1723,21 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
return;
}
case ASI_DTLB_DATA_IN: /* D-MMU data in */
- replace_tlb_1bit_lru(env->dtlb, env->dmmu.tag_access, val, "dmmu", env);
- return;
+ /* ignore real translation entries */
+ if (!(addr & TLB_UST1_IS_REAL_BIT)) {
+ replace_tlb_1bit_lru(env->dtlb, env->dmmu.tag_access,
+ val, "dmmu", env, addr);
+ }
+ return;
case ASI_DTLB_DATA_ACCESS: /* D-MMU data access */
{
unsigned int i = (addr >> 3) & 0x3f;
- replace_tlb_entry(&env->dtlb[i], env->dmmu.tag_access, val, env);
-
+ /* ignore real translation entries */
+ if (!(addr & TLB_UST1_IS_REAL_BIT)) {
+ replace_tlb_entry(&env->dtlb[i], env->dmmu.tag_access,
+ sun4v_tte_to_sun4u(env, addr, val), env);
+ }
#ifdef DEBUG_MMU
DPRINTF_MMU("dmmu data access replaced entry [%i]\n", i);
dump_mmu(stdout, fprintf, env);
@@ -1562,6 +1750,38 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
case ASI_INTR_RECEIVE: /* Interrupt data receive */
env->ivec_status = val & 0x20;
return;
+ case ASI_SCRATCHPAD: /* UA2005 privileged scratchpad */
+ if (unlikely((addr >= 0x20) && (addr < 0x30))) {
+ /* Hyperprivileged access only */
+ cpu_unassigned_access(cs, addr, true, false, 1, size);
+ }
+ /* fall through */
+ case ASI_HYP_SCRATCHPAD: /* UA2005 hyperprivileged scratchpad */
+ {
+ unsigned int i = (addr >> 3) & 0x7;
+ env->scratch[i] = val;
+ return;
+ }
+ case ASI_MMU: /* UA2005 Context ID registers */
+ {
+ switch ((addr >> 3) & 0x3) {
+ case 1:
+ env->dmmu.mmu_primary_context = val;
+ env->immu.mmu_primary_context = val;
+ tlb_flush_by_mmuidx(CPU(cpu), MMU_USER_IDX, MMU_KERNEL_IDX, -1);
+ break;
+ case 2:
+ env->dmmu.mmu_secondary_context = val;
+ env->immu.mmu_secondary_context = val;
+ tlb_flush_by_mmuidx(CPU(cpu), MMU_USER_SECONDARY_IDX,
+ MMU_KERNEL_SECONDARY_IDX, -1);
+ break;
+ default:
+ cpu_unassigned_access(cs, addr, true, false, 1, size);
+ }
+ }
+ return;
+ case ASI_QUEUE: /* UA2005 CPU mondo queue */
case ASI_DCACHE_DATA: /* D-cache data */
case ASI_DCACHE_TAG: /* D-cache tag access */
case ASI_ESTATE_ERROR_EN: /* E-cache error enable */
@@ -1664,14 +1884,25 @@ void sparc_cpu_unassigned_access(CPUState *cs, hwaddr addr,
{
SPARCCPU *cpu = SPARC_CPU(cs);
CPUSPARCState *env = &cpu->env;
- int tt = is_exec ? TT_CODE_ACCESS : TT_DATA_ACCESS;
#ifdef DEBUG_UNASSIGNED
printf("Unassigned mem access to " TARGET_FMT_plx " from " TARGET_FMT_lx
"\n", addr, env->pc);
#endif
- cpu_raise_exception_ra(env, tt, GETPC());
+ if (is_exec) { /* XXX has_hypervisor */
+ if (env->lsu & (IMMU_E)) {
+ cpu_raise_exception_ra(env, TT_CODE_ACCESS, GETPC());
+ } else if (cpu_has_hypervisor(env) && !(env->hpstate & HS_PRIV)) {
+ cpu_raise_exception_ra(env, TT_INSN_REAL_TRANSLATION_MISS, GETPC());
+ }
+ } else {
+ if (env->lsu & (DMMU_E)) {
+ cpu_raise_exception_ra(env, TT_DATA_ACCESS, GETPC());
+ } else if (cpu_has_hypervisor(env) && !(env->hpstate & HS_PRIV)) {
+ cpu_raise_exception_ra(env, TT_DATA_REAL_TRANSLATION_MISS, GETPC());
+ }
+ }
}
#endif
#endif
diff --git a/target/sparc/machine.c b/target/sparc/machine.c
index aea6397..39e262c 100644
--- a/target/sparc/machine.c
+++ b/target/sparc/machine.c
@@ -148,8 +148,8 @@ const VMStateDescription vmstate_sparc_cpu = {
VMSTATE_UINT64_ARRAY(env.mmubpregs, SPARCCPU, 4),
#else
VMSTATE_UINT64(env.lsu, SPARCCPU),
- VMSTATE_UINT64_ARRAY(env.immuregs, SPARCCPU, 16),
- VMSTATE_UINT64_ARRAY(env.dmmuregs, SPARCCPU, 16),
+ VMSTATE_UINT64_ARRAY(env.immu.mmuregs, SPARCCPU, 16),
+ VMSTATE_UINT64_ARRAY(env.dmmu.mmuregs, SPARCCPU, 16),
VMSTATE_STRUCT_ARRAY(env.itlb, SPARCCPU, 64, 0,
vmstate_tlb_entry, SparcTLBEntry),
VMSTATE_STRUCT_ARRAY(env.dtlb, SPARCCPU, 64, 0,
diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c
index 044e88c..8b4664d 100644
--- a/target/sparc/mmu_helper.c
+++ b/target/sparc/mmu_helper.c
@@ -456,23 +456,7 @@ static inline int ultrasparc_tag_match(SparcTLBEntry *tlb,
uint64_t address, uint64_t context,
hwaddr *physical)
{
- uint64_t mask;
-
- switch (TTE_PGSIZE(tlb->tte)) {
- default:
- case 0x0: /* 8k */
- mask = 0xffffffffffffe000ULL;
- break;
- case 0x1: /* 64k */
- mask = 0xffffffffffff0000ULL;
- break;
- case 0x2: /* 512k */
- mask = 0xfffffffffff80000ULL;
- break;
- case 0x3: /* 4M */
- mask = 0xffffffffffc00000ULL;
- break;
- }
+ uint64_t mask = -(8192ULL << 3 * TTE_PGSIZE(tlb->tte));
/* valid, context match, virtual address match? */
if (TTE_IS_VALID(tlb->tte) &&
@@ -757,6 +741,8 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUSPARCState *env)
PRId64 "\n",
env->dmmu.mmu_primary_context,
env->dmmu.mmu_secondary_context);
+ (*cpu_fprintf)(f, "DMMU Tag Access: %" PRIx64 ", TSB Tag Target: %" PRIx64
+ "\n", env->dmmu.tag_access, env->dmmu.tsb_tag_target);
if ((env->lsu & DMMU_E) == 0) {
(*cpu_fprintf)(f, "DMMU disabled\n");
} else {
diff --git a/target/sparc/translate.c b/target/sparc/translate.c
index ead585e..655060c 100644
--- a/target/sparc/translate.c
+++ b/target/sparc/translate.c
@@ -72,9 +72,16 @@ typedef struct DisasContext {
target_ulong jump_pc[2]; /* used when JUMP_PC pc value is used */
int is_br;
int mem_idx;
- int fpu_enabled;
- int address_mask_32bit;
- int singlestep;
+ bool fpu_enabled;
+ bool address_mask_32bit;
+ bool singlestep;
+#ifndef CONFIG_USER_ONLY
+ bool supervisor;
+#ifdef TARGET_SPARC64
+ bool hypervisor;
+#endif
+#endif
+
uint32_t cc_op; /* current CC operation */
struct TranslationBlock *tb;
sparc_def_t *def;
@@ -283,10 +290,11 @@ static void gen_move_Q(DisasContext *dc, unsigned int rd, unsigned int rs)
#define hypervisor(dc) 0
#endif
#else
-#define supervisor(dc) (dc->mem_idx >= MMU_KERNEL_IDX)
#ifdef TARGET_SPARC64
-#define hypervisor(dc) (dc->mem_idx == MMU_HYPV_IDX)
+#define hypervisor(dc) (dc->hypervisor)
+#define supervisor(dc) (dc->supervisor | dc->hypervisor)
#else
+#define supervisor(dc) (dc->supervisor)
#endif
#endif
@@ -2134,7 +2142,11 @@ static DisasASI get_asi(DisasContext *dc, int insn, TCGMemOp memop)
case ASI_TWINX_NL:
case ASI_NUCLEUS_QUAD_LDD:
case ASI_NUCLEUS_QUAD_LDD_L:
- mem_idx = MMU_NUCLEUS_IDX;
+ if (hypervisor(dc)) {
+ mem_idx = MMU_PHYS_IDX;
+ } else {
+ mem_idx = MMU_NUCLEUS_IDX;
+ }
break;
case ASI_AIUP: /* As if user primary */
case ASI_AIUPL: /* As if user primary LE */
@@ -2309,8 +2321,19 @@ static void gen_st_asi(DisasContext *dc, TCGv src, TCGv addr,
case GET_ASI_EXCP:
break;
case GET_ASI_DTWINX: /* Reserved for stda. */
+#ifndef TARGET_SPARC64
gen_exception(dc, TT_ILL_INSN);
break;
+#else
+ if (!(dc->def->features & CPU_FEATURE_HYPV)) {
+ /* Pre OpenSPARC CPUs don't have these */
+ gen_exception(dc, TT_ILL_INSN);
+ return;
+ }
+ /* in OpenSPARC T1+ CPUs TWINX ASIs in store instructions
+ * are ST_BLKINIT_ ASIs */
+ /* fall through */
+#endif
case GET_ASI_DIRECT:
gen_address_mask(dc, addr);
tcg_gen_qemu_st_tl(src, addr, da.mem_idx, da.memop);
@@ -3286,7 +3309,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn)
rs1 = GET_FIELD_SP(insn, 14, 18);
if (IS_IMM) {
- rs2 = GET_FIELD_SP(insn, 0, 6);
+ rs2 = GET_FIELD_SP(insn, 0, 7);
if (rs1 == 0) {
tcg_gen_movi_i32(trap, (rs2 & mask) + TT_TRAP);
/* Signal that the trap value is fully constant. */
@@ -3421,6 +3444,17 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn)
case 0x19: /* System tick compare */
gen_store_gpr(dc, rd, cpu_stick_cmpr);
break;
+ case 0x1a: /* UltraSPARC-T1 Strand status */
+ /* XXX HYPV check maybe not enough, UA2005 & UA2007 describe
+ * this ASR as impl. dep
+ */
+ CHECK_IU_FEATURE(dc, HYPV);
+ {
+ TCGv t = gen_dest_gpr(dc, rd);
+ tcg_gen_movi_tl(t, 1UL);
+ gen_store_gpr(dc, rd, t);
+ }
+ break;
case 0x10: /* Performance Control */
case 0x11: /* Performance Instrumentation Counter */
case 0x12: /* Dispatch Control */
@@ -3445,7 +3479,8 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn)
rs1 = GET_FIELD(insn, 13, 17);
switch (rs1) {
case 0: // hpstate
- // gen_op_rdhpstate();
+ tcg_gen_ld_i64(cpu_dst, cpu_env,
+ offsetof(CPUSPARCState, hpstate));
break;
case 1: // htstate
// gen_op_rdhtstate();
@@ -4535,8 +4570,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn)
break;
case 16: // UA2005 gl
CHECK_IU_FEATURE(dc, GL);
- tcg_gen_st32_tl(cpu_tmp0, cpu_env,
- offsetof(CPUSPARCState, gl));
+ gen_helper_wrgl(cpu_env, cpu_tmp0);
break;
case 26: // UA2005 strand status
CHECK_IU_FEATURE(dc, HYPV);
@@ -4570,7 +4604,9 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn)
tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2);
switch (rd) {
case 0: // hpstate
- // XXX gen_op_wrhpstate();
+ tcg_gen_st_i64(cpu_tmp0, cpu_env,
+ offsetof(CPUSPARCState,
+ hpstate));
save_state(dc);
gen_op_next_insn();
tcg_gen_exit_tb(0);
@@ -5710,9 +5746,15 @@ void gen_intermediate_code(CPUSPARCState * env, TranslationBlock * tb)
dc->fpu_enabled = tb_fpu_enabled(tb->flags);
dc->address_mask_32bit = tb_am_enabled(tb->flags);
dc->singlestep = (cs->singlestep_enabled || singlestep);
+#ifndef CONFIG_USER_ONLY
+ dc->supervisor = (tb->flags & TB_FLAG_SUPER) != 0;
+#endif
#ifdef TARGET_SPARC64
dc->fprs_dirty = 0;
dc->asi = (tb->flags >> TB_FLAG_ASI_SHIFT) & 0xff;
+#ifndef CONFIG_USER_ONLY
+ dc->hypervisor = (tb->flags & TB_FLAG_HYPER) != 0;
+#endif
#endif
num_insns = 0;
diff --git a/target/sparc/win_helper.c b/target/sparc/win_helper.c
index 2d5b546..71b3dd3 100644
--- a/target/sparc/win_helper.c
+++ b/target/sparc/win_helper.c
@@ -290,6 +290,10 @@ void helper_wrcwp(CPUSPARCState *env, target_ulong new_cwp)
static inline uint64_t *get_gregset(CPUSPARCState *env, uint32_t pstate)
{
+ if (env->def->features & CPU_FEATURE_GL) {
+ return env->glregs + (env->gl & 7) * 8;
+ }
+
switch (pstate) {
default:
trace_win_helper_gregset_error(pstate);
@@ -305,14 +309,40 @@ static inline uint64_t *get_gregset(CPUSPARCState *env, uint32_t pstate)
}
}
+static inline uint64_t *get_gl_gregset(CPUSPARCState *env, uint32_t gl)
+{
+ return env->glregs + (gl & 7) * 8;
+}
+
+/* Switch global register bank */
+void cpu_gl_switch_gregs(CPUSPARCState *env, uint32_t new_gl)
+{
+ uint64_t *src, *dst;
+ src = get_gl_gregset(env, new_gl);
+ dst = get_gl_gregset(env, env->gl);
+
+ if (src != dst) {
+ memcpy32(dst, env->gregs);
+ memcpy32(env->gregs, src);
+ }
+}
+
+void helper_wrgl(CPUSPARCState *env, target_ulong new_gl)
+{
+ cpu_gl_switch_gregs(env, new_gl & 7);
+ env->gl = new_gl & 7;
+}
+
void cpu_change_pstate(CPUSPARCState *env, uint32_t new_pstate)
{
uint32_t pstate_regs, new_pstate_regs;
uint64_t *src, *dst;
if (env->def->features & CPU_FEATURE_GL) {
- /* PS_AG is not implemented in this case */
- new_pstate &= ~PS_AG;
+ /* PS_AG, IG and MG are not implemented in this case */
+ new_pstate &= ~(PS_AG | PS_IG | PS_MG);
+ env->pstate = new_pstate;
+ return;
}
pstate_regs = env->pstate & 0xc01;
@@ -366,6 +396,12 @@ void helper_done(CPUSPARCState *env)
env->asi = (tsptr->tstate >> 24) & 0xff;
cpu_change_pstate(env, (tsptr->tstate >> 8) & 0xf3f);
cpu_put_cwp64(env, tsptr->tstate & 0xff);
+ if (cpu_has_hypervisor(env)) {
+ uint32_t new_gl = (tsptr->tstate >> 40) & 7;
+ env->hpstate = env->htstate[env->tl];
+ cpu_gl_switch_gregs(env, new_gl);
+ env->gl = new_gl;
+ }
env->tl--;
trace_win_helper_done(env->tl);
@@ -387,6 +423,12 @@ void helper_retry(CPUSPARCState *env)
env->asi = (tsptr->tstate >> 24) & 0xff;
cpu_change_pstate(env, (tsptr->tstate >> 8) & 0xf3f);
cpu_put_cwp64(env, tsptr->tstate & 0xff);
+ if (cpu_has_hypervisor(env)) {
+ uint32_t new_gl = (tsptr->tstate >> 40) & 7;
+ env->hpstate = env->htstate[env->tl];
+ cpu_gl_switch_gregs(env, new_gl);
+ env->gl = new_gl;
+ }
env->tl--;
trace_win_helper_retry(env->tl);