diff options
Diffstat (limited to 'target')
-rw-r--r-- | target/sparc/asi.h | 1 | ||||
-rw-r--r-- | target/sparc/cpu.c | 13 | ||||
-rw-r--r-- | target/sparc/cpu.h | 105 | ||||
-rw-r--r-- | target/sparc/helper.h | 1 | ||||
-rw-r--r-- | target/sparc/int64_helper.c | 43 | ||||
-rw-r--r-- | target/sparc/ldst_helper.c | 387 | ||||
-rw-r--r-- | target/sparc/machine.c | 4 | ||||
-rw-r--r-- | target/sparc/mmu_helper.c | 20 | ||||
-rw-r--r-- | target/sparc/translate.c | 64 | ||||
-rw-r--r-- | target/sparc/win_helper.c | 46 |
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); |