summary refs log tree commit diff stats
path: root/target/sparc/ldst_helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'target/sparc/ldst_helper.c')
-rw-r--r--target/sparc/ldst_helper.c317
1 files changed, 185 insertions, 132 deletions
diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c
index 7f56c100c6..7345827a96 100644
--- a/target/sparc/ldst_helper.c
+++ b/target/sparc/ldst_helper.c
@@ -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
 
@@ -625,26 +718,36 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr,
     case ASI_M_DATAC_DATA: /* SparcStation 5 D-cache data */
         break;
     case 0x21 ... 0x2f: /* MMU passthrough, 0x100000000 to 0xfffffffff */
+    {
+        MemTxResult result;
+        hwaddr access_addr = (hwaddr)addr | ((hwaddr)(asi & 0xf) << 32);
+
         switch (size) {
         case 1:
-            ret = ldub_phys(cs->as, (hwaddr)addr
-                            | ((hwaddr)(asi & 0xf) << 32));
+            ret = address_space_ldub(cs->as, access_addr,
+                                     MEMTXATTRS_UNSPECIFIED, &result);
             break;
         case 2:
-            ret = lduw_phys(cs->as, (hwaddr)addr
-                            | ((hwaddr)(asi & 0xf) << 32));
+            ret = address_space_lduw(cs->as, access_addr,
+                                     MEMTXATTRS_UNSPECIFIED, &result);
             break;
         default:
         case 4:
-            ret = ldl_phys(cs->as, (hwaddr)addr
-                           | ((hwaddr)(asi & 0xf) << 32));
+            ret = address_space_ldl(cs->as, access_addr,
+                                    MEMTXATTRS_UNSPECIFIED, &result);
             break;
         case 8:
-            ret = ldq_phys(cs->as, (hwaddr)addr
-                           | ((hwaddr)(asi & 0xf) << 32));
+            ret = address_space_ldq(cs->as, access_addr,
+                                    MEMTXATTRS_UNSPECIFIED, &result);
             break;
         }
+
+        if (result != MEMTX_OK) {
+            sparc_raise_mmu_fault(cs, access_addr, false, false, false,
+                                  size, GETPC());
+        }
         break;
+    }
     case 0x30: /* Turbosparc secondary cache diagnostic */
     case 0x31: /* Turbosparc RAM snoop */
     case 0x32: /* Turbosparc page table descriptor diagnostic */
@@ -688,7 +791,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;
 
@@ -777,6 +880,9 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val,
             }
             break;
         case 0x01c00100: /* MXCC stream source */
+        {
+            int i;
+
             if (size == 8) {
                 env->mxccregs[0] = val;
             } else {
@@ -784,20 +890,27 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val,
                               "%08x: unimplemented access size: %d\n", addr,
                               size);
             }
-            env->mxccdata[0] = ldq_phys(cs->as,
-                                        (env->mxccregs[0] & 0xffffffffULL) +
-                                        0);
-            env->mxccdata[1] = ldq_phys(cs->as,
-                                        (env->mxccregs[0] & 0xffffffffULL) +
-                                        8);
-            env->mxccdata[2] = ldq_phys(cs->as,
-                                        (env->mxccregs[0] & 0xffffffffULL) +
-                                        16);
-            env->mxccdata[3] = ldq_phys(cs->as,
-                                        (env->mxccregs[0] & 0xffffffffULL) +
-                                        24);
+
+            for (i = 0; i < 4; i++) {
+                MemTxResult result;
+                hwaddr access_addr = (env->mxccregs[0] & 0xffffffffULL) + 8 * i;
+
+                env->mxccdata[i] = address_space_ldq(cs->as,
+                                                     access_addr,
+                                                     MEMTXATTRS_UNSPECIFIED,
+                                                     &result);
+                if (result != MEMTX_OK) {
+                    /* TODO: investigate whether this is the right behaviour */
+                    sparc_raise_mmu_fault(cs, access_addr, false, false,
+                                          false, size, GETPC());
+                }
+            }
             break;
+        }
         case 0x01c00200: /* MXCC stream destination */
+        {
+            int i;
+
             if (size == 8) {
                 env->mxccregs[1] = val;
             } else {
@@ -805,15 +918,22 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val,
                               "%08x: unimplemented access size: %d\n", addr,
                               size);
             }
-            stq_phys(cs->as, (env->mxccregs[1] & 0xffffffffULL) +  0,
-                     env->mxccdata[0]);
-            stq_phys(cs->as, (env->mxccregs[1] & 0xffffffffULL) +  8,
-                     env->mxccdata[1]);
-            stq_phys(cs->as, (env->mxccregs[1] & 0xffffffffULL) + 16,
-                     env->mxccdata[2]);
-            stq_phys(cs->as, (env->mxccregs[1] & 0xffffffffULL) + 24,
-                     env->mxccdata[3]);
+
+            for (i = 0; i < 4; i++) {
+                MemTxResult result;
+                hwaddr access_addr = (env->mxccregs[1] & 0xffffffffULL) + 8 * i;
+
+                address_space_stq(cs->as, access_addr, env->mxccdata[i],
+                                  MEMTXATTRS_UNSPECIFIED, &result);
+
+                if (result != MEMTX_OK) {
+                    /* TODO: investigate whether this is the right behaviour */
+                    sparc_raise_mmu_fault(cs, access_addr, true, false,
+                                          false, size, GETPC());
+                }
+            }
             break;
+        }
         case 0x01c00a00: /* MXCC control register */
             if (size == 8) {
                 env->mxccregs[3] = val;
@@ -960,25 +1080,32 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val,
         break;
     case 0x21 ... 0x2f: /* MMU passthrough, 0x100000000 to 0xfffffffff */
         {
+            MemTxResult result;
+            hwaddr access_addr = (hwaddr)addr | ((hwaddr)(asi & 0xf) << 32);
+
             switch (size) {
             case 1:
-                stb_phys(cs->as, (hwaddr)addr
-                         | ((hwaddr)(asi & 0xf) << 32), val);
+                address_space_stb(cs->as, access_addr, val,
+                                  MEMTXATTRS_UNSPECIFIED, &result);
                 break;
             case 2:
-                stw_phys(cs->as, (hwaddr)addr
-                         | ((hwaddr)(asi & 0xf) << 32), val);
+                address_space_stw(cs->as, access_addr, val,
+                                  MEMTXATTRS_UNSPECIFIED, &result);
                 break;
             case 4:
             default:
-                stl_phys(cs->as, (hwaddr)addr
-                         | ((hwaddr)(asi & 0xf) << 32), val);
+                address_space_stl(cs->as, access_addr, val,
+                                  MEMTXATTRS_UNSPECIFIED, &result);
                 break;
             case 8:
-                stq_phys(cs->as, (hwaddr)addr
-                         | ((hwaddr)(asi & 0xf) << 32), val);
+                address_space_stq(cs->as, access_addr, val,
+                                  MEMTXATTRS_UNSPECIFIED, &result);
                 break;
             }
+            if (result != MEMTX_OK) {
+                sparc_raise_mmu_fault(cs, access_addr, true, false, false,
+                                      size, GETPC());
+            }
         }
         break;
     case 0x30: /* store buffer tags or Turbosparc secondary cache diagnostic */
@@ -1026,7 +1153,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 +1419,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 +1485,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 +1534,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 +1552,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 +1575,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 +1749,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 +1833,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 +1877,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 +1903,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 +1935,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,95 +1943,21 @@ 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);
-    }
-}
-#else
-void sparc_cpu_unassigned_access(CPUState *cs, hwaddr addr,
-                                 bool is_write, bool is_exec, int is_asi,
-                                 unsigned size)
+void sparc_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
+                                     vaddr addr, unsigned size,
+                                     MMUAccessType access_type,
+                                     int mmu_idx, MemTxAttrs attrs,
+                                     MemTxResult response, 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
+    bool is_write = access_type == MMU_DATA_STORE;
+    bool is_exec = access_type == MMU_INST_FETCH;
+    bool is_asi = false;
 
-    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());
-        }
-    }
+    sparc_raise_mmu_fault(cs, physaddr, is_write, is_exec,
+                          is_asi, size, retaddr);
 }
 #endif
-#endif
 
 #if !defined(CONFIG_USER_ONLY)
 void QEMU_NORETURN sparc_cpu_do_unaligned_access(CPUState *cs, vaddr addr,