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:
parent
186c0ab9b9
commit
c9d793f446
@ -422,6 +422,99 @@ static void dump_asi(const char *txt, target_ulong addr, int asi, int size,
|
|||||||
}
|
}
|
||||||
#endif
|
#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 TARGET_SPARC64
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
|
||||||
@ -688,7 +781,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr,
|
|||||||
break;
|
break;
|
||||||
case ASI_USERTXT: /* User code access, XXX */
|
case ASI_USERTXT: /* User code access, XXX */
|
||||||
default:
|
default:
|
||||||
cpu_unassigned_access(cs, addr, false, false, asi, size);
|
sparc_raise_mmu_fault(cs, addr, false, false, asi, size, GETPC());
|
||||||
ret = 0;
|
ret = 0;
|
||||||
break;
|
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_USERTXT: /* User code access, XXX */
|
||||||
case ASI_KERNELTXT: /* Supervisor code access, XXX */
|
case ASI_KERNELTXT: /* Supervisor code access, XXX */
|
||||||
default:
|
default:
|
||||||
cpu_unassigned_access(cs, addr, true, false, asi, size);
|
sparc_raise_mmu_fault(cs, addr, true, false, asi, size, GETPC());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ASI_USERDATA: /* User data access */
|
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;
|
ret = env->immu.tag_access;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
cpu_unassigned_access(cs, addr, false, false, 1, size);
|
sparc_raise_mmu_fault(cs, addr, false, false, 1, size, GETPC());
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1358,7 +1451,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr,
|
|||||||
ret = env->dmmu.physical_watchpoint;
|
ret = env->dmmu.physical_watchpoint;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
cpu_unassigned_access(cs, addr, false, false, 1, size);
|
sparc_raise_mmu_fault(cs, addr, false, false, 1, size, GETPC());
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1407,7 +1500,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr,
|
|||||||
case ASI_SCRATCHPAD: /* UA2005 privileged scratchpad */
|
case ASI_SCRATCHPAD: /* UA2005 privileged scratchpad */
|
||||||
if (unlikely((addr >= 0x20) && (addr < 0x30))) {
|
if (unlikely((addr >= 0x20) && (addr < 0x30))) {
|
||||||
/* Hyperprivileged access only */
|
/* 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 */
|
/* fall through */
|
||||||
case ASI_HYP_SCRATCHPAD: /* UA2005 hyperprivileged scratchpad */
|
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;
|
ret = env->dmmu.mmu_secondary_context;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
cpu_unassigned_access(cs, addr, true, false, 1, size);
|
sparc_raise_mmu_fault(cs, addr, true, false, 1, size, GETPC());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ASI_DCACHE_DATA: /* D-cache data */
|
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_DMMU_DEMAP: /* D-MMU demap, WO */
|
||||||
case ASI_INTR_W: /* Interrupt vector, WO */
|
case ASI_INTR_W: /* Interrupt vector, WO */
|
||||||
default:
|
default:
|
||||||
cpu_unassigned_access(cs, addr, false, false, 1, size);
|
sparc_raise_mmu_fault(cs, addr, false, false, 1, size, GETPC());
|
||||||
ret = 0;
|
ret = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1622,7 +1715,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
|
|||||||
case 8:
|
case 8:
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
cpu_unassigned_access(cs, addr, true, false, 1, size);
|
sparc_raise_mmu_fault(cs, addr, true, false, 1, size, GETPC());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1706,7 +1799,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
|
|||||||
env->dmmu.physical_watchpoint = val;
|
env->dmmu.physical_watchpoint = val;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
cpu_unassigned_access(cs, addr, true, false, 1, size);
|
sparc_raise_mmu_fault(cs, addr, true, false, 1, size, GETPC());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1750,7 +1843,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
|
|||||||
case ASI_SCRATCHPAD: /* UA2005 privileged scratchpad */
|
case ASI_SCRATCHPAD: /* UA2005 privileged scratchpad */
|
||||||
if (unlikely((addr >= 0x20) && (addr < 0x30))) {
|
if (unlikely((addr >= 0x20) && (addr < 0x30))) {
|
||||||
/* Hyperprivileged access only */
|
/* 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 */
|
/* fall through */
|
||||||
case ASI_HYP_SCRATCHPAD: /* UA2005 hyperprivileged scratchpad */
|
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));
|
(1 << MMU_KERNEL_SECONDARY_IDX));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
cpu_unassigned_access(cs, addr, true, false, 1, size);
|
sparc_raise_mmu_fault(cs, addr, true, false, 1, size, GETPC());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
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_PNFL: /* Primary no-fault LE, RO */
|
||||||
case ASI_SNFL: /* Secondary no-fault LE, RO */
|
case ASI_SNFL: /* Secondary no-fault LE, RO */
|
||||||
default:
|
default:
|
||||||
cpu_unassigned_access(cs, addr, true, false, 1, size);
|
sparc_raise_mmu_fault(cs, addr, true, false, 1, size, GETPC());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1816,94 +1909,12 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
|
|||||||
#endif /* TARGET_SPARC64 */
|
#endif /* TARGET_SPARC64 */
|
||||||
|
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
#ifndef TARGET_SPARC64
|
|
||||||
void sparc_cpu_unassigned_access(CPUState *cs, hwaddr addr,
|
void sparc_cpu_unassigned_access(CPUState *cs, hwaddr addr,
|
||||||
bool is_write, bool is_exec, int is_asi,
|
bool is_write, bool is_exec, int is_asi,
|
||||||
unsigned size)
|
unsigned size)
|
||||||
{
|
{
|
||||||
SPARCCPU *cpu = SPARC_CPU(cs);
|
sparc_raise_mmu_fault(cs, addr, is_write, is_exec, is_asi, size, GETPC());
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
|
Loading…
Reference in New Issue
Block a user