diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2019-08-01 19:30:06 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2019-09-17 12:01:00 +0100 |
commit | c9d793f44620a4793239da73f67758ce5f5ba5d0 (patch) | |
tree | a0d7f1bdb050d2509a8cbdbb55932004a48f8a81 /target/sparc | |
parent | 186c0ab9b98d90642f37a629b1ff520cbfd06394 (diff) | |
download | qemu-c9d793f44620a4793239da73f67758ce5f5ba5d0.zip qemu-c9d793f44620a4793239da73f67758ce5f5ba5d0.tar.gz qemu-c9d793f44620a4793239da73f67758ce5f5ba5d0.tar.bz2 |
target/sparc: Factor out the body of sparc_cpu_unassigned_access()
Currently the SPARC target uses the old-style do_unassigned_access
hook. We want to switch it over to do_transaction_failed, but to do
this we must first remove all the direct calls in ldst_helper.c to
cpu_unassigned_access(). Factor out the body of the hook function's
code into a new sparc_raise_mmu_fault() and call it from the hook and
from the various places that used to call cpu_unassigned_access().
In passing, this fixes a bug where the code that raised the
MMU exception was directly calling GETPC() from a function that
was several levels deep in the callstack from the original
helper function: the new sparc_raise_mmu_fault() instead takes
the return address as an argument.
Other than the use of retaddr rather than GETPC() and a comment
format fixup, the body of the new function has no changes from
that of the old hook function.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Tested-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
Message-id: 20190801183012.17564-2-peter.maydell@linaro.org
Diffstat (limited to 'target/sparc')
-rw-r--r-- | target/sparc/ldst_helper.c | 201 |
1 files changed, 106 insertions, 95 deletions
diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c index 7f56c10..26876e5 100644 --- a/target/sparc/ldst_helper.c +++ b/target/sparc/ldst_helper.c @@ -422,6 +422,99 @@ static void dump_asi(const char *txt, target_ulong addr, int asi, int size, } #endif +#ifndef CONFIG_USER_ONLY +#ifndef TARGET_SPARC64 +static void sparc_raise_mmu_fault(CPUState *cs, hwaddr addr, + bool is_write, bool is_exec, int is_asi, + unsigned size, uintptr_t retaddr) +{ + SPARCCPU *cpu = SPARC_CPU(cs); + CPUSPARCState *env = &cpu->env; + int fault_type; + +#ifdef DEBUG_UNASSIGNED + if (is_asi) { + printf("Unassigned mem %s access of %d byte%s to " TARGET_FMT_plx + " asi 0x%02x from " TARGET_FMT_lx "\n", + is_exec ? "exec" : is_write ? "write" : "read", size, + size == 1 ? "" : "s", addr, is_asi, env->pc); + } else { + printf("Unassigned mem %s access of %d byte%s to " TARGET_FMT_plx + " from " TARGET_FMT_lx "\n", + is_exec ? "exec" : is_write ? "write" : "read", size, + size == 1 ? "" : "s", addr, env->pc); + } +#endif + /* Don't overwrite translation and access faults */ + fault_type = (env->mmuregs[3] & 0x1c) >> 2; + if ((fault_type > 4) || (fault_type == 0)) { + env->mmuregs[3] = 0; /* Fault status register */ + if (is_asi) { + env->mmuregs[3] |= 1 << 16; + } + if (env->psrs) { + env->mmuregs[3] |= 1 << 5; + } + if (is_exec) { + env->mmuregs[3] |= 1 << 6; + } + if (is_write) { + env->mmuregs[3] |= 1 << 7; + } + env->mmuregs[3] |= (5 << 2) | 2; + /* SuperSPARC will never place instruction fault addresses in the FAR */ + if (!is_exec) { + env->mmuregs[4] = addr; /* Fault address register */ + } + } + /* overflow (same type fault was not read before another fault) */ + if (fault_type == ((env->mmuregs[3] & 0x1c)) >> 2) { + env->mmuregs[3] |= 1; + } + + if ((env->mmuregs[0] & MMU_E) && !(env->mmuregs[0] & MMU_NF)) { + int tt = is_exec ? TT_CODE_ACCESS : TT_DATA_ACCESS; + cpu_raise_exception_ra(env, tt, retaddr); + } + + /* + * flush neverland mappings created during no-fault mode, + * so the sequential MMU faults report proper fault types + */ + if (env->mmuregs[0] & MMU_NF) { + tlb_flush(cs); + } +} +#else +static void sparc_raise_mmu_fault(CPUState *cs, hwaddr addr, + bool is_write, bool is_exec, int is_asi, + unsigned size, uintptr_t retaddr) +{ + SPARCCPU *cpu = SPARC_CPU(cs); + CPUSPARCState *env = &cpu->env; + +#ifdef DEBUG_UNASSIGNED + printf("Unassigned mem access to " TARGET_FMT_plx " from " TARGET_FMT_lx + "\n", addr, env->pc); +#endif + + if (is_exec) { /* XXX has_hypervisor */ + if (env->lsu & (IMMU_E)) { + cpu_raise_exception_ra(env, TT_CODE_ACCESS, retaddr); + } else if (cpu_has_hypervisor(env) && !(env->hpstate & HS_PRIV)) { + cpu_raise_exception_ra(env, TT_INSN_REAL_TRANSLATION_MISS, retaddr); + } + } else { + if (env->lsu & (DMMU_E)) { + cpu_raise_exception_ra(env, TT_DATA_ACCESS, retaddr); + } else if (cpu_has_hypervisor(env) && !(env->hpstate & HS_PRIV)) { + cpu_raise_exception_ra(env, TT_DATA_REAL_TRANSLATION_MISS, retaddr); + } + } +} +#endif +#endif + #ifndef TARGET_SPARC64 #ifndef CONFIG_USER_ONLY @@ -688,7 +781,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, break; case ASI_USERTXT: /* User code access, XXX */ default: - cpu_unassigned_access(cs, addr, false, false, asi, size); + sparc_raise_mmu_fault(cs, addr, false, false, asi, size, GETPC()); ret = 0; break; @@ -1026,7 +1119,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val, case ASI_USERTXT: /* User code access, XXX */ case ASI_KERNELTXT: /* Supervisor code access, XXX */ default: - cpu_unassigned_access(cs, addr, true, false, asi, size); + sparc_raise_mmu_fault(cs, addr, true, false, asi, size, GETPC()); break; case ASI_USERDATA: /* User data access */ @@ -1292,7 +1385,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, ret = env->immu.tag_access; break; default: - cpu_unassigned_access(cs, addr, false, false, 1, size); + sparc_raise_mmu_fault(cs, addr, false, false, 1, size, GETPC()); ret = 0; } break; @@ -1358,7 +1451,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, ret = env->dmmu.physical_watchpoint; break; default: - cpu_unassigned_access(cs, addr, false, false, 1, size); + sparc_raise_mmu_fault(cs, addr, false, false, 1, size, GETPC()); ret = 0; } break; @@ -1407,7 +1500,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, case ASI_SCRATCHPAD: /* UA2005 privileged scratchpad */ if (unlikely((addr >= 0x20) && (addr < 0x30))) { /* Hyperprivileged access only */ - cpu_unassigned_access(cs, addr, false, false, 1, size); + sparc_raise_mmu_fault(cs, addr, false, false, 1, size, GETPC()); } /* fall through */ case ASI_HYP_SCRATCHPAD: /* UA2005 hyperprivileged scratchpad */ @@ -1425,7 +1518,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, ret = env->dmmu.mmu_secondary_context; break; default: - cpu_unassigned_access(cs, addr, true, false, 1, size); + sparc_raise_mmu_fault(cs, addr, true, false, 1, size, GETPC()); } break; case ASI_DCACHE_DATA: /* D-cache data */ @@ -1448,7 +1541,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, case ASI_DMMU_DEMAP: /* D-MMU demap, WO */ case ASI_INTR_W: /* Interrupt vector, WO */ default: - cpu_unassigned_access(cs, addr, false, false, 1, size); + sparc_raise_mmu_fault(cs, addr, false, false, 1, size, GETPC()); ret = 0; break; } @@ -1622,7 +1715,7 @@ 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); + sparc_raise_mmu_fault(cs, addr, true, false, 1, size, GETPC()); break; } @@ -1706,7 +1799,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, env->dmmu.physical_watchpoint = val; break; default: - cpu_unassigned_access(cs, addr, true, false, 1, size); + sparc_raise_mmu_fault(cs, addr, true, false, 1, size, GETPC()); break; } @@ -1750,7 +1843,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, case ASI_SCRATCHPAD: /* UA2005 privileged scratchpad */ if (unlikely((addr >= 0x20) && (addr < 0x30))) { /* Hyperprivileged access only */ - cpu_unassigned_access(cs, addr, true, false, 1, size); + sparc_raise_mmu_fault(cs, addr, true, false, 1, size, GETPC()); } /* fall through */ case ASI_HYP_SCRATCHPAD: /* UA2005 hyperprivileged scratchpad */ @@ -1776,7 +1869,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, (1 << MMU_KERNEL_SECONDARY_IDX)); break; default: - cpu_unassigned_access(cs, addr, true, false, 1, size); + sparc_raise_mmu_fault(cs, addr, true, false, 1, size, GETPC()); } } return; @@ -1808,7 +1901,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: - cpu_unassigned_access(cs, addr, true, false, 1, size); + sparc_raise_mmu_fault(cs, addr, true, false, 1, size, GETPC()); return; } } @@ -1816,95 +1909,13 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, #endif /* TARGET_SPARC64 */ #if !defined(CONFIG_USER_ONLY) -#ifndef TARGET_SPARC64 void sparc_cpu_unassigned_access(CPUState *cs, hwaddr addr, bool is_write, bool is_exec, int is_asi, unsigned size) { - SPARCCPU *cpu = SPARC_CPU(cs); - CPUSPARCState *env = &cpu->env; - int fault_type; - -#ifdef DEBUG_UNASSIGNED - if (is_asi) { - printf("Unassigned mem %s access of %d byte%s to " TARGET_FMT_plx - " asi 0x%02x from " TARGET_FMT_lx "\n", - is_exec ? "exec" : is_write ? "write" : "read", size, - size == 1 ? "" : "s", addr, is_asi, env->pc); - } else { - printf("Unassigned mem %s access of %d byte%s to " TARGET_FMT_plx - " from " TARGET_FMT_lx "\n", - is_exec ? "exec" : is_write ? "write" : "read", size, - size == 1 ? "" : "s", addr, env->pc); - } -#endif - /* Don't overwrite translation and access faults */ - fault_type = (env->mmuregs[3] & 0x1c) >> 2; - if ((fault_type > 4) || (fault_type == 0)) { - env->mmuregs[3] = 0; /* Fault status register */ - if (is_asi) { - env->mmuregs[3] |= 1 << 16; - } - if (env->psrs) { - env->mmuregs[3] |= 1 << 5; - } - if (is_exec) { - env->mmuregs[3] |= 1 << 6; - } - if (is_write) { - env->mmuregs[3] |= 1 << 7; - } - env->mmuregs[3] |= (5 << 2) | 2; - /* SuperSPARC will never place instruction fault addresses in the FAR */ - if (!is_exec) { - env->mmuregs[4] = addr; /* Fault address register */ - } - } - /* overflow (same type fault was not read before another fault) */ - if (fault_type == ((env->mmuregs[3] & 0x1c)) >> 2) { - env->mmuregs[3] |= 1; - } - - if ((env->mmuregs[0] & MMU_E) && !(env->mmuregs[0] & MMU_NF)) { - 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, - so the sequential MMU faults report proper fault types */ - if (env->mmuregs[0] & MMU_NF) { - tlb_flush(cs); - } -} -#else -void sparc_cpu_unassigned_access(CPUState *cs, hwaddr addr, - bool is_write, bool is_exec, int is_asi, - unsigned size) -{ - SPARCCPU *cpu = SPARC_CPU(cs); - CPUSPARCState *env = &cpu->env; - -#ifdef DEBUG_UNASSIGNED - printf("Unassigned mem access to " TARGET_FMT_plx " from " TARGET_FMT_lx - "\n", addr, env->pc); -#endif - - 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()); - } - } + sparc_raise_mmu_fault(cs, addr, is_write, is_exec, is_asi, size, GETPC()); } #endif -#endif #if !defined(CONFIG_USER_ONLY) void QEMU_NORETURN sparc_cpu_do_unaligned_access(CPUState *cs, vaddr addr, |