aboutsummaryrefslogtreecommitdiff
path: root/target/sparc
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2019-08-01 19:30:06 +0100
committerPeter Maydell <peter.maydell@linaro.org>2019-09-17 12:01:00 +0100
commitc9d793f44620a4793239da73f67758ce5f5ba5d0 (patch)
treea0d7f1bdb050d2509a8cbdbb55932004a48f8a81 /target/sparc
parent186c0ab9b98d90642f37a629b1ff520cbfd06394 (diff)
downloadqemu-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.c201
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,