diff options
author | Richard Henderson <rth@twiddle.net> | 2016-07-12 13:12:50 -0700 |
---|---|---|
committer | Richard Henderson <rth@twiddle.net> | 2016-10-31 09:46:25 -0600 |
commit | 2f9d35fc4006122bad33f9ae3e2e51d2263e98ee (patch) | |
tree | f6499fba9d4ce0cc5c0860fff9ea939784257ded /target-sparc/ldst_helper.c | |
parent | 808832277af11dafee5a55da2b9e41d019b879ca (diff) |
target-sparc: Introduce cpu_raise_exception_ra
Several helpers call helper_raise_exception directly, which requires
in turn that their callers have performed save_state. The new function
allows a TCG return address to be passed in so that we can restore
PC + NPC + flags data from that.
This fixes a bug in the usage of helper_check_align, whose callers had
not been calling save_state. It fixes another bug in which the divide
helpers used GETPC at a level other than the direct callee from TCG.
This allows the translator to avoid save_state prior to SAVE, RESTORE,
and FLUSHW instructions.
Tested-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
Signed-off-by: Richard Henderson <rth@twiddle.net>
Diffstat (limited to 'target-sparc/ldst_helper.c')
-rw-r--r-- | target-sparc/ldst_helper.c | 85 |
1 files changed, 39 insertions, 46 deletions
diff --git a/target-sparc/ldst_helper.c b/target-sparc/ldst_helper.c index 6ce5ccc37f..3c70766adf 100644 --- a/target-sparc/ldst_helper.c +++ b/target-sparc/ldst_helper.c @@ -301,17 +301,23 @@ static inline target_ulong asi_address_mask(CPUSPARCState *env, } #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 +446,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 */ @@ -708,7 +714,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 */ @@ -1112,10 +1118,10 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong 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) { @@ -1218,10 +1224,9 @@ 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); + 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 */ @@ -1276,7 +1281,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, case ASI_PNFL: /* Primary no-fault LE, RO */ case ASI_SNFL: /* Secondary no-fault LE, RO */ default: - helper_raise_exception(env, TT_DATA_ACCESS); + cpu_raise_exception_ra(env, TT_DATA_ACCESS, GETPC()); return; } } @@ -1300,10 +1305,10 @@ 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 */ @@ -1322,7 +1327,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, 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); + cpu_raise_exception_ra(env, cs->exception_index, GETPC()); } /* convert nonfaulting load ASIs to normal load ASIs */ @@ -1614,7 +1619,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, 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); + cpu_raise_exception_ra(env, TT_ILL_INSN, GETPC()); return 0; } @@ -1683,10 +1688,10 @@ 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 */ @@ -2032,7 +2037,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, case ASI_TWINX_S: /* Secondary, twinx */ case ASI_TWINX_SL: /* Secondary, twinx, LE */ /* Only stda allowed */ - helper_raise_exception(env, TT_ILL_INSN); + cpu_raise_exception_ra(env, TT_ILL_INSN, GETPC()); return; case ASI_DCACHE_DATA: /* D-cache data */ case ASI_DCACHE_TAG: /* D-cache tag access */ @@ -2076,7 +2081,7 @@ void helper_ldda_asi(CPUSPARCState *env, target_ulong addr, int asi) || (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()); } addr = asi_address_mask(env, asi, addr); @@ -2085,19 +2090,19 @@ void helper_ldda_asi(CPUSPARCState *env, target_ulong addr, int 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); + do_check_align(env, addr, 0xf, GETPC()); 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); + do_check_align(env, addr, 0xf, GETPC()); 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); + do_check_align(env, addr, 0xf, GETPC()); { CPUState *cs = CPU(sparc_env_get_cpu(env)); h = ldq_phys(cs->as, addr); @@ -2108,14 +2113,14 @@ void helper_ldda_asi(CPUSPARCState *env, target_ulong addr, int asi) 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); + do_check_align(env, addr, 0xf, GETPC()); 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); + do_check_align(env, addr, 0xf, GETPC()); if (env->pstate & PS_PRIV) { h = cpu_ldq_kernel_secondary(env, addr); l = cpu_ldq_kernel_secondary(env, addr + 8); @@ -2128,7 +2133,7 @@ void helper_ldda_asi(CPUSPARCState *env, target_ulong addr, int asi) /* fallthru */ case ASI_TWINX_P: /* Primary, twinx */ case ASI_TWINX_PL: /* Primary, twinx, LE */ - helper_check_align(env, addr, 0xf); + do_check_align(env, addr, 0xf, GETPC()); h = cpu_ldq_data(env, addr); l = cpu_ldq_data(env, addr + 8); break; @@ -2139,7 +2144,7 @@ void helper_ldda_asi(CPUSPARCState *env, target_ulong addr, int asi) 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); + cpu_raise_exception_ra(env, TT_PRIV_ACT, GETPC()); #endif default: /* Non-twinx asi, so this is the legacy ldda insn, which @@ -2147,7 +2152,7 @@ void helper_ldda_asi(CPUSPARCState *env, target_ulong addr, int asi) /* ??? 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); + do_check_align(env, addr, 7, GETPC()); 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; @@ -2196,7 +2201,7 @@ 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); + do_check_align(env, addr, 7, GETPC()); #if !defined(CONFIG_USER_ONLY) switch (mem_idx) { case MMU_USER_IDX: @@ -2232,7 +2237,7 @@ 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); + do_check_align(env, addr, 7, GETPC()); #if !defined(CONFIG_USER_ONLY) switch (mem_idx) { case MMU_USER_IDX: @@ -2314,11 +2319,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 +2336,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 +2361,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 +2375,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 |