diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2016-11-01 11:21:02 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2016-11-01 11:21:02 +0000 |
commit | bf99fd3983d7185178a0f65ce29bb94b1aecaed1 (patch) | |
tree | 8663962d1a07705404c33eb0572caeb690fd66d7 /target-sparc | |
parent | 0e356366516b9e1f8cb03945d1ce72e8e0751fb0 (diff) | |
parent | 5a7267b6a9e94c264ca77a7ca5a239e70dac81da (diff) |
Merge remote-tracking branch 'remotes/rth/tags/pull-sparc-20161031-2' into staging
target-sparc updates for atomics and alignment
# gpg: Signature made Mon 31 Oct 2016 20:47:57 GMT
# gpg: using RSA key 0xAD1270CC4DD0279B
# gpg: Good signature from "Richard Henderson <rth7680@gmail.com>"
# gpg: aka "Richard Henderson <rth@redhat.com>"
# gpg: aka "Richard Henderson <rth@twiddle.net>"
# Primary key fingerprint: 9CB1 8DDA F8E8 49AD 2AFC 16A4 AD12 70CC 4DD0 279B
* remotes/rth/tags/pull-sparc-20161031-2:
target-sparc: Use tcg_gen_atomic_cmpxchg_tl
target-sparc: Use tcg_gen_atomic_xchg_tl
target-sparc: Remove MMU_MODE*_SUFFIX
target-sparc: Allow 4-byte alignment on fp mem ops
target-sparc: Implement ldqf and stqf inline
target-sparc: Remove asi helper code handled inline
target-sparc: Implement BCOPY/BFILL inline
target-sparc: Implement cas_asi/casx_asi inline
target-sparc: Implement ldstub_asi inline
target-sparc: Implement swap_asi inline
target-sparc: Handle more twinx asis
target-sparc: Use MMU_PHYS_IDX for bypass asis
target-sparc: Add MMU_PHYS_IDX
target-sparc: Introduce cpu_raise_exception_ra
target-sparc: Use overalignment flags for twinx and block asis
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'target-sparc')
-rw-r--r-- | target-sparc/cpu.h | 34 | ||||
-rw-r--r-- | target-sparc/helper.c | 52 | ||||
-rw-r--r-- | target-sparc/helper.h | 7 | ||||
-rw-r--r-- | target-sparc/ldst_helper.c | 1021 | ||||
-rw-r--r-- | target-sparc/mmu_helper.c | 47 | ||||
-rw-r--r-- | target-sparc/translate.c | 434 | ||||
-rw-r--r-- | target-sparc/win_helper.c | 37 |
7 files changed, 540 insertions, 1092 deletions
diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h index 646a103513..5fb0ed1aad 100644 --- a/target-sparc/cpu.h +++ b/target-sparc/cpu.h @@ -225,9 +225,9 @@ enum { #define MAX_NWINDOWS 32 #if !defined(TARGET_SPARC64) -#define NB_MMU_MODES 2 +#define NB_MMU_MODES 3 #else -#define NB_MMU_MODES 6 +#define NB_MMU_MODES 7 typedef struct trap_state { uint64_t tpc; uint64_t tnpc; @@ -549,6 +549,7 @@ void QEMU_NORETURN sparc_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr); +void cpu_raise_exception_ra(CPUSPARCState *, int, uintptr_t) QEMU_NORETURN; #ifndef NO_CPU_IO_DEFS /* cpu_init.c */ @@ -637,22 +638,16 @@ int cpu_sparc_signal_handler(int host_signum, void *pinfo, void *puc); /* MMU modes definitions */ #if defined (TARGET_SPARC64) #define MMU_USER_IDX 0 -#define MMU_MODE0_SUFFIX _user #define MMU_USER_SECONDARY_IDX 1 -#define MMU_MODE1_SUFFIX _user_secondary #define MMU_KERNEL_IDX 2 -#define MMU_MODE2_SUFFIX _kernel #define MMU_KERNEL_SECONDARY_IDX 3 -#define MMU_MODE3_SUFFIX _kernel_secondary #define MMU_NUCLEUS_IDX 4 -#define MMU_MODE4_SUFFIX _nucleus #define MMU_HYPV_IDX 5 -#define MMU_MODE5_SUFFIX _hypv +#define MMU_PHYS_IDX 6 #else #define MMU_USER_IDX 0 -#define MMU_MODE0_SUFFIX _user #define MMU_KERNEL_IDX 1 -#define MMU_MODE1_SUFFIX _kernel +#define MMU_PHYS_IDX 2 #endif #if defined (TARGET_SPARC64) @@ -672,18 +667,27 @@ static inline int cpu_supervisor_mode(CPUSPARCState *env1) } #endif -static inline int cpu_mmu_index(CPUSPARCState *env1, bool ifetch) +static inline int cpu_mmu_index(CPUSPARCState *env, bool ifetch) { #if defined(CONFIG_USER_ONLY) return MMU_USER_IDX; #elif !defined(TARGET_SPARC64) - return env1->psrs; + if ((env->mmuregs[0] & MMU_E) == 0) { /* MMU disabled */ + return MMU_PHYS_IDX; + } else { + return env->psrs; + } #else - if (env1->tl > 0) { + /* IMMU or DMMU disabled. */ + if (ifetch + ? (env->lsu & IMMU_E) == 0 || (env->pstate & PS_RED) != 0 + : (env->lsu & DMMU_E) == 0) { + return MMU_PHYS_IDX; + } else if (env->tl > 0) { return MMU_NUCLEUS_IDX; - } else if (cpu_hypervisor_mode(env1)) { + } else if (cpu_hypervisor_mode(env)) { return MMU_HYPV_IDX; - } else if (cpu_supervisor_mode(env1)) { + } else if (cpu_supervisor_mode(env)) { return MMU_KERNEL_IDX; } else { return MMU_USER_IDX; diff --git a/target-sparc/helper.c b/target-sparc/helper.c index bedc6722a1..359b0b15ed 100644 --- a/target-sparc/helper.c +++ b/target-sparc/helper.c @@ -24,6 +24,14 @@ #include "exec/helper-proto.h" #include "sysemu/sysemu.h" +void cpu_raise_exception_ra(CPUSPARCState *env, int tt, uintptr_t ra) +{ + CPUState *cs = CPU(sparc_env_get_cpu(env)); + + cs->exception_index = tt; + cpu_loop_exit_restore(cs, ra); +} + void helper_raise_exception(CPUSPARCState *env, int tt) { CPUState *cs = CPU(sparc_env_get_cpu(env)); @@ -59,7 +67,7 @@ uint64_t helper_tick_get_count(CPUSPARCState *env, void *opaque, int mem_idx) CPUTimer *timer = opaque; if (timer->npt && mem_idx < MMU_KERNEL_IDX) { - helper_raise_exception(env, TT_PRIV_INSN); + cpu_raise_exception_ra(env, TT_PRIV_INSN, GETPC()); } return cpu_tick_get_count(timer); @@ -76,10 +84,9 @@ void helper_tick_set_limit(void *opaque, uint64_t limit) } #endif -static target_ulong helper_udiv_common(CPUSPARCState *env, target_ulong a, - target_ulong b, int cc) +static target_ulong do_udiv(CPUSPARCState *env, target_ulong a, + target_ulong b, int cc, uintptr_t ra) { - SPARCCPU *cpu = sparc_env_get_cpu(env); int overflow = 0; uint64_t x0; uint32_t x1; @@ -88,8 +95,7 @@ static target_ulong helper_udiv_common(CPUSPARCState *env, target_ulong a, x1 = (b & 0xffffffff); if (x1 == 0) { - cpu_restore_state(CPU(cpu), GETPC()); - helper_raise_exception(env, TT_DIV_ZERO); + cpu_raise_exception_ra(env, TT_DIV_ZERO, ra); } x0 = x0 / x1; @@ -108,18 +114,17 @@ static target_ulong helper_udiv_common(CPUSPARCState *env, target_ulong a, target_ulong helper_udiv(CPUSPARCState *env, target_ulong a, target_ulong b) { - return helper_udiv_common(env, a, b, 0); + return do_udiv(env, a, b, 0, GETPC()); } target_ulong helper_udiv_cc(CPUSPARCState *env, target_ulong a, target_ulong b) { - return helper_udiv_common(env, a, b, 1); + return do_udiv(env, a, b, 1, GETPC()); } -static target_ulong helper_sdiv_common(CPUSPARCState *env, target_ulong a, - target_ulong b, int cc) +static target_ulong do_sdiv(CPUSPARCState *env, target_ulong a, + target_ulong b, int cc, uintptr_t ra) { - SPARCCPU *cpu = sparc_env_get_cpu(env); int overflow = 0; int64_t x0; int32_t x1; @@ -128,8 +133,7 @@ static target_ulong helper_sdiv_common(CPUSPARCState *env, target_ulong a, x1 = (b & 0xffffffff); if (x1 == 0) { - cpu_restore_state(CPU(cpu), GETPC()); - helper_raise_exception(env, TT_DIV_ZERO); + cpu_raise_exception_ra(env, TT_DIV_ZERO, ra); } else if (x1 == -1 && x0 == INT64_MIN) { x0 = INT32_MAX; overflow = 1; @@ -151,12 +155,12 @@ static target_ulong helper_sdiv_common(CPUSPARCState *env, target_ulong a, target_ulong helper_sdiv(CPUSPARCState *env, target_ulong a, target_ulong b) { - return helper_sdiv_common(env, a, b, 0); + return do_sdiv(env, a, b, 0, GETPC()); } target_ulong helper_sdiv_cc(CPUSPARCState *env, target_ulong a, target_ulong b) { - return helper_sdiv_common(env, a, b, 1); + return do_sdiv(env, a, b, 1, GETPC()); } #ifdef TARGET_SPARC64 @@ -164,10 +168,7 @@ int64_t helper_sdivx(CPUSPARCState *env, int64_t a, int64_t b) { if (b == 0) { /* Raise divide by zero trap. */ - SPARCCPU *cpu = sparc_env_get_cpu(env); - - cpu_restore_state(CPU(cpu), GETPC()); - helper_raise_exception(env, TT_DIV_ZERO); + cpu_raise_exception_ra(env, TT_DIV_ZERO, GETPC()); } else if (b == -1) { /* Avoid overflow trap with i386 divide insn. */ return -a; @@ -180,10 +181,7 @@ uint64_t helper_udivx(CPUSPARCState *env, uint64_t a, uint64_t b) { if (b == 0) { /* Raise divide by zero trap. */ - SPARCCPU *cpu = sparc_env_get_cpu(env); - - cpu_restore_state(CPU(cpu), GETPC()); - helper_raise_exception(env, TT_DIV_ZERO); + cpu_raise_exception_ra(env, TT_DIV_ZERO, GETPC()); } return a / b; } @@ -192,7 +190,6 @@ uint64_t helper_udivx(CPUSPARCState *env, uint64_t a, uint64_t b) target_ulong helper_taddcctv(CPUSPARCState *env, target_ulong src1, target_ulong src2) { - SPARCCPU *cpu = sparc_env_get_cpu(env); target_ulong dst; /* Tag overflow occurs if either input has bits 0 or 1 set. */ @@ -215,14 +212,12 @@ target_ulong helper_taddcctv(CPUSPARCState *env, target_ulong src1, return dst; tag_overflow: - cpu_restore_state(CPU(cpu), GETPC()); - helper_raise_exception(env, TT_TOVF); + cpu_raise_exception_ra(env, TT_TOVF, GETPC()); } target_ulong helper_tsubcctv(CPUSPARCState *env, target_ulong src1, target_ulong src2) { - SPARCCPU *cpu = sparc_env_get_cpu(env); target_ulong dst; /* Tag overflow occurs if either input has bits 0 or 1 set. */ @@ -245,8 +240,7 @@ target_ulong helper_tsubcctv(CPUSPARCState *env, target_ulong src1, return dst; tag_overflow: - cpu_restore_state(CPU(cpu), GETPC()); - helper_raise_exception(env, TT_TOVF); + cpu_raise_exception_ra(env, TT_TOVF, GETPC()); } #ifndef TARGET_SPARC64 diff --git a/target-sparc/helper.h b/target-sparc/helper.h index caa2a895d0..0cf1bfb73a 100644 --- a/target-sparc/helper.h +++ b/target-sparc/helper.h @@ -17,8 +17,6 @@ DEF_HELPER_1(rdcwp, tl, env) DEF_HELPER_2(wrcwp, void, env, tl) DEF_HELPER_FLAGS_2(array8, TCG_CALL_NO_RWG_SE, tl, tl, tl) DEF_HELPER_FLAGS_1(popc, TCG_CALL_NO_RWG_SE, tl, tl) -DEF_HELPER_FLAGS_3(ldda_asi, TCG_CALL_NO_WG, void, env, tl, int) -DEF_HELPER_FLAGS_5(casx_asi, TCG_CALL_NO_WG, tl, env, tl, tl, tl, i32) DEF_HELPER_FLAGS_2(set_softint, TCG_CALL_NO_RWG, void, env, i64) DEF_HELPER_FLAGS_2(clear_softint, TCG_CALL_NO_RWG, void, env, i64) DEF_HELPER_FLAGS_2(write_softint, TCG_CALL_NO_RWG, void, env, i64) @@ -26,9 +24,6 @@ DEF_HELPER_FLAGS_2(tick_set_count, TCG_CALL_NO_RWG, void, ptr, i64) DEF_HELPER_FLAGS_3(tick_get_count, TCG_CALL_NO_WG, i64, env, ptr, int) DEF_HELPER_FLAGS_2(tick_set_limit, TCG_CALL_NO_RWG, void, ptr, i64) #endif -#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) -DEF_HELPER_FLAGS_5(cas_asi, TCG_CALL_NO_WG, tl, env, tl, tl, tl, i32) -#endif DEF_HELPER_FLAGS_3(check_align, TCG_CALL_NO_WG, void, env, tl, i32) DEF_HELPER_1(debug, void, env) DEF_HELPER_1(save, void, env) @@ -43,8 +38,6 @@ DEF_HELPER_3(tsubcctv, tl, env, tl, tl) DEF_HELPER_FLAGS_3(sdivx, TCG_CALL_NO_WG, s64, env, s64, s64) DEF_HELPER_FLAGS_3(udivx, TCG_CALL_NO_WG, i64, env, i64, i64) #endif -DEF_HELPER_FLAGS_3(ldqf, TCG_CALL_NO_WG, void, env, tl, int) -DEF_HELPER_FLAGS_3(stqf, TCG_CALL_NO_WG, void, env, tl, int) #if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) DEF_HELPER_FLAGS_4(ld_asi, TCG_CALL_NO_WG, i64, env, tl, int, i32) DEF_HELPER_FLAGS_5(st_asi, TCG_CALL_NO_WG, void, env, tl, i64, int, i32) diff --git a/target-sparc/ldst_helper.c b/target-sparc/ldst_helper.c index 6ce5ccc37f..de7d53ae20 100644 --- a/target-sparc/ldst_helper.c +++ b/target-sparc/ldst_helper.c @@ -254,18 +254,6 @@ static void replace_tlb_1bit_lru(SparcTLBEntry *tlb, #endif -#if defined(TARGET_SPARC64) || defined(CONFIG_USER_ONLY) -static inline target_ulong address_mask(CPUSPARCState *env1, target_ulong addr) -{ -#ifdef TARGET_SPARC64 - if (AM_CHECK(env1)) { - addr &= 0xffffffffULL; - } -#endif - return addr; -} -#endif - #ifdef TARGET_SPARC64 /* returns true if access using this ASI is to have address translated by MMU otherwise access is to raw physical address */ @@ -290,28 +278,41 @@ static inline int is_translating_asi(int asi) } } +static inline target_ulong address_mask(CPUSPARCState *env1, target_ulong addr) +{ + if (AM_CHECK(env1)) { + addr &= 0xffffffffULL; + } + return addr; +} + static inline target_ulong asi_address_mask(CPUSPARCState *env, int asi, target_ulong addr) { if (is_translating_asi(asi)) { - return address_mask(env, addr); - } else { - return addr; + addr = address_mask(env, addr); } + return addr; } #endif -void helper_check_align(CPUSPARCState *env, target_ulong addr, uint32_t align) +static void do_check_align(CPUSPARCState *env, target_ulong addr, + uint32_t align, uintptr_t ra) { if (addr & align) { #ifdef DEBUG_UNALIGNED printf("Unaligned access to 0x" TARGET_FMT_lx " from 0x" TARGET_FMT_lx "\n", addr, env->pc); #endif - helper_raise_exception(env, TT_UNALIGNED); + cpu_raise_exception_ra(env, TT_UNALIGNED, ra); } } +void helper_check_align(CPUSPARCState *env, target_ulong addr, uint32_t align) +{ + do_check_align(env, addr, align, GETPC()); +} + #if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) && \ defined(DEBUG_MXCC) static void dump_mxcc(CPUSPARCState *env) @@ -440,7 +441,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, uint32_t last_addr = addr; #endif - helper_check_align(env, addr, size - 1); + do_check_align(env, addr, size - 1, GETPC()); switch (asi) { case ASI_M_MXCC: /* SuperSparc MXCC registers, or... */ /* case ASI_LEON_CACHEREGS: Leon3 cache control */ @@ -554,64 +555,11 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, break; } break; - case ASI_USERDATA: /* User data access */ - switch (size) { - case 1: - ret = cpu_ldub_user(env, addr); - break; - case 2: - ret = cpu_lduw_user(env, addr); - break; - default: - case 4: - ret = cpu_ldl_user(env, addr); - break; - case 8: - ret = cpu_ldq_user(env, addr); - break; - } - break; - case ASI_KERNELDATA: /* Supervisor data access */ - case ASI_P: /* Implicit primary context data access (v9 only?) */ - switch (size) { - case 1: - ret = cpu_ldub_kernel(env, addr); - break; - case 2: - ret = cpu_lduw_kernel(env, addr); - break; - default: - case 4: - ret = cpu_ldl_kernel(env, addr); - break; - case 8: - ret = cpu_ldq_kernel(env, addr); - break; - } - break; case ASI_M_TXTC_TAG: /* SparcStation 5 I-cache tag */ case ASI_M_TXTC_DATA: /* SparcStation 5 I-cache data */ case ASI_M_DATAC_TAG: /* SparcStation 5 D-cache tag */ case ASI_M_DATAC_DATA: /* SparcStation 5 D-cache data */ break; - case ASI_M_BYPASS: /* MMU passthrough */ - case ASI_LEON_BYPASS: /* LEON MMU passthrough */ - switch (size) { - case 1: - ret = ldub_phys(cs->as, addr); - break; - case 2: - ret = lduw_phys(cs->as, addr); - break; - default: - case 4: - ret = ldl_phys(cs->as, addr); - break; - case 8: - ret = ldq_phys(cs->as, addr); - break; - } - break; case 0x21 ... 0x2f: /* MMU passthrough, 0x100000000 to 0xfffffffff */ switch (size) { case 1: @@ -679,6 +627,14 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, cpu_unassigned_access(cs, addr, false, false, asi, size); ret = 0; break; + + case ASI_USERDATA: /* User data access */ + case ASI_KERNELDATA: /* Supervisor data access */ + case ASI_P: /* Implicit primary context data access (v9 only?) */ + case ASI_M_BYPASS: /* MMU passthrough */ + case ASI_LEON_BYPASS: /* LEON MMU passthrough */ + /* These are always handled inline. */ + g_assert_not_reached(); } if (sign) { switch (size) { @@ -708,7 +664,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val, SPARCCPU *cpu = sparc_env_get_cpu(env); CPUState *cs = CPU(cpu); - helper_check_align(env, addr, size - 1); + do_check_align(env, addr, size - 1, GETPC()); switch (asi) { case ASI_M_MXCC: /* SuperSparc MXCC registers, or... */ /* case ASI_LEON_CACHEREGS: Leon3 cache control */ @@ -881,10 +837,10 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val, case 0: /* Control Register */ env->mmuregs[reg] = (env->mmuregs[reg] & 0xff000000) | (val & 0x00ffffff); - /* Mappings generated during no-fault mode or MMU - disabled mode are invalid in normal mode */ - if ((oldreg & (MMU_E | MMU_NF | env->def->mmu_bm)) != - (env->mmuregs[reg] & (MMU_E | MMU_NF | env->def->mmu_bm))) { + /* Mappings generated during no-fault mode + are invalid in normal mode. */ + if ((oldreg ^ env->mmuregs[reg]) + & (MMU_NF | env->def->mmu_bm)) { tlb_flush(CPU(cpu), 1); } break; @@ -929,41 +885,6 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val, case ASI_M_DIAGS: /* Turbosparc DTLB Diagnostic */ case ASI_M_IODIAG: /* Turbosparc IOTLB Diagnostic */ break; - case ASI_USERDATA: /* User data access */ - switch (size) { - case 1: - cpu_stb_user(env, addr, val); - break; - case 2: - cpu_stw_user(env, addr, val); - break; - default: - case 4: - cpu_stl_user(env, addr, val); - break; - case 8: - cpu_stq_user(env, addr, val); - break; - } - break; - case ASI_KERNELDATA: /* Supervisor data access */ - case ASI_P: - switch (size) { - case 1: - cpu_stb_kernel(env, addr, val); - break; - case 2: - cpu_stw_kernel(env, addr, val); - break; - default: - case 4: - cpu_stl_kernel(env, addr, val); - break; - case 8: - cpu_stq_kernel(env, addr, val); - break; - } - break; case ASI_M_TXTC_TAG: /* I-cache tag */ case ASI_M_TXTC_DATA: /* I-cache data */ case ASI_M_DATAC_TAG: /* D-cache tag */ @@ -974,52 +895,6 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val, case ASI_M_FLUSH_CTX: /* I/D-cache flush context */ case ASI_M_FLUSH_USER: /* I/D-cache flush user */ break; - case ASI_M_BCOPY: /* Block copy, sta access */ - { - /* val = src - addr = dst - copy 32 bytes */ - unsigned int i; - uint32_t src = val & ~3, dst = addr & ~3, temp; - - for (i = 0; i < 32; i += 4, src += 4, dst += 4) { - temp = cpu_ldl_kernel(env, src); - cpu_stl_kernel(env, dst, temp); - } - } - break; - case ASI_M_BFILL: /* Block fill, stda access */ - { - /* addr = dst - fill 32 bytes with val */ - unsigned int i; - uint32_t dst = addr & ~7; - - for (i = 0; i < 32; i += 8, dst += 8) { - cpu_stq_kernel(env, dst, val); - } - } - break; - case ASI_M_BYPASS: /* MMU passthrough */ - case ASI_LEON_BYPASS: /* LEON MMU passthrough */ - { - switch (size) { - case 1: - stb_phys(cs->as, addr, val); - break; - case 2: - stw_phys(cs->as, addr, val); - break; - case 4: - default: - stl_phys(cs->as, addr, val); - break; - case 8: - stq_phys(cs->as, addr, val); - break; - } - } - break; case 0x21 ... 0x2f: /* MMU passthrough, 0x100000000 to 0xfffffffff */ { switch (size) { @@ -1091,6 +966,16 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val, cpu_unassigned_access(CPU(sparc_env_get_cpu(env)), addr, true, false, asi, size); break; + + case ASI_USERDATA: /* User data access */ + case ASI_KERNELDATA: /* Supervisor data access */ + case ASI_P: + case ASI_M_BYPASS: /* MMU passthrough */ + case ASI_LEON_BYPASS: /* LEON MMU passthrough */ + case ASI_M_BCOPY: /* Block copy, sta access */ + case ASI_M_BFILL: /* Block fill, stda access */ + /* These are always handled inline. */ + g_assert_not_reached(); } #ifdef DEBUG_ASI dump_asi("write", addr, asi, size, val); @@ -1107,68 +992,54 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, int size = 1 << (memop & MO_SIZE); int sign = memop & MO_SIGN; uint64_t ret = 0; -#if defined(DEBUG_ASI) - target_ulong last_addr = addr; -#endif if (asi < 0x80) { - helper_raise_exception(env, TT_PRIV_ACT); + cpu_raise_exception_ra(env, TT_PRIV_ACT, GETPC()); } - - helper_check_align(env, addr, size - 1); + do_check_align(env, addr, size - 1, GETPC()); addr = asi_address_mask(env, asi, addr); switch (asi) { case ASI_PNF: /* Primary no-fault */ case ASI_PNFL: /* Primary no-fault LE */ - if (page_check_range(addr, size, PAGE_READ) == -1) { -#ifdef DEBUG_ASI - dump_asi("read ", last_addr, asi, size, ret); -#endif - return 0; - } - /* Fall through */ - case ASI_P: /* Primary */ - case ASI_PL: /* Primary LE */ - { - switch (size) { - case 1: - ret = cpu_ldub_data(env, addr); - break; - case 2: - ret = cpu_lduw_data(env, addr); - break; - case 4: - ret = cpu_ldl_data(env, addr); - break; - default: - case 8: - ret = cpu_ldq_data(env, addr); - break; - } - } - break; case ASI_SNF: /* Secondary no-fault */ case ASI_SNFL: /* Secondary no-fault LE */ if (page_check_range(addr, size, PAGE_READ) == -1) { -#ifdef DEBUG_ASI - dump_asi("read ", last_addr, asi, size, ret); -#endif - return 0; + ret = 0; + break; + } + switch (size) { + case 1: + ret = cpu_ldub_data(env, addr); + break; + case 2: + ret = cpu_lduw_data(env, addr); + break; + case 4: + ret = cpu_ldl_data(env, addr); + break; + case 8: + ret = cpu_ldq_data(env, addr); + break; + default: + g_assert_not_reached(); } - /* Fall through */ + break; + break; + + case ASI_P: /* Primary */ + case ASI_PL: /* Primary LE */ case ASI_S: /* Secondary */ case ASI_SL: /* Secondary LE */ - /* XXX */ - break; + /* These are always handled inline. */ + g_assert_not_reached(); + default: - break; + cpu_raise_exception_ra(env, TT_DATA_ACCESS, GETPC()); } /* Convert from little endian */ switch (asi) { - case ASI_PL: /* Primary LE */ - case ASI_SL: /* Secondary LE */ case ASI_PNFL: /* Primary no-fault LE */ case ASI_SNFL: /* Secondary no-fault LE */ switch (size) { @@ -1181,11 +1052,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, case 8: ret = bswap64(ret); break; - default: - break; } - default: - break; } /* Convert to signed number */ @@ -1200,12 +1067,10 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, case 4: ret = (int32_t) ret; break; - default: - break; } } #ifdef DEBUG_ASI - dump_asi("read ", last_addr, asi, size, ret); + dump_asi("read", addr, asi, size, ret); #endif return ret; } @@ -1218,66 +1083,24 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, dump_asi("write", addr, asi, size, val); #endif if (asi < 0x80) { - helper_raise_exception(env, TT_PRIV_ACT); - } - - helper_check_align(env, addr, size - 1); - addr = asi_address_mask(env, asi, addr); - - /* Convert to little endian */ - switch (asi) { - case ASI_PL: /* Primary LE */ - case ASI_SL: /* Secondary LE */ - switch (size) { - case 2: - val = bswap16(val); - break; - case 4: - val = bswap32(val); - break; - case 8: - val = bswap64(val); - break; - default: - break; - } - default: - break; + cpu_raise_exception_ra(env, TT_PRIV_ACT, GETPC()); } + do_check_align(env, addr, size - 1, GETPC()); switch (asi) { case ASI_P: /* Primary */ case ASI_PL: /* Primary LE */ - { - switch (size) { - case 1: - cpu_stb_data(env, addr, val); - break; - case 2: - cpu_stw_data(env, addr, val); - break; - case 4: - cpu_stl_data(env, addr, val); - break; - case 8: - default: - cpu_stq_data(env, addr, val); - break; - } - } - break; case ASI_S: /* Secondary */ case ASI_SL: /* Secondary LE */ - /* XXX */ - return; + /* These are always handled inline. */ + g_assert_not_reached(); case ASI_PNF: /* Primary no-fault, RO */ case ASI_SNF: /* Secondary no-fault, RO */ case ASI_PNFL: /* Primary no-fault LE, RO */ case ASI_SNFL: /* Secondary no-fault LE, RO */ default: - helper_raise_exception(env, TT_DATA_ACCESS); - return; + cpu_raise_exception_ra(env, TT_DATA_ACCESS, GETPC()); } } @@ -1300,176 +1123,94 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, || (cpu_has_hypervisor(env) && asi >= 0x30 && asi < 0x80 && !(env->hpstate & HS_PRIV))) { - helper_raise_exception(env, TT_PRIV_ACT); + cpu_raise_exception_ra(env, TT_PRIV_ACT, GETPC()); } - helper_check_align(env, addr, size - 1); + do_check_align(env, addr, size - 1, GETPC()); addr = asi_address_mask(env, asi, addr); - /* process nonfaulting loads first */ - if ((asi & 0xf6) == 0x82) { - int mmu_idx; - - /* secondary space access has lowest asi bit equal to 1 */ - if (env->pstate & PS_PRIV) { - mmu_idx = (asi & 1) ? MMU_KERNEL_SECONDARY_IDX : MMU_KERNEL_IDX; - } else { - mmu_idx = (asi & 1) ? MMU_USER_SECONDARY_IDX : MMU_USER_IDX; - } + switch (asi) { + case ASI_PNF: + case ASI_PNFL: + case ASI_SNF: + case ASI_SNFL: + { + TCGMemOpIdx oi; + int idx = (env->pstate & PS_PRIV + ? (asi & 1 ? MMU_KERNEL_SECONDARY_IDX : MMU_KERNEL_IDX) + : (asi & 1 ? MMU_USER_SECONDARY_IDX : MMU_USER_IDX)); - if (cpu_get_phys_page_nofault(env, addr, mmu_idx) == -1ULL) { + if (cpu_get_phys_page_nofault(env, addr, idx) == -1ULL) { #ifdef DEBUG_ASI - dump_asi("read ", last_addr, asi, size, ret); + dump_asi("read ", last_addr, asi, size, ret); #endif - /* env->exception_index is set in get_physical_address_data(). */ - helper_raise_exception(env, cs->exception_index); - } - - /* convert nonfaulting load ASIs to normal load ASIs */ - asi &= ~0x02; - } - - switch (asi) { - case ASI_AIUP: /* As if user primary */ - case ASI_AIUS: /* As if user secondary */ - case ASI_AIUPL: /* As if user primary LE */ - case ASI_AIUSL: /* As if user secondary LE */ - case ASI_P: /* Primary */ - case ASI_S: /* Secondary */ - case ASI_PL: /* Primary LE */ - case ASI_SL: /* Secondary LE */ - if ((asi & 0x80) && (env->pstate & PS_PRIV)) { - if (cpu_hypervisor_mode(env)) { - switch (size) { - case 1: - ret = cpu_ldub_hypv(env, addr); - break; - case 2: - ret = cpu_lduw_hypv(env, addr); - break; - case 4: - ret = cpu_ldl_hypv(env, addr); - break; - default: - case 8: - ret = cpu_ldq_hypv(env, addr); - break; - } - } else { - /* secondary space access has lowest asi bit equal to 1 */ - if (asi & 1) { - switch (size) { - case 1: - ret = cpu_ldub_kernel_secondary(env, addr); - break; - case 2: - ret = cpu_lduw_kernel_secondary(env, addr); - break; - case 4: - ret = cpu_ldl_kernel_secondary(env, addr); - break; - default: - case 8: - ret = cpu_ldq_kernel_secondary(env, addr); - break; - } - } else { - switch (size) { - case 1: - ret = cpu_ldub_kernel(env, addr); - break; - case 2: - ret = cpu_lduw_kernel(env, addr); - break; - case 4: - ret = cpu_ldl_kernel(env, addr); - break; - default: - case 8: - ret = cpu_ldq_kernel(env, addr); - break; - } - } + /* exception_index is set in get_physical_address_data. */ + cpu_raise_exception_ra(env, cs->exception_index, GETPC()); } - } else { - /* secondary space access has lowest asi bit equal to 1 */ - if (asi & 1) { - switch (size) { - case 1: - ret = cpu_ldub_user_secondary(env, addr); - break; - case 2: - ret = cpu_lduw_user_secondary(env, addr); - break; - case 4: - ret = cpu_ldl_user_secondary(env, addr); - break; - default: - case 8: - ret = cpu_ldq_user_secondary(env, addr); - break; - } - } else { - switch (size) { - case 1: - ret = cpu_ldub_user(env, addr); - break; - case 2: - ret = cpu_lduw_user(env, addr); - break; - case 4: - ret = cpu_ldl_user(env, addr); - break; - default: - case 8: - ret = cpu_ldq_user(env, addr); - break; - } - } - } - break; - case ASI_REAL: /* Bypass */ - case ASI_REAL_IO: /* Bypass, non-cacheable */ - case ASI_REAL_L: /* Bypass LE */ - case ASI_REAL_IO_L: /* Bypass, non-cacheable LE */ - { + oi = make_memop_idx(memop, idx); switch (size) { case 1: - ret = ldub_phys(cs->as, addr); + ret = helper_ret_ldub_mmu(env, addr, oi, GETPC()); break; case 2: - ret = lduw_phys(cs->as, addr); + if (asi & 8) { + ret = helper_le_lduw_mmu(env, addr, oi, GETPC()); + } else { + ret = helper_be_lduw_mmu(env, addr, oi, GETPC()); + } break; case 4: - ret = ldl_phys(cs->as, addr); + if (asi & 8) { + ret = helper_le_ldul_mmu(env, addr, oi, GETPC()); + } else { + ret = helper_be_ldul_mmu(env, addr, oi, GETPC()); + } break; - default: case 8: - ret = ldq_phys(cs->as, addr); + if (asi & 8) { + ret = helper_le_ldq_mmu(env, addr, oi, GETPC()); + } else { + ret = helper_be_ldq_mmu(env, addr, oi, GETPC()); + } break; + default: + g_assert_not_reached(); } - break; } + break; + + case ASI_AIUP: /* As if user primary */ + case ASI_AIUS: /* As if user secondary */ + case ASI_AIUPL: /* As if user primary LE */ + case ASI_AIUSL: /* As if user secondary LE */ + case ASI_P: /* Primary */ + case ASI_S: /* Secondary */ + case ASI_PL: /* Primary LE */ + case ASI_SL: /* Secondary LE */ + case ASI_REAL: /* Bypass */ + case ASI_REAL_IO: /* Bypass, non-cacheable */ + case ASI_REAL_L: /* Bypass LE */ + case ASI_REAL_IO_L: /* Bypass, non-cacheable LE */ case ASI_N: /* Nucleus */ case ASI_NL: /* Nucleus Little Endian (LE) */ - { - switch (size) { - case 1: - ret = cpu_ldub_nucleus(env, addr); - break; - case 2: - ret = cpu_lduw_nucleus(env, addr); - break; - case 4: - ret = cpu_ldl_nucleus(env, addr); - break; - default: - case 8: - ret = cpu_ldq_nucleus(env, addr); - break; - } - break; - } + case ASI_NUCLEUS_QUAD_LDD: /* Nucleus quad LDD 128 bit atomic */ + case ASI_NUCLEUS_QUAD_LDD_L: /* Nucleus quad LDD 128 bit atomic LE */ + case ASI_TWINX_AIUP: /* As if user primary, twinx */ + case ASI_TWINX_AIUS: /* As if user secondary, twinx */ + case ASI_TWINX_REAL: /* Real address, twinx */ + case ASI_TWINX_AIUP_L: /* As if user primary, twinx, LE */ + case ASI_TWINX_AIUS_L: /* As if user secondary, twinx, LE */ + case ASI_TWINX_REAL_L: /* Real address, twinx, LE */ + case ASI_TWINX_N: /* Nucleus, twinx */ + case ASI_TWINX_NL: /* Nucleus, twinx, LE */ + /* ??? From the UA2011 document; overlaps BLK_INIT_QUAD_LDD_* */ + case ASI_TWINX_P: /* Primary, twinx */ + case ASI_TWINX_PL: /* Primary, twinx, LE */ + case ASI_TWINX_S: /* Secondary, twinx */ + case ASI_TWINX_SL: /* Secondary, twinx, LE */ + /* These are always handled inline. */ + g_assert_not_reached(); + case ASI_UPA_CONFIG: /* UPA config */ /* XXX */ break; @@ -1597,51 +1338,6 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, cpu_unassigned_access(cs, addr, false, false, 1, size); ret = 0; break; - - case ASI_NUCLEUS_QUAD_LDD: /* Nucleus quad LDD 128 bit atomic */ - case ASI_NUCLEUS_QUAD_LDD_L: /* Nucleus quad LDD 128 bit atomic LE */ - case ASI_TWINX_AIUP: /* As if user primary, twinx */ - case ASI_TWINX_AIUS: /* As if user secondary, twinx */ - case ASI_TWINX_REAL: /* Real address, twinx */ - case ASI_TWINX_AIUP_L: /* As if user primary, twinx, LE */ - case ASI_TWINX_AIUS_L: /* As if user secondary, twinx, LE */ - case ASI_TWINX_REAL_L: /* Real address, twinx, LE */ - case ASI_TWINX_N: /* Nucleus, twinx */ - case ASI_TWINX_NL: /* Nucleus, twinx, LE */ - /* ??? From the UA2011 document; overlaps BLK_INIT_QUAD_LDD_* */ - case ASI_TWINX_P: /* Primary, twinx */ - case ASI_TWINX_PL: /* Primary, twinx, LE */ - case ASI_TWINX_S: /* Secondary, twinx */ - case ASI_TWINX_SL: /* Secondary, twinx, LE */ - /* These are all 128-bit atomic; only ldda (now ldtxa) allowed */ - helper_raise_exception(env, TT_ILL_INSN); - return 0; - } - - /* Convert from little endian */ - switch (asi) { - case ASI_NL: /* Nucleus Little Endian (LE) */ - case ASI_AIUPL: /* As if user primary LE */ - case ASI_AIUSL: /* As if user secondary LE */ - case ASI_REAL_L: /* Bypass LE */ - case ASI_REAL_IO_L: /* Bypass, non-cacheable LE */ - case ASI_PL: /* Primary LE */ - case ASI_SL: /* Secondary LE */ - switch(size) { - case 2: - ret = bswap16(ret); - break; - case 4: - ret = bswap32(ret); - break; - case 8: - ret = bswap64(ret); - break; - default: - break; - } - default: - break; } /* Convert to signed number */ @@ -1683,38 +1379,12 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, || (cpu_has_hypervisor(env) && asi >= 0x30 && asi < 0x80 && !(env->hpstate & HS_PRIV))) { - helper_raise_exception(env, TT_PRIV_ACT); + cpu_raise_exception_ra(env, TT_PRIV_ACT, GETPC()); } - helper_check_align(env, addr, size - 1); + do_check_align(env, addr, size - 1, GETPC()); addr = asi_address_mask(env, asi, addr); - /* Convert to little endian */ - switch (asi) { - case ASI_NL: /* Nucleus Little Endian (LE) */ - case ASI_AIUPL: /* As if user primary LE */ - case ASI_AIUSL: /* As if user secondary LE */ - case ASI_REAL_L: /* Bypass LE */ - case ASI_REAL_IO_L: /* Bypass, non-cacheable LE */ - case ASI_PL: /* Primary LE */ - case ASI_SL: /* Secondary LE */ - switch (size) { - case 2: - val = bswap16(val); - break; - case 4: - val = bswap32(val); - break; - case 8: - val = bswap64(val); - break; - default: - break; - } - default: - break; - } - switch (asi) { case ASI_AIUP: /* As if user primary */ case ASI_AIUS: /* As if user secondary */ @@ -1724,160 +1394,36 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, case ASI_S: /* Secondary */ case ASI_PL: /* Primary LE */ case ASI_SL: /* Secondary LE */ - if ((asi & 0x80) && (env->pstate & PS_PRIV)) { - if (cpu_hypervisor_mode(env)) { - switch (size) { - case 1: - cpu_stb_hypv(env, addr, val); - break; - case 2: - cpu_stw_hypv(env, addr, val); - break; - case 4: - cpu_stl_hypv(env, addr, val); - break; - case 8: - default: - cpu_stq_hypv(env, addr, val); - break; - } - } else { - /* secondary space access has lowest asi bit equal to 1 */ - if (asi & 1) { - switch (size) { - case 1: - cpu_stb_kernel_secondary(env, addr, val); - break; - case 2: - cpu_stw_kernel_secondary(env, addr, val); - break; - case 4: - cpu_stl_kernel_secondary(env, addr, val); - break; - case 8: - default: - cpu_stq_kernel_secondary(env, addr, val); - break; - } - } else { - switch (size) { - case 1: - cpu_stb_kernel(env, addr, val); - break; - case 2: - cpu_stw_kernel(env, addr, val); - break; - case 4: - cpu_stl_kernel(env, addr, val); - break; - case 8: - default: - cpu_stq_kernel(env, addr, val); - break; - } - } - } - } else { - /* secondary space access has lowest asi bit equal to 1 */ - if (asi & 1) { - switch (size) { - case 1: - cpu_stb_user_secondary(env, addr, val); - break; - case 2: - cpu_stw_user_secondary(env, addr, val); - break; - case 4: - cpu_stl_user_secondary(env, addr, val); - break; - case 8: - default: - cpu_stq_user_secondary(env, addr, val); - break; - } - } else { - switch (size) { - case 1: - cpu_stb_user(env, addr, val); - break; - case 2: - cpu_stw_user(env, addr, val); - break; - case 4: - cpu_stl_user(env, addr, val); - break; - case 8: - default: - cpu_stq_user(env, addr, val); - break; - } - } - } - break; case ASI_REAL: /* Bypass */ case ASI_REAL_IO: /* Bypass, non-cacheable */ case ASI_REAL_L: /* Bypass LE */ case ASI_REAL_IO_L: /* Bypass, non-cacheable LE */ - { - switch (size) { - case 1: - stb_phys(cs->as, addr, val); - break; - case 2: - stw_phys(cs->as, addr, val); - break; - case 4: - stl_phys(cs->as, addr, val); - break; - case 8: - default: - stq_phys(cs->as, addr, val); - break; - } - } - return; case ASI_N: /* Nucleus */ case ASI_NL: /* Nucleus Little Endian (LE) */ - { - switch (size) { - case 1: - cpu_stb_nucleus(env, addr, val); - break; - case 2: - cpu_stw_nucleus(env, addr, val); - break; - case 4: - cpu_stl_nucleus(env, addr, val); - break; - default: - case 8: - cpu_stq_nucleus(env, addr, val); - break; - } - break; - } + case ASI_NUCLEUS_QUAD_LDD: /* Nucleus quad LDD 128 bit atomic */ + case ASI_NUCLEUS_QUAD_LDD_L: /* Nucleus quad LDD 128 bit atomic LE */ + case ASI_TWINX_AIUP: /* As if user primary, twinx */ + case ASI_TWINX_AIUS: /* As if user secondary, twinx */ + case ASI_TWINX_REAL: /* Real address, twinx */ + case ASI_TWINX_AIUP_L: /* As if user primary, twinx, LE */ + case ASI_TWINX_AIUS_L: /* As if user secondary, twinx, LE */ + case ASI_TWINX_REAL_L: /* Real address, twinx, LE */ + case ASI_TWINX_N: /* Nucleus, twinx */ + case ASI_TWINX_NL: /* Nucleus, twinx, LE */ + /* ??? From the UA2011 document; overlaps BLK_INIT_QUAD_LDD_* */ + case ASI_TWINX_P: /* Primary, twinx */ + case ASI_TWINX_PL: /* Primary, twinx, LE */ + case ASI_TWINX_S: /* Secondary, twinx */ + case ASI_TWINX_SL: /* Secondary, twinx, LE */ + /* These are always handled inline. */ + g_assert_not_reached(); case ASI_UPA_CONFIG: /* UPA config */ /* XXX */ return; case ASI_LSU_CONTROL: /* LSU */ - { - uint64_t oldreg; - - oldreg = env->lsu; - env->lsu = val & (DMMU_E | IMMU_E); - /* Mappings generated during D/I MMU disabled mode are - invalid in normal mode */ - if (oldreg != env->lsu) { - DPRINTF_MMU("LSU change: 0x%" PRIx64 " -> 0x%" PRIx64 "\n", - oldreg, env->lsu); -#ifdef DEBUG_MMU - dump_mmu(stdout, fprintf, env); -#endif - tlb_flush(CPU(cpu), 1); - } - return; - } + env->lsu = val & (DMMU_E | IMMU_E); + return; case ASI_IMMU: /* I-MMU regs */ { int reg = (addr >> 3) & 0xf; @@ -2016,24 +1562,6 @@ 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_NUCLEUS_QUAD_LDD: /* Nucleus quad LDD 128 bit atomic */ - case ASI_NUCLEUS_QUAD_LDD_L: /* Nucleus quad LDD 128 bit atomic LE */ - case ASI_TWINX_AIUP: /* As if user primary, twinx */ - case ASI_TWINX_AIUS: /* As if user secondary, twinx */ - case ASI_TWINX_REAL: /* Real address, twinx */ - case ASI_TWINX_AIUP_L: /* As if user primary, twinx, LE */ - case ASI_TWINX_AIUS_L: /* As if user secondary, twinx, LE */ - case ASI_TWINX_REAL_L: /* Real address, twinx, LE */ - case ASI_TWINX_N: /* Nucleus, twinx */ - case ASI_TWINX_NL: /* Nucleus, twinx, LE */ - /* ??? From the UA2011 document; overlaps BLK_INIT_QUAD_LDD_* */ - case ASI_TWINX_P: /* Primary, twinx */ - case ASI_TWINX_PL: /* Primary, twinx, LE */ - case ASI_TWINX_S: /* Secondary, twinx */ - case ASI_TWINX_SL: /* Secondary, twinx, LE */ - /* Only stda allowed */ - helper_raise_exception(env, TT_ILL_INSN); - return; case ASI_DCACHE_DATA: /* D-cache data */ case ASI_DCACHE_TAG: /* D-cache tag access */ case ASI_ESTATE_ERROR_EN: /* E-cache error enable */ @@ -2066,203 +1594,8 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, } } #endif /* CONFIG_USER_ONLY */ - -/* 128-bit LDDA; result returned in QT0. */ -void helper_ldda_asi(CPUSPARCState *env, target_ulong addr, int asi) -{ - uint64_t h, l; - - if ((asi < 0x80 && (env->pstate & PS_PRIV) == 0) - || (cpu_has_hypervisor(env) - && asi >= 0x30 && asi < 0x80 - && !(env->hpstate & HS_PRIV))) { - helper_raise_exception(env, TT_PRIV_ACT); - } - - addr = asi_address_mask(env, asi, addr); - - switch (asi) { -#if !defined(CONFIG_USER_ONLY) - case ASI_TWINX_AIUP: /* As if user primary, twinx */ - case ASI_TWINX_AIUP_L: /* As if user primary, twinx, LE */ - helper_check_align(env, addr, 0xf); - h = cpu_ldq_user(env, addr); - l = cpu_ldq_user(env, addr + 8); - break; - case ASI_TWINX_AIUS: /* As if user secondary, twinx */ - case ASI_TWINX_AIUS_L: /* As if user secondary, twinx, LE */ - helper_check_align(env, addr, 0xf); - h = cpu_ldq_user_secondary(env, addr); - l = cpu_ldq_user_secondary(env, addr + 8); - break; - case ASI_TWINX_REAL: /* Real address, twinx */ - case ASI_TWINX_REAL_L: /* Real address, twinx, LE */ - helper_check_align(env, addr, 0xf); - { - CPUState *cs = CPU(sparc_env_get_cpu(env)); - h = ldq_phys(cs->as, addr); - l = ldq_phys(cs->as, addr + 8); - } - break; - case ASI_NUCLEUS_QUAD_LDD: - case ASI_NUCLEUS_QUAD_LDD_L: - case ASI_TWINX_N: /* Nucleus, twinx */ - case ASI_TWINX_NL: /* Nucleus, twinx, LE */ - helper_check_align(env, addr, 0xf); - h = cpu_ldq_nucleus(env, addr); - l = cpu_ldq_nucleus(env, addr + 8); - break; - case ASI_TWINX_S: /* Secondary, twinx */ - case ASI_TWINX_SL: /* Secondary, twinx, LE */ - if (!cpu_hypervisor_mode(env)) { - helper_check_align(env, addr, 0xf); - if (env->pstate & PS_PRIV) { - h = cpu_ldq_kernel_secondary(env, addr); - l = cpu_ldq_kernel_secondary(env, addr + 8); - } else { - h = cpu_ldq_user_secondary(env, addr); - l = cpu_ldq_user_secondary(env, addr + 8); - } - break; - } - /* fallthru */ - case ASI_TWINX_P: /* Primary, twinx */ - case ASI_TWINX_PL: /* Primary, twinx, LE */ - helper_check_align(env, addr, 0xf); - h = cpu_ldq_data(env, addr); - l = cpu_ldq_data(env, addr + 8); - break; -#else - case ASI_TWINX_P: /* Primary, twinx */ - case ASI_TWINX_PL: /* Primary, twinx, LE */ - case ASI_TWINX_S: /* Primary, twinx */ - case ASI_TWINX_SL: /* Primary, twinx, LE */ - /* ??? Should be available, but we need to implement - an atomic 128-bit load. */ - helper_raise_exception(env, TT_PRIV_ACT); -#endif - default: - /* Non-twinx asi, so this is the legacy ldda insn, which - performs two word sized operations. */ - /* ??? The UA2011 manual recommends emulating this with - a single 64-bit load. However, LE asis *are* treated - as two 32-bit loads individually byte swapped. */ - helper_check_align(env, addr, 0x7); - QT0.high = (uint32_t)helper_ld_asi(env, addr, asi, MO_UL); - QT0.low = (uint32_t)helper_ld_asi(env, addr + 4, asi, MO_UL); - return; - } - - if (asi & 8) { - h = bswap64(h); - l = bswap64(l); - } - QT0.high = h; - QT0.low = l; -} - -target_ulong helper_casx_asi(CPUSPARCState *env, target_ulong addr, - target_ulong val1, target_ulong val2, - uint32_t asi) -{ - target_ulong ret; - - ret = helper_ld_asi(env, addr, asi, MO_Q); - if (val2 == ret) { - helper_st_asi(env, addr, val1, asi, MO_Q); - } - return ret; -} #endif /* TARGET_SPARC64 */ -#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) -target_ulong helper_cas_asi(CPUSPARCState *env, target_ulong addr, - target_ulong val1, target_ulong val2, uint32_t asi) -{ - target_ulong ret; - - val2 &= 0xffffffffUL; - ret = helper_ld_asi(env, addr, asi, MO_UL); - ret &= 0xffffffffUL; - if (val2 == ret) { - helper_st_asi(env, addr, val1 & 0xffffffffUL, asi, MO_UL); - } - return ret; -} -#endif /* !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) */ - -void helper_ldqf(CPUSPARCState *env, target_ulong addr, int mem_idx) -{ - /* XXX add 128 bit load */ - CPU_QuadU u; - - helper_check_align(env, addr, 7); -#if !defined(CONFIG_USER_ONLY) - switch (mem_idx) { - case MMU_USER_IDX: - u.ll.upper = cpu_ldq_user(env, addr); - u.ll.lower = cpu_ldq_user(env, addr + 8); - QT0 = u.q; - break; - case MMU_KERNEL_IDX: - u.ll.upper = cpu_ldq_kernel(env, addr); - u.ll.lower = cpu_ldq_kernel(env, addr + 8); - QT0 = u.q; - break; -#ifdef TARGET_SPARC64 - case MMU_HYPV_IDX: - u.ll.upper = cpu_ldq_hypv(env, addr); - u.ll.lower = cpu_ldq_hypv(env, addr + 8); - QT0 = u.q; - break; -#endif - default: - DPRINTF_MMU("helper_ldqf: need to check MMU idx %d\n", mem_idx); - break; - } -#else - u.ll.upper = cpu_ldq_data(env, address_mask(env, addr)); - u.ll.lower = cpu_ldq_data(env, address_mask(env, addr + 8)); - QT0 = u.q; -#endif -} - -void helper_stqf(CPUSPARCState *env, target_ulong addr, int mem_idx) -{ - /* XXX add 128 bit store */ - CPU_QuadU u; - - helper_check_align(env, addr, 7); -#if !defined(CONFIG_USER_ONLY) - switch (mem_idx) { - case MMU_USER_IDX: - u.q = QT0; - cpu_stq_user(env, addr, u.ll.upper); - cpu_stq_user(env, addr + 8, u.ll.lower); - break; - case MMU_KERNEL_IDX: - u.q = QT0; - cpu_stq_kernel(env, addr, u.ll.upper); - cpu_stq_kernel(env, addr + 8, u.ll.lower); - break; -#ifdef TARGET_SPARC64 - case MMU_HYPV_IDX: - u.q = QT0; - cpu_stq_hypv(env, addr, u.ll.upper); - cpu_stq_hypv(env, addr + 8, u.ll.lower); - break; -#endif - default: - DPRINTF_MMU("helper_stqf: need to check MMU idx %d\n", mem_idx); - break; - } -#else - u.q = QT0; - cpu_stq_data(env, address_mask(env, addr), u.ll.upper); - cpu_stq_data(env, address_mask(env, addr + 8), u.ll.lower); -#endif -} - #if !defined(CONFIG_USER_ONLY) #ifndef TARGET_SPARC64 void sparc_cpu_unassigned_access(CPUState *cs, hwaddr addr, @@ -2314,11 +1647,8 @@ void sparc_cpu_unassigned_access(CPUState *cs, hwaddr addr, } if ((env->mmuregs[0] & MMU_E) && !(env->mmuregs[0] & MMU_NF)) { - if (is_exec) { - helper_raise_exception(env, TT_CODE_ACCESS); - } else { - helper_raise_exception(env, TT_DATA_ACCESS); - } + int tt = is_exec ? TT_CODE_ACCESS : TT_DATA_ACCESS; + cpu_raise_exception_ra(env, tt, GETPC()); } /* flush neverland mappings created during no-fault mode, @@ -2334,17 +1664,14 @@ 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 - if (is_exec) { - helper_raise_exception(env, TT_CODE_ACCESS); - } else { - helper_raise_exception(env, TT_DATA_ACCESS); - } + cpu_raise_exception_ra(env, tt, GETPC()); } #endif #endif @@ -2362,10 +1689,7 @@ void QEMU_NORETURN sparc_cpu_do_unaligned_access(CPUState *cs, vaddr addr, printf("Unaligned access to 0x" TARGET_FMT_lx " from 0x" TARGET_FMT_lx "\n", addr, env->pc); #endif - if (retaddr) { - cpu_restore_state(CPU(cpu), retaddr); - } - helper_raise_exception(env, TT_UNALIGNED); + cpu_raise_exception_ra(env, TT_UNALIGNED, retaddr); } /* try to fill the TLB and return an exception if error. If retaddr is @@ -2379,10 +1703,7 @@ void tlb_fill(CPUState *cs, target_ulong addr, MMUAccessType access_type, ret = sparc_cpu_handle_mmu_fault(cs, addr, access_type, mmu_idx); if (ret) { - if (retaddr) { - cpu_restore_state(cs, retaddr); - } - cpu_loop_exit(cs); + cpu_loop_exit_restore(cs, retaddr); } } #endif diff --git a/target-sparc/mmu_helper.c b/target-sparc/mmu_helper.c index 32b629fb0d..044e88c4c5 100644 --- a/target-sparc/mmu_helper.c +++ b/target-sparc/mmu_helper.c @@ -92,7 +92,7 @@ static int get_physical_address(CPUSPARCState *env, hwaddr *physical, is_user = mmu_idx == MMU_USER_IDX; - if ((env->mmuregs[0] & MMU_E) == 0) { /* MMU disabled */ + if (mmu_idx == MMU_PHYS_IDX) { *page_size = TARGET_PAGE_SIZE; /* Boot mode: instruction fetches are taken from PROM */ if (rw == 2 && (env->mmuregs[0] & env->def->mmu_bm)) { @@ -494,23 +494,21 @@ static int get_physical_address_data(CPUSPARCState *env, unsigned int i; uint64_t context; uint64_t sfsr = 0; - - int is_user = (mmu_idx == MMU_USER_IDX || - mmu_idx == MMU_USER_SECONDARY_IDX); - - if ((env->lsu & DMMU_E) == 0) { /* DMMU disabled */ - *physical = ultrasparc_truncate_physical(address); - *prot = PAGE_READ | PAGE_WRITE; - return 0; - } + bool is_user = false; switch (mmu_idx) { + case MMU_PHYS_IDX: + g_assert_not_reached(); case MMU_USER_IDX: + is_user = true; + /* fallthru */ case MMU_KERNEL_IDX: context = env->dmmu.mmu_primary_context & 0x1fff; sfsr |= SFSR_CT_PRIMARY; break; case MMU_USER_SECONDARY_IDX: + is_user = true; + /* fallthru */ case MMU_KERNEL_SECONDARY_IDX: context = env->dmmu.mmu_secondary_context & 0x1fff; sfsr |= SFSR_CT_SECONDARY; @@ -613,15 +611,22 @@ static int get_physical_address_code(CPUSPARCState *env, CPUState *cs = CPU(sparc_env_get_cpu(env)); unsigned int i; uint64_t context; + bool is_user = false; - int is_user = (mmu_idx == MMU_USER_IDX || - mmu_idx == MMU_USER_SECONDARY_IDX); - - if ((env->lsu & IMMU_E) == 0 || (env->pstate & PS_RED) != 0) { - /* IMMU disabled */ - *physical = ultrasparc_truncate_physical(address); - *prot = PAGE_EXEC; - return 0; + switch (mmu_idx) { + case MMU_PHYS_IDX: + case MMU_USER_SECONDARY_IDX: + case MMU_KERNEL_SECONDARY_IDX: + g_assert_not_reached(); + case MMU_USER_IDX: + is_user = true; + /* fallthru */ + case MMU_KERNEL_IDX: + context = env->dmmu.mmu_primary_context & 0x1fff; + break; + default: + context = 0; + break; } if (env->tl == 0) { @@ -700,6 +705,12 @@ static int get_physical_address(CPUSPARCState *env, hwaddr *physical, } } + if (mmu_idx == MMU_PHYS_IDX) { + *physical = ultrasparc_truncate_physical(address); + *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return 0; + } + if (rw == 2) { return get_physical_address_code(env, physical, prot, address, mmu_idx); diff --git a/target-sparc/translate.c b/target-sparc/translate.c index e7691e4458..a13b76ebd9 100644 --- a/target-sparc/translate.c +++ b/target-sparc/translate.c @@ -242,7 +242,29 @@ static void gen_op_store_QT0_fpr(unsigned int dst) offsetof(CPU_QuadU, ll.lower)); } +static void gen_store_fpr_Q(DisasContext *dc, unsigned int dst, + TCGv_i64 v1, TCGv_i64 v2) +{ + dst = QFPREG(dst); + + tcg_gen_mov_i64(cpu_fpr[dst / 2], v1); + tcg_gen_mov_i64(cpu_fpr[dst / 2 + 1], v2); + gen_update_fprs_dirty(dc, dst); +} + #ifdef TARGET_SPARC64 +static TCGv_i64 gen_load_fpr_Q0(DisasContext *dc, unsigned int src) +{ + src = QFPREG(src); + return cpu_fpr[src / 2]; +} + +static TCGv_i64 gen_load_fpr_Q1(DisasContext *dc, unsigned int src) +{ + src = QFPREG(src); + return cpu_fpr[src / 2 + 1]; +} + static void gen_move_Q(DisasContext *dc, unsigned int rd, unsigned int rs) { rd = QFPREG(rd); @@ -2001,6 +2023,21 @@ static inline void gen_ne_fop_QD(DisasContext *dc, int rd, int rs, gen_update_fprs_dirty(dc, QFPREG(rd)); } +static void gen_swap(DisasContext *dc, TCGv dst, TCGv src, + TCGv addr, int mmu_idx, TCGMemOp memop) +{ + gen_address_mask(dc, addr); + tcg_gen_atomic_xchg_tl(dst, addr, src, mmu_idx, memop); +} + +static void gen_ldstub(DisasContext *dc, TCGv dst, TCGv addr, int mmu_idx) +{ + TCGv m1 = tcg_const_tl(0xff); + gen_address_mask(dc, addr); + tcg_gen_atomic_xchg_tl(dst, addr, m1, mmu_idx, MO_UB); + tcg_temp_free(m1); +} + /* asi moves */ #if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) typedef enum { @@ -2010,6 +2047,8 @@ typedef enum { GET_ASI_DTWINX, GET_ASI_BLOCK, GET_ASI_SHORT, + GET_ASI_BCOPY, + GET_ASI_BFILL, } ASIType; typedef struct { @@ -2046,6 +2085,19 @@ static DisasASI get_asi(DisasContext *dc, int insn, TCGMemOp memop) mem_idx = MMU_KERNEL_IDX; type = GET_ASI_DIRECT; break; + case ASI_M_BYPASS: /* MMU passthrough */ + case ASI_LEON_BYPASS: /* LEON MMU passthrough */ + mem_idx = MMU_PHYS_IDX; + type = GET_ASI_DIRECT; + break; + case ASI_M_BCOPY: /* Block copy, sta access */ + mem_idx = MMU_KERNEL_IDX; + type = GET_ASI_BCOPY; + break; + case ASI_M_BFILL: /* Block fill, stda access */ + mem_idx = MMU_KERNEL_IDX; + type = GET_ASI_BFILL; + break; } } else { gen_exception(dc, TT_PRIV_INSN); @@ -2066,10 +2118,22 @@ static DisasASI get_asi(DisasContext *dc, int insn, TCGMemOp memop) type = GET_ASI_EXCP; } else { switch (asi) { + case ASI_REAL: /* Bypass */ + case ASI_REAL_IO: /* Bypass, non-cacheable */ + case ASI_REAL_L: /* Bypass LE */ + case ASI_REAL_IO_L: /* Bypass, non-cacheable LE */ + case ASI_TWINX_REAL: /* Real address, twinx */ + case ASI_TWINX_REAL_L: /* Real address, twinx, LE */ + case ASI_QUAD_LDD_PHYS: + case ASI_QUAD_LDD_PHYS_L: + mem_idx = MMU_PHYS_IDX; + break; case ASI_N: /* Nucleus */ case ASI_NL: /* Nucleus LE */ case ASI_TWINX_N: case ASI_TWINX_NL: + case ASI_NUCLEUS_QUAD_LDD: + case ASI_NUCLEUS_QUAD_LDD_L: mem_idx = MMU_NUCLEUS_IDX; break; case ASI_AIUP: /* As if user primary */ @@ -2123,6 +2187,10 @@ static DisasASI get_asi(DisasContext *dc, int insn, TCGMemOp memop) break; } switch (asi) { + case ASI_REAL: + case ASI_REAL_IO: + case ASI_REAL_L: + case ASI_REAL_IO_L: case ASI_N: case ASI_NL: case ASI_AIUP: @@ -2135,6 +2203,8 @@ static DisasASI get_asi(DisasContext *dc, int insn, TCGMemOp memop) case ASI_PL: type = GET_ASI_DIRECT; break; + case ASI_TWINX_REAL: + case ASI_TWINX_REAL_L: case ASI_TWINX_N: case ASI_TWINX_NL: case ASI_TWINX_AIUP: @@ -2145,6 +2215,10 @@ static DisasASI get_asi(DisasContext *dc, int insn, TCGMemOp memop) case ASI_TWINX_PL: case ASI_TWINX_S: case ASI_TWINX_SL: + case ASI_QUAD_LDD_PHYS: + case ASI_QUAD_LDD_PHYS_L: + case ASI_NUCLEUS_QUAD_LDD: + case ASI_NUCLEUS_QUAD_LDD_L: type = GET_ASI_DTWINX; break; case ASI_BLK_COMMIT_P: @@ -2241,6 +2315,38 @@ static void gen_st_asi(DisasContext *dc, TCGv src, TCGv addr, gen_address_mask(dc, addr); tcg_gen_qemu_st_tl(src, addr, da.mem_idx, da.memop); break; +#if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) + case GET_ASI_BCOPY: + /* Copy 32 bytes from the address in SRC to ADDR. */ + /* ??? The original qemu code suggests 4-byte alignment, dropping + the low bits, but the only place I can see this used is in the + Linux kernel with 32 byte alignment, which would make more sense + as a cacheline-style operation. */ + { + TCGv saddr = tcg_temp_new(); + TCGv daddr = tcg_temp_new(); + TCGv four = tcg_const_tl(4); + TCGv_i32 tmp = tcg_temp_new_i32(); + int i; + + tcg_gen_andi_tl(saddr, src, -4); + tcg_gen_andi_tl(daddr, addr, -4); + for (i = 0; i < 32; i += 4) { + /* Since the loads and stores are paired, allow the + copy to happen in the host endianness. */ + tcg_gen_qemu_ld_i32(tmp, saddr, da.mem_idx, MO_UL); + tcg_gen_qemu_st_i32(tmp, daddr, da.mem_idx, MO_UL); + tcg_gen_add_tl(saddr, saddr, four); + tcg_gen_add_tl(daddr, daddr, four); + } + + tcg_temp_free(saddr); + tcg_temp_free(daddr); + tcg_temp_free(four); + tcg_temp_free_i32(tmp); + } + break; +#endif default: { TCGv_i32 r_asi = tcg_const_i32(da.asi); @@ -2275,48 +2381,37 @@ static void gen_swap_asi(DisasContext *dc, TCGv dst, TCGv src, switch (da.type) { case GET_ASI_EXCP: break; + case GET_ASI_DIRECT: + gen_swap(dc, dst, src, addr, da.mem_idx, da.memop); + break; default: - { - TCGv_i32 r_asi = tcg_const_i32(da.asi); - TCGv_i32 r_mop = tcg_const_i32(MO_UL); - TCGv_i64 s64, t64; - - save_state(dc); - t64 = tcg_temp_new_i64(); - gen_helper_ld_asi(t64, cpu_env, addr, r_asi, r_mop); - - s64 = tcg_temp_new_i64(); - tcg_gen_extu_tl_i64(s64, src); - gen_helper_st_asi(cpu_env, addr, s64, r_asi, r_mop); - tcg_temp_free_i64(s64); - tcg_temp_free_i32(r_mop); - tcg_temp_free_i32(r_asi); - - tcg_gen_trunc_i64_tl(dst, t64); - tcg_temp_free_i64(t64); - } + /* ??? Should be DAE_invalid_asi. */ + gen_exception(dc, TT_DATA_ACCESS); break; } } -static void gen_cas_asi(DisasContext *dc, TCGv addr, TCGv val2, +static void gen_cas_asi(DisasContext *dc, TCGv addr, TCGv cmpv, int insn, int rd) { DisasASI da = get_asi(dc, insn, MO_TEUL); - TCGv val1, dst; - TCGv_i32 r_asi; + TCGv oldv; - if (da.type == GET_ASI_EXCP) { + switch (da.type) { + case GET_ASI_EXCP: return; + case GET_ASI_DIRECT: + oldv = tcg_temp_new(); + tcg_gen_atomic_cmpxchg_tl(oldv, addr, cmpv, gen_load_gpr(dc, rd), + da.mem_idx, da.memop); + gen_store_gpr(dc, rd, oldv); + tcg_temp_free(oldv); + break; + default: + /* ??? Should be DAE_invalid_asi. */ + gen_exception(dc, TT_DATA_ACCESS); + break; } - - save_state(dc); - val1 = gen_load_gpr(dc, rd); - dst = gen_dest_gpr(dc, rd); - r_asi = tcg_const_i32(da.asi); - gen_helper_cas_asi(dst, cpu_env, addr, val1, val2, r_asi); - tcg_temp_free_i32(r_asi); - gen_store_gpr(dc, rd, dst); } static void gen_ldstub_asi(DisasContext *dc, TCGv dst, TCGv addr, int insn) @@ -2326,25 +2421,12 @@ static void gen_ldstub_asi(DisasContext *dc, TCGv dst, TCGv addr, int insn) switch (da.type) { case GET_ASI_EXCP: break; + case GET_ASI_DIRECT: + gen_ldstub(dc, dst, addr, da.mem_idx); + break; default: - { - TCGv_i32 r_asi = tcg_const_i32(da.asi); - TCGv_i32 r_mop = tcg_const_i32(MO_UB); - TCGv_i64 s64, t64; - - save_state(dc); - t64 = tcg_temp_new_i64(); - gen_helper_ld_asi(t64, cpu_env, addr, r_asi, r_mop); - - s64 = tcg_const_i64(0xff); - gen_helper_st_asi(cpu_env, addr, s64, r_asi, r_mop); - tcg_temp_free_i64(s64); - tcg_temp_free_i32(r_mop); - tcg_temp_free_i32(r_asi); - - tcg_gen_trunc_i64_tl(dst, t64); - tcg_temp_free_i64(t64); - } + /* ??? Should be DAE_invalid_asi. */ + gen_exception(dc, TT_DATA_ACCESS); break; } } @@ -2356,6 +2438,7 @@ static void gen_ldf_asi(DisasContext *dc, TCGv addr, { DisasASI da = get_asi(dc, insn, (size == 4 ? MO_TEUL : MO_TEQ)); TCGv_i32 d32; + TCGv_i64 d64; switch (da.type) { case GET_ASI_EXCP: @@ -2370,12 +2453,17 @@ static void gen_ldf_asi(DisasContext *dc, TCGv addr, gen_store_fpr_F(dc, rd, d32); break; case 8: - tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2], addr, da.mem_idx, da.memop); + tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2], addr, da.mem_idx, + da.memop | MO_ALIGN_4); break; case 16: - tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2], addr, da.mem_idx, da.memop); + d64 = tcg_temp_new_i64(); + tcg_gen_qemu_ld_i64(d64, addr, da.mem_idx, da.memop | MO_ALIGN_4); tcg_gen_addi_tl(addr, addr, 8); - tcg_gen_qemu_ld_i64(cpu_fpr[rd/2+1], addr, da.mem_idx, da.memop); + tcg_gen_qemu_ld_i64(cpu_fpr[rd/2+1], addr, da.mem_idx, + da.memop | MO_ALIGN_4); + tcg_gen_mov_i64(cpu_fpr[rd / 2], d64); + tcg_temp_free_i64(d64); break; default: g_assert_not_reached(); @@ -2385,20 +2473,23 @@ static void gen_ldf_asi(DisasContext *dc, TCGv addr, case GET_ASI_BLOCK: /* Valid for lddfa on aligned registers only. */ if (size == 8 && (rd & 7) == 0) { + TCGMemOp memop; TCGv eight; int i; - gen_check_align(addr, 0x3f); gen_address_mask(dc, addr); + /* The first operation checks required alignment. */ + memop = da.memop | MO_ALIGN_64; eight = tcg_const_tl(8); for (i = 0; ; ++i) { tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2 + i], addr, - da.mem_idx, da.memop); + da.mem_idx, memop); if (i == 7) { break; } tcg_gen_add_tl(addr, addr, eight); + memop = da.memop; } tcg_temp_free(eight); } else { @@ -2428,22 +2519,23 @@ static void gen_ldf_asi(DisasContext *dc, TCGv addr, but we can just use the integer asi helper for them. */ switch (size) { case 4: - { - TCGv d64 = tcg_temp_new_i64(); - gen_helper_ld_asi(d64, cpu_env, addr, r_asi, r_mop); - d32 = gen_dest_fpr_F(dc); - tcg_gen_extrl_i64_i32(d32, d64); - tcg_temp_free_i64(d64); - gen_store_fpr_F(dc, rd, d32); - } + d64 = tcg_temp_new_i64(); + gen_helper_ld_asi(d64, cpu_env, addr, r_asi, r_mop); + d32 = gen_dest_fpr_F(dc); + tcg_gen_extrl_i64_i32(d32, d64); + tcg_temp_free_i64(d64); + gen_store_fpr_F(dc, rd, d32); break; case 8: gen_helper_ld_asi(cpu_fpr[rd / 2], cpu_env, addr, r_asi, r_mop); break; case 16: - gen_helper_ld_asi(cpu_fpr[rd / 2], cpu_env, addr, r_asi, r_mop); + d64 = tcg_temp_new_i64(); + gen_helper_ld_asi(d64, cpu_env, addr, r_asi, r_mop); tcg_gen_addi_tl(addr, addr, 8); gen_helper_ld_asi(cpu_fpr[rd/2+1], cpu_env, addr, r_asi, r_mop); + tcg_gen_mov_i64(cpu_fpr[rd / 2], d64); + tcg_temp_free_i64(d64); break; default: g_assert_not_reached(); @@ -2473,10 +2565,17 @@ static void gen_stf_asi(DisasContext *dc, TCGv addr, tcg_gen_qemu_st_i32(d32, addr, da.mem_idx, da.memop); break; case 8: - tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da.mem_idx, da.memop); + tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da.mem_idx, + da.memop | MO_ALIGN_4); break; case 16: - tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da.mem_idx, da.memop); + /* Only 4-byte alignment required. However, it is legal for the + cpu to signal the alignment fault, and the OS trap handler is + required to fix it up. Requiring 16-byte alignment here avoids + having to probe the second page before performing the first + write. */ + tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da.mem_idx, + da.memop | MO_ALIGN_16); tcg_gen_addi_tl(addr, addr, 8); tcg_gen_qemu_st_i64(cpu_fpr[rd/2+1], addr, da.mem_idx, da.memop); break; @@ -2488,20 +2587,23 @@ static void gen_stf_asi(DisasContext *dc, TCGv addr, case GET_ASI_BLOCK: /* Valid for stdfa on aligned registers only. */ if (size == 8 && (rd & 7) == 0) { + TCGMemOp memop; TCGv eight; int i; - gen_check_align(addr, 0x3f); gen_address_mask(dc, addr); + /* The first operation checks required alignment. */ + memop = da.memop | MO_ALIGN_64; eight = tcg_const_tl(8); for (i = 0; ; ++i) { tcg_gen_qemu_st_i64(cpu_fpr[rd / 2 + i], addr, - da.mem_idx, da.memop); + da.mem_idx, memop); if (i == 7) { break; } tcg_gen_add_tl(addr, addr, eight); + memop = da.memop; } tcg_temp_free(eight); } else { @@ -2539,9 +2641,8 @@ static void gen_ldda_asi(DisasContext *dc, TCGv addr, int insn, int rd) return; case GET_ASI_DTWINX: - gen_check_align(addr, 15); gen_address_mask(dc, addr); - tcg_gen_qemu_ld_i64(hi, addr, da.mem_idx, da.memop); + tcg_gen_qemu_ld_i64(hi, addr, da.mem_idx, da.memop | MO_ALIGN_16); tcg_gen_addi_tl(addr, addr, 8); tcg_gen_qemu_ld_i64(lo, addr, da.mem_idx, da.memop); break; @@ -2566,15 +2667,27 @@ static void gen_ldda_asi(DisasContext *dc, TCGv addr, int insn, int rd) break; default: + /* ??? In theory we've handled all of the ASIs that are valid + for ldda, and this should raise DAE_invalid_asi. However, + real hardware allows others. This can be seen with e.g. + FreeBSD 10.3 wrt ASI_IC_TAG. */ { TCGv_i32 r_asi = tcg_const_i32(da.asi); + TCGv_i32 r_mop = tcg_const_i32(da.memop); + TCGv_i64 tmp = tcg_temp_new_i64(); save_state(dc); - gen_helper_ldda_asi(cpu_env, addr, r_asi); + gen_helper_ld_asi(tmp, cpu_env, addr, r_asi, r_mop); tcg_temp_free_i32(r_asi); + tcg_temp_free_i32(r_mop); - tcg_gen_ld_i64(hi, cpu_env, offsetof(CPUSPARCState, qt0.high)); - tcg_gen_ld_i64(lo, cpu_env, offsetof(CPUSPARCState, qt0.low)); + /* See above. */ + if ((da.memop & MO_BSWAP) == MO_TE) { + tcg_gen_extr32_i64(lo, hi, tmp); + } else { + tcg_gen_extr32_i64(hi, lo, tmp); + } + tcg_temp_free_i64(tmp); } break; } @@ -2594,9 +2707,8 @@ static void gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr, break; case GET_ASI_DTWINX: - gen_check_align(addr, 15); gen_address_mask(dc, addr); - tcg_gen_qemu_st_i64(hi, addr, da.mem_idx, da.memop); + tcg_gen_qemu_st_i64(hi, addr, da.mem_idx, da.memop | MO_ALIGN_16); tcg_gen_addi_tl(addr, addr, 8); tcg_gen_qemu_st_i64(lo, addr, da.mem_idx, da.memop); break; @@ -2620,15 +2732,21 @@ static void gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr, break; default: + /* ??? In theory we've handled all of the ASIs that are valid + for stda, and this should raise DAE_invalid_asi. */ { TCGv_i32 r_asi = tcg_const_i32(da.asi); - TCGv_i32 r_mop = tcg_const_i32(MO_Q); - TCGv_i64 t64; + TCGv_i32 r_mop = tcg_const_i32(da.memop); + TCGv_i64 t64 = tcg_temp_new_i64(); - save_state(dc); + /* See above. */ + if ((da.memop & MO_BSWAP) == MO_TE) { + tcg_gen_concat32_i64(t64, lo, hi); + } else { + tcg_gen_concat32_i64(t64, hi, lo); + } - t64 = tcg_temp_new_i64(); - tcg_gen_concat_tl_i64(t64, lo, hi); + save_state(dc); gen_helper_st_asi(cpu_env, addr, t64, r_asi, r_mop); tcg_temp_free_i32(r_mop); tcg_temp_free_i32(r_asi); @@ -2638,23 +2756,27 @@ static void gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr, } } -static void gen_casx_asi(DisasContext *dc, TCGv addr, TCGv val2, +static void gen_casx_asi(DisasContext *dc, TCGv addr, TCGv cmpv, int insn, int rd) { DisasASI da = get_asi(dc, insn, MO_TEQ); - TCGv val1 = gen_load_gpr(dc, rd); - TCGv dst = gen_dest_gpr(dc, rd); - TCGv_i32 r_asi; + TCGv oldv; - if (da.type == GET_ASI_EXCP) { + switch (da.type) { + case GET_ASI_EXCP: return; + case GET_ASI_DIRECT: + oldv = tcg_temp_new(); + tcg_gen_atomic_cmpxchg_tl(oldv, addr, cmpv, gen_load_gpr(dc, rd), + da.mem_idx, da.memop); + gen_store_gpr(dc, rd, oldv); + tcg_temp_free(oldv); + break; + default: + /* ??? Should be DAE_invalid_asi. */ + gen_exception(dc, TT_DATA_ACCESS); + break; } - - save_state(dc); - r_asi = tcg_const_i32(da.asi); - gen_helper_casx_asi(dst, cpu_env, addr, val1, val2, r_asi); - tcg_temp_free_i32(r_asi); - gen_store_gpr(dc, rd, dst); } #elif !defined(CONFIG_USER_ONLY) @@ -2712,6 +2834,27 @@ static void gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr, gen_address_mask(dc, addr); tcg_gen_qemu_st_i64(t64, addr, da.mem_idx, da.memop); break; + case GET_ASI_BFILL: + /* Store 32 bytes of T64 to ADDR. */ + /* ??? The original qemu code suggests 8-byte alignment, dropping + the low bits, but the only place I can see this used is in the + Linux kernel with 32 byte alignment, which would make more sense + as a cacheline-style operation. */ + { + TCGv d_addr = tcg_temp_new(); + TCGv eight = tcg_const_tl(8); + int i; + + tcg_gen_andi_tl(d_addr, addr, -8); + for (i = 0; i < 32; i += 8) { + tcg_gen_qemu_st_i64(t64, d_addr, da.mem_idx, da.memop); + tcg_gen_add_tl(d_addr, d_addr, eight); + } + + tcg_temp_free(d_addr); + tcg_temp_free(eight); + } + break; default: { TCGv_i32 r_asi = tcg_const_i32(da.asi); @@ -3454,7 +3597,6 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) break; } else if (xop == 0x2b) { /* rdtbr / V9 flushw */ #ifdef TARGET_SPARC64 - save_state(dc); gen_helper_flushw(cpu_env); #else if (!supervisor(dc)) @@ -5058,12 +5200,10 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) /* nop */ break; case 0x3c: /* save */ - save_state(dc); gen_helper_save(cpu_env); gen_store_gpr(dc, rd, cpu_tmp0); break; case 0x3d: /* restore */ - save_state(dc); gen_helper_restore(cpu_env); gen_store_gpr(dc, rd, cpu_tmp0); break; @@ -5163,31 +5303,15 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) gen_address_mask(dc, cpu_addr); tcg_gen_qemu_ld16s(cpu_val, cpu_addr, dc->mem_idx); break; - case 0xd: /* ldstub -- XXX: should be atomically */ - { - TCGv r_const; - TCGv tmp = tcg_temp_new(); - - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_ld8u(tmp, cpu_addr, dc->mem_idx); - r_const = tcg_const_tl(0xff); - tcg_gen_qemu_st8(r_const, cpu_addr, dc->mem_idx); - tcg_gen_mov_tl(cpu_val, tmp); - tcg_temp_free(r_const); - tcg_temp_free(tmp); - } + case 0xd: /* ldstub */ + gen_ldstub(dc, cpu_val, cpu_addr, dc->mem_idx); break; case 0x0f: /* swap, swap register with memory. Also atomically */ - { - TCGv t0 = get_temp_tl(dc); - CHECK_IU_FEATURE(dc, SWAP); - cpu_src1 = gen_load_gpr(dc, rd); - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_ld32u(t0, cpu_addr, dc->mem_idx); - tcg_gen_qemu_st32(cpu_src1, cpu_addr, dc->mem_idx); - tcg_gen_mov_tl(cpu_val, t0); - } + CHECK_IU_FEATURE(dc, SWAP); + cpu_src1 = gen_load_gpr(dc, rd); + gen_swap(dc, cpu_val, cpu_src1, cpu_addr, + dc->mem_idx, MO_TEUL); break; #if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) case 0x10: /* lda, V9 lduwa, load word alternate */ @@ -5278,18 +5402,15 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) skip_move: ; #endif } else if (xop >= 0x20 && xop < 0x24) { - TCGv t0; - if (gen_trap_ifnofpu(dc)) { goto jmp_insn; } switch (xop) { case 0x20: /* ldf, load fpreg */ gen_address_mask(dc, cpu_addr); - t0 = get_temp_tl(dc); - tcg_gen_qemu_ld32u(t0, cpu_addr, dc->mem_idx); cpu_dst_32 = gen_dest_fpr_F(dc); - tcg_gen_trunc_tl_i32(cpu_dst_32, t0); + tcg_gen_qemu_ld_i32(cpu_dst_32, cpu_addr, + dc->mem_idx, MO_TEUL); gen_store_fpr_F(dc, rd, cpu_dst_32); break; case 0x21: /* ldfsr, V9 ldxfsr */ @@ -5297,35 +5418,37 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) gen_address_mask(dc, cpu_addr); if (rd == 1) { TCGv_i64 t64 = tcg_temp_new_i64(); - tcg_gen_qemu_ld64(t64, cpu_addr, dc->mem_idx); + tcg_gen_qemu_ld_i64(t64, cpu_addr, + dc->mem_idx, MO_TEQ); gen_helper_ldxfsr(cpu_fsr, cpu_env, cpu_fsr, t64); tcg_temp_free_i64(t64); break; } #endif cpu_dst_32 = get_temp_i32(dc); - t0 = get_temp_tl(dc); - tcg_gen_qemu_ld32u(t0, cpu_addr, dc->mem_idx); - tcg_gen_trunc_tl_i32(cpu_dst_32, t0); + tcg_gen_qemu_ld_i32(cpu_dst_32, cpu_addr, + dc->mem_idx, MO_TEUL); gen_helper_ldfsr(cpu_fsr, cpu_env, cpu_fsr, cpu_dst_32); break; case 0x22: /* ldqf, load quad fpreg */ - { - TCGv_i32 r_const; - - CHECK_FPU_FEATURE(dc, FLOAT128); - r_const = tcg_const_i32(dc->mem_idx); - gen_address_mask(dc, cpu_addr); - gen_helper_ldqf(cpu_env, cpu_addr, r_const); - tcg_temp_free_i32(r_const); - gen_op_store_QT0_fpr(QFPREG(rd)); - gen_update_fprs_dirty(dc, QFPREG(rd)); - } + CHECK_FPU_FEATURE(dc, FLOAT128); + gen_address_mask(dc, cpu_addr); + cpu_src1_64 = tcg_temp_new_i64(); + tcg_gen_qemu_ld_i64(cpu_src1_64, cpu_addr, dc->mem_idx, + MO_TEQ | MO_ALIGN_4); + tcg_gen_addi_tl(cpu_addr, cpu_addr, 8); + cpu_src2_64 = tcg_temp_new_i64(); + tcg_gen_qemu_ld_i64(cpu_src2_64, cpu_addr, dc->mem_idx, + MO_TEQ | MO_ALIGN_4); + gen_store_fpr_Q(dc, rd, cpu_src1_64, cpu_src2_64); + tcg_temp_free_i64(cpu_src1_64); + tcg_temp_free_i64(cpu_src2_64); break; case 0x23: /* lddf, load double fpreg */ gen_address_mask(dc, cpu_addr); cpu_dst_64 = gen_dest_fpr_D(dc, rd); - tcg_gen_qemu_ld64(cpu_dst_64, cpu_addr, dc->mem_idx); + tcg_gen_qemu_ld_i64(cpu_dst_64, cpu_addr, dc->mem_idx, + MO_TEQ | MO_ALIGN_4); gen_store_fpr_D(dc, rd, cpu_dst_64); break; default: @@ -5398,13 +5521,10 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) } switch (xop) { case 0x24: /* stf, store fpreg */ - { - TCGv t = get_temp_tl(dc); - gen_address_mask(dc, cpu_addr); - cpu_src1_32 = gen_load_fpr_F(dc, rd); - tcg_gen_ext_i32_tl(t, cpu_src1_32); - tcg_gen_qemu_st32(t, cpu_addr, dc->mem_idx); - } + gen_address_mask(dc, cpu_addr); + cpu_src1_32 = gen_load_fpr_F(dc, rd); + tcg_gen_qemu_st_i32(cpu_src1_32, cpu_addr, + dc->mem_idx, MO_TEUL); break; case 0x25: /* stfsr, V9 stxfsr */ { @@ -5421,16 +5541,20 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) case 0x26: #ifdef TARGET_SPARC64 /* V9 stqf, store quad fpreg */ - { - TCGv_i32 r_const; - - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_op_load_fpr_QT0(QFPREG(rd)); - r_const = tcg_const_i32(dc->mem_idx); - gen_address_mask(dc, cpu_addr); - gen_helper_stqf(cpu_env, cpu_addr, r_const); - tcg_temp_free_i32(r_const); - } + CHECK_FPU_FEATURE(dc, FLOAT128); + gen_address_mask(dc, cpu_addr); + /* ??? While stqf only requires 4-byte alignment, it is + legal for the cpu to signal the unaligned exception. + The OS trap handler is then required to fix it up. + For qemu, this avoids having to probe the second page + before performing the first write. */ + cpu_src1_64 = gen_load_fpr_Q0(dc, rd); + tcg_gen_qemu_st_i64(cpu_src1_64, cpu_addr, + dc->mem_idx, MO_TEQ | MO_ALIGN_16); + tcg_gen_addi_tl(cpu_addr, cpu_addr, 8); + cpu_src2_64 = gen_load_fpr_Q1(dc, rd); + tcg_gen_qemu_st_i64(cpu_src1_64, cpu_addr, + dc->mem_idx, MO_TEQ); break; #else /* !TARGET_SPARC64 */ /* stdfq, store floating point queue */ @@ -5448,7 +5572,8 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) case 0x27: /* stdf, store double fpreg */ gen_address_mask(dc, cpu_addr); cpu_src1_64 = gen_load_fpr_D(dc, rd); - tcg_gen_qemu_st64(cpu_src1_64, cpu_addr, dc->mem_idx); + tcg_gen_qemu_st_i64(cpu_src1_64, cpu_addr, dc->mem_idx, + MO_TEQ | MO_ALIGN_4); break; default: goto illegal_insn; @@ -5468,7 +5593,6 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) if (gen_trap_ifnofpu(dc)) { goto jmp_insn; } - gen_check_align(cpu_addr, 7); gen_stf_asi(dc, cpu_addr, insn, 16, QFPREG(rd)); } break; diff --git a/target-sparc/win_helper.c b/target-sparc/win_helper.c index a8a6c0cfc4..2d5b5469a9 100644 --- a/target-sparc/win_helper.c +++ b/target-sparc/win_helper.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "trace.h" @@ -111,13 +112,13 @@ void helper_rett(CPUSPARCState *env) unsigned int cwp; if (env->psret == 1) { - helper_raise_exception(env, TT_ILL_INSN); + cpu_raise_exception_ra(env, TT_ILL_INSN, GETPC()); } env->psret = 1; cwp = cpu_cwp_inc(env, env->cwp + 1) ; if (env->wim & (1 << cwp)) { - helper_raise_exception(env, TT_WIN_UNF); + cpu_raise_exception_ra(env, TT_WIN_UNF, GETPC()); } cpu_set_cwp(env, cwp); env->psrs = env->psrps; @@ -131,7 +132,7 @@ void helper_save(CPUSPARCState *env) cwp = cpu_cwp_dec(env, env->cwp - 1); if (env->wim & (1 << cwp)) { - helper_raise_exception(env, TT_WIN_OVF); + cpu_raise_exception_ra(env, TT_WIN_OVF, GETPC()); } cpu_set_cwp(env, cwp); } @@ -142,7 +143,7 @@ void helper_restore(CPUSPARCState *env) cwp = cpu_cwp_inc(env, env->cwp + 1); if (env->wim & (1 << cwp)) { - helper_raise_exception(env, TT_WIN_UNF); + cpu_raise_exception_ra(env, TT_WIN_UNF, GETPC()); } cpu_set_cwp(env, cwp); } @@ -150,7 +151,7 @@ void helper_restore(CPUSPARCState *env) void helper_wrpsr(CPUSPARCState *env, target_ulong new_psr) { if ((new_psr & PSR_CWP) >= env->nwindows) { - helper_raise_exception(env, TT_ILL_INSN); + cpu_raise_exception_ra(env, TT_ILL_INSN, GETPC()); } else { cpu_put_psr(env, new_psr); } @@ -170,14 +171,14 @@ void helper_save(CPUSPARCState *env) cwp = cpu_cwp_dec(env, env->cwp - 1); if (env->cansave == 0) { - helper_raise_exception(env, TT_SPILL | (env->otherwin != 0 ? - (TT_WOTHER | - ((env->wstate & 0x38) >> 1)) : - ((env->wstate & 0x7) << 2))); + int tt = TT_SPILL | (env->otherwin != 0 + ? (TT_WOTHER | ((env->wstate & 0x38) >> 1)) + : ((env->wstate & 0x7) << 2)); + cpu_raise_exception_ra(env, tt, GETPC()); } else { if (env->cleanwin - env->canrestore == 0) { /* XXX Clean windows without trap */ - helper_raise_exception(env, TT_CLRWIN); + cpu_raise_exception_ra(env, TT_CLRWIN, GETPC()); } else { env->cansave--; env->canrestore++; @@ -192,10 +193,10 @@ void helper_restore(CPUSPARCState *env) cwp = cpu_cwp_inc(env, env->cwp + 1); if (env->canrestore == 0) { - helper_raise_exception(env, TT_FILL | (env->otherwin != 0 ? - (TT_WOTHER | - ((env->wstate & 0x38) >> 1)) : - ((env->wstate & 0x7) << 2))); + int tt = TT_FILL | (env->otherwin != 0 + ? (TT_WOTHER | ((env->wstate & 0x38) >> 1)) + : ((env->wstate & 0x7) << 2)); + cpu_raise_exception_ra(env, tt, GETPC()); } else { env->cansave++; env->canrestore--; @@ -206,10 +207,10 @@ void helper_restore(CPUSPARCState *env) void helper_flushw(CPUSPARCState *env) { if (env->cansave != env->nwindows - 2) { - helper_raise_exception(env, TT_SPILL | (env->otherwin != 0 ? - (TT_WOTHER | - ((env->wstate & 0x38) >> 1)) : - ((env->wstate & 0x7) << 2))); + int tt = TT_SPILL | (env->otherwin != 0 + ? (TT_WOTHER | ((env->wstate & 0x38) >> 1)) + : ((env->wstate & 0x7) << 2)); + cpu_raise_exception_ra(env, tt, GETPC()); } } |