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
This commit is contained in:
Peter Maydell 2019-08-01 19:30:06 +01:00
parent 186c0ab9b9
commit c9d793f446

View File

@ -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,94 +1909,12 @@ 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);
}
sparc_raise_mmu_fault(cs, addr, is_write, is_exec, is_asi, size, GETPC());
}
#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());
}
}
}
#endif
#endif
#if !defined(CONFIG_USER_ONLY)