summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArtyom Tarasenko <atar4qemu@googlemail.com>2010-01-15 22:28:56 +0100
committerBlue Swirl <blauwirbel@gmail.com>2010-01-15 21:33:28 +0000
commit576c2cdc767ab9e2dc038fa4c99f22e53287a3de (patch)
tree911b667315790cff9d418b7195c7088733464833
parent09aac1266e8acd02f1beb7adc91286716d7162bd (diff)
sparc32 do_unassigned_access overhaul v2
According to pages 9-31 - 9-34 of "SuperSPARC & MultiCache Controller User's Manual": 1. "A lower priority fault may not overwrite the MFSR status of a higher priority fault." 2. The MFAR is overwritten according to the policy defined for the MFSR 3. The overwrite bit is asserted if the fault status register (MFSR) has been written more than once by faults of the same class 4. SuperSPARC will never place instruction fault addresses in the MFAR. Implementation of points 1-3 allows booting Solaris 2.6 and 2.5.1. v2: CODING_STYLE fixes Signed-off-by: Artyom Tarasenko <atar4qemu@gmail.com> Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
-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,