diff options
-rw-r--r-- | target-sparc/op_helper.c | 42 |
1 files changed, 30 insertions, 12 deletions
diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c index 381e6c4..ce8c6f1 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, |