summaryrefslogtreecommitdiff
path: root/target-sparc/op_helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'target-sparc/op_helper.c')
-rw-r--r--target-sparc/op_helper.c42
1 files changed, 30 insertions, 12 deletions
diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c
index 381e6c49d7..ce8c6f1aca 100644
--- a/target-sparc/op_helper.c
+++ b/target-sparc/op_helper.c
@@ -3714,6 +3714,7 @@ void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
int is_asi, int size)
{
CPUState *saved_env;
+ int fault_type;
/* XXX: hack to restore env in all cases, even if not called from
generated code */
@@ -3731,18 +3732,29 @@ void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
is_exec ? "exec" : is_write ? "write" : "read", size,
size == 1 ? "" : "s", addr, env->pc);
#endif
- if (env->mmuregs[3]) /* Fault status register */
- env->mmuregs[3] = 1; /* overflow (not read before another fault) */
- 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;
- env->mmuregs[4] = addr; /* Fault address register */
+ /* 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)) {
if (is_exec)
raise_exception(TT_CODE_ACCESS);
@@ -3750,6 +3762,12 @@ void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
raise_exception(TT_DATA_ACCESS);
}
env = saved_env;
+
+ /* 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(env, 1);
+ }
}
#else
void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,