Patchwork [3/3] sparc64: handle asi referencing nucleus and secondary MMU contexts

login
register
mail settings
Submitter Igor V. Kovalenko
Date May 3, 2010, 7:29 a.m.
Message ID <20100503072944.367.50356.stgit@skyserv>
Download mbox | patch
Permalink /patch/51481/
State New
Headers show

Comments

Igor V. Kovalenko - May 3, 2010, 7:29 a.m.
From: Igor V. Kovalenko <igor.v.kovalenko@gmail.com>

- increase max supported MMU modes to 6
- handle nucleus context asi
- handle secondary context asi
- handle non-faulting loads from secondary context

Signed-off-by: Igor V. Kovalenko <igor.v.kovalenko@gmail.com>
---
 softmmu_exec.h           |   25 ++++-
 target-sparc/cpu.h       |   13 ++-
 target-sparc/exec.h      |    4 +
 target-sparc/helper.c    |   42 ++++++--
 target-sparc/op_helper.c |  235 ++++++++++++++++++++++++++++++++++------------
 5 files changed, 241 insertions(+), 78 deletions(-)

Patch

diff --git a/softmmu_exec.h b/softmmu_exec.h
index a43e621..28d1d53 100644
--- a/softmmu_exec.h
+++ b/softmmu_exec.h
@@ -100,9 +100,28 @@ 
 #undef MEMSUFFIX
 #endif /* (NB_MMU_MODES >= 5) */
 
-#if (NB_MMU_MODES > 5)
-#error "NB_MMU_MODES > 5 is not supported for now"
-#endif /* (NB_MMU_MODES > 5) */
+#if (NB_MMU_MODES >= 6)
+
+#define ACCESS_TYPE 5
+#define MEMSUFFIX MMU_MODE5_SUFFIX
+#define DATA_SIZE 1
+#include "softmmu_header.h"
+
+#define DATA_SIZE 2
+#include "softmmu_header.h"
+
+#define DATA_SIZE 4
+#include "softmmu_header.h"
+
+#define DATA_SIZE 8
+#include "softmmu_header.h"
+#undef ACCESS_TYPE
+#undef MEMSUFFIX
+#endif /* (NB_MMU_MODES >= 6) */
+
+#if (NB_MMU_MODES > 6)
+#error "NB_MMU_MODES > 6 is not supported for now"
+#endif /* (NB_MMU_MODES > 6) */
 
 /* these access are slower, they must be as rare as possible */
 #define ACCESS_TYPE (NB_MMU_MODES)
diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h
index b705728..b679333 100644
--- a/target-sparc/cpu.h
+++ b/target-sparc/cpu.h
@@ -224,7 +224,7 @@  enum {
 #if !defined(TARGET_SPARC64)
 #define NB_MMU_MODES 2
 #else
-#define NB_MMU_MODES 3
+#define NB_MMU_MODES 6
 typedef struct trap_state {
     uint64_t tpc;
     uint64_t tnpc;
@@ -571,6 +571,9 @@  static inline void PUT_CWP64(CPUSPARCState *env1, int cwp)
 #if !defined(CONFIG_USER_ONLY)
 void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
                           int is_asi, int size);
+target_phys_addr_t cpu_get_phys_page_nofault(CPUState *env, target_ulong addr,
+                                           int mmu_idx);
+
 #endif
 int cpu_sparc_signal_handler(int host_signum, void *pinfo, void *puc);
 
@@ -587,10 +590,18 @@  int cpu_sparc_signal_handler(int host_signum, void *pinfo, void *puc);
 #define MMU_MODE1_SUFFIX _kernel
 #ifdef TARGET_SPARC64
 #define MMU_MODE2_SUFFIX _hypv
+#define MMU_MODE3_SUFFIX _nucleus
+#define MMU_MODE4_SUFFIX _user_secondary
+#define MMU_MODE5_SUFFIX _kernel_secondary
 #endif
 #define MMU_USER_IDX   0
 #define MMU_KERNEL_IDX 1
 #define MMU_HYPV_IDX   2
+#ifdef TARGET_SPARC64
+#define MMU_NUCLEUS_IDX 3
+#define MMU_USER_SECONDARY_IDX   4
+#define MMU_KERNEL_SECONDARY_IDX 5
+#endif
 
 static inline int cpu_mmu_index(CPUState *env1)
 {
diff --git a/target-sparc/exec.h b/target-sparc/exec.h
index 70df828..1e9de82 100644
--- a/target-sparc/exec.h
+++ b/target-sparc/exec.h
@@ -13,6 +13,10 @@  register struct CPUSPARCState *env asm(AREG0);
 #include "cpu.h"
 #include "exec-all.h"
 
+#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+#endif /* !defined(CONFIG_USER_ONLY) */
+
 /* op_helper.c */
 void do_interrupt(CPUState *env);
 
diff --git a/target-sparc/helper.c b/target-sparc/helper.c
index 4ece01b..cac6cad 100644
--- a/target-sparc/helper.c
+++ b/target-sparc/helper.c
@@ -420,21 +420,32 @@  static inline int ultrasparc_tag_match(SparcTLBEntry *tlb,
 
 static int get_physical_address_data(CPUState *env,
                                      target_phys_addr_t *physical, int *prot,
-                                     target_ulong address, int rw, int is_user)
+                                     target_ulong address, int rw, int mmu_idx)
 {
     unsigned int i;
     uint64_t context;
 
+    int is_user = (mmu_idx == MMU_USER_IDX ||
+                   mmu_idx == MMU_USER_SECONDARY_IDX);
+
     if ((env->lsu & DMMU_E) == 0) { /* DMMU disabled */
         *physical = ultrasparc_truncate_physical(address);
         *prot = PAGE_READ | PAGE_WRITE;
         return 0;
     }
 
-    if (env->tl == 0) {
+    switch(mmu_idx) {
+    case MMU_USER_IDX:
+    case MMU_KERNEL_IDX:
         context = env->dmmu.mmu_primary_context & 0x1fff;
-    } else {
+        break;
+    case MMU_USER_SECONDARY_IDX:
+    case MMU_KERNEL_SECONDARY_IDX:
+        context = env->dmmu.mmu_secondary_context & 0x1fff;
+        break;
+    case MMU_NUCLEUS_IDX:
         context = 0;
+        break;
     }
 
     for (i = 0; i < 64; i++) {
@@ -482,11 +493,14 @@  static int get_physical_address_data(CPUState *env,
 
 static int get_physical_address_code(CPUState *env,
                                      target_phys_addr_t *physical, int *prot,
-                                     target_ulong address, int is_user)
+                                     target_ulong address, int mmu_idx)
 {
     unsigned int i;
     uint64_t context;
 
+    int is_user = (mmu_idx == MMU_USER_IDX ||
+                   mmu_idx == MMU_USER_SECONDARY_IDX);
+
     if ((env->lsu & IMMU_E) == 0 || (env->pstate & PS_RED) != 0) {
         /* IMMU disabled */
         *physical = ultrasparc_truncate_physical(address);
@@ -495,8 +509,10 @@  static int get_physical_address_code(CPUState *env,
     }
 
     if (env->tl == 0) {
+        /* PRIMARY context */
         context = env->dmmu.mmu_primary_context & 0x1fff;
     } else {
+        /* NUCLEUS context */
         context = 0;
     }
 
@@ -535,17 +551,15 @@  static int get_physical_address(CPUState *env, target_phys_addr_t *physical,
                                 target_ulong address, int rw, int mmu_idx,
                                 target_ulong *page_size)
 {
-    int is_user = mmu_idx == MMU_USER_IDX;
-
     /* ??? We treat everything as a small page, then explicitly flush
        everything when an entry is evicted.  */
     *page_size = TARGET_PAGE_SIZE;
     if (rw == 2)
         return get_physical_address_code(env, physical, prot, address,
-                                         is_user);
+                                         mmu_idx);
     else
         return get_physical_address_data(env, physical, prot, address, rw,
-                                         is_user);
+                                         mmu_idx);
 }
 
 /* Perform address translation */
@@ -659,21 +673,27 @@  void dump_mmu(CPUState *env)
 
 
 #if !defined(CONFIG_USER_ONLY)
-target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+target_phys_addr_t cpu_get_phys_page_nofault(CPUState *env, target_ulong addr,
+                                           int mmu_idx)
 {
     target_phys_addr_t phys_addr;
     target_ulong page_size;
     int prot, access_index;
 
     if (get_physical_address(env, &phys_addr, &prot, &access_index, addr, 2,
-                             MMU_KERNEL_IDX, &page_size) != 0)
+                             mmu_idx, &page_size) != 0)
         if (get_physical_address(env, &phys_addr, &prot, &access_index, addr,
-                                 0, MMU_KERNEL_IDX, &page_size) != 0)
+                                 0, mmu_idx, &page_size) != 0)
             return -1;
     if (cpu_get_physical_page_desc(phys_addr) == IO_MEM_UNASSIGNED)
         return -1;
     return phys_addr;
 }
+
+target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+    return cpu_get_phys_page_nofault(env, addr, MMU_KERNEL_IDX);
+}
 #endif
 
 void cpu_reset(CPUSPARCState *env)
diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c
index e048845..0388646 100644
--- a/target-sparc/op_helper.c
+++ b/target-sparc/op_helper.c
@@ -1,9 +1,6 @@ 
 #include "exec.h"
 #include "host-utils.h"
 #include "helper.h"
-#if !defined(CONFIG_USER_ONLY)
-#include "softmmu_exec.h"
-#endif /* !defined(CONFIG_USER_ONLY) */
 
 //#define DEBUG_MMU
 //#define DEBUG_MXCC
@@ -2141,17 +2138,29 @@  uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign)
     switch (asi) {
     case 0x82: // Primary no-fault
     case 0x8a: // Primary no-fault LE
-        if (cpu_get_phys_page_debug(env, addr) == -1ULL) {
+    case 0x83: // Secondary no-fault
+    case 0x8b: // Secondary no-fault LE
+        {
+            /* secondary space access has lowest asi bit equal to 1 */
+            int access_mmu_idx = ( asi & 1 ) ? MMU_KERNEL_IDX
+                                             : MMU_KERNEL_SECONDARY_IDX;
+
+            if (cpu_get_phys_page_nofault(env, addr, access_mmu_idx) == -1ULL) {
 #ifdef DEBUG_ASI
-            dump_asi("read ", last_addr, asi, size, ret);
+                dump_asi("read ", last_addr, asi, size, ret);
 #endif
-            return 0;
+                return 0;
+            }
         }
         // Fall through
     case 0x10: // As if user primary
+    case 0x11: // As if user secondary
     case 0x18: // As if user primary LE
+    case 0x19: // As if user secondary LE
     case 0x80: // Primary
+    case 0x81: // Secondary
     case 0x88: // Primary LE
+    case 0x89: // Secondary LE
     case 0xe2: // UA2007 Primary block init
     case 0xe3: // UA2007 Secondary block init
         if ((asi & 0x80) && (env->pstate & PS_PRIV)) {
@@ -2173,37 +2182,75 @@  uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign)
                     break;
                 }
             } else {
+                /* secondary space access has lowest asi bit equal to 1 */
+                if (asi & 1) {
+                    switch(size) {
+                    case 1:
+                        ret = ldub_kernel_secondary(addr);
+                        break;
+                    case 2:
+                        ret = lduw_kernel_secondary(addr);
+                        break;
+                    case 4:
+                        ret = ldl_kernel_secondary(addr);
+                        break;
+                    default:
+                    case 8:
+                        ret = ldq_kernel_secondary(addr);
+                        break;
+                    }
+                } else {
+                    switch(size) {
+                    case 1:
+                        ret = ldub_kernel(addr);
+                        break;
+                    case 2:
+                        ret = lduw_kernel(addr);
+                        break;
+                    case 4:
+                        ret = ldl_kernel(addr);
+                        break;
+                    default:
+                    case 8:
+                        ret = ldq_kernel(addr);
+                        break;
+                    }
+                }
+            }
+        } else {
+            /* secondary space access has lowest asi bit equal to 1 */
+            if (asi & 1) {
                 switch(size) {
                 case 1:
-                    ret = ldub_kernel(addr);
+                    ret = ldub_user_secondary(addr);
                     break;
                 case 2:
-                    ret = lduw_kernel(addr);
+                    ret = lduw_user_secondary(addr);
                     break;
                 case 4:
-                    ret = ldl_kernel(addr);
+                    ret = ldl_user_secondary(addr);
                     break;
                 default:
                 case 8:
-                    ret = ldq_kernel(addr);
+                    ret = ldq_user_secondary(addr);
+                    break;
+                }
+            } else {
+                switch(size) {
+                case 1:
+                    ret = ldub_user(addr);
+                    break;
+                case 2:
+                    ret = lduw_user(addr);
+                    break;
+                case 4:
+                    ret = ldl_user(addr);
+                    break;
+                default:
+                case 8:
+                    ret = ldq_user(addr);
                     break;
                 }
-            }
-        } else {
-            switch(size) {
-            case 1:
-                ret = ldub_user(addr);
-                break;
-            case 2:
-                ret = lduw_user(addr);
-                break;
-            case 4:
-                ret = ldl_user(addr);
-                break;
-            default:
-            case 8:
-                ret = ldq_user(addr);
-                break;
             }
         }
         break;
@@ -2234,22 +2281,27 @@  uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign)
         //  Only ldda allowed
         raise_exception(TT_ILL_INSN);
         return 0;
-    case 0x83: // Secondary no-fault
-    case 0x8b: // Secondary no-fault LE
-        if (cpu_get_phys_page_debug(env, addr) == -1ULL) {
-#ifdef DEBUG_ASI
-            dump_asi("read ", last_addr, asi, size, ret);
-#endif
-            return 0;
-        }
-        // Fall through
     case 0x04: // Nucleus
     case 0x0c: // Nucleus Little Endian (LE)
-    case 0x11: // As if user secondary
-    case 0x19: // As if user secondary LE
+    {
+        switch(size) {
+        case 1:
+            ret = ldub_nucleus(addr);
+            break;
+        case 2:
+            ret = lduw_nucleus(addr);
+            break;
+        case 4:
+            ret = ldl_nucleus(addr);
+            break;
+        default:
+        case 8:
+            ret = ldq_nucleus(addr);
+            break;
+        }
+        break;
+    }
     case 0x4a: // UPA config
-    case 0x81: // Secondary
-    case 0x89: // Secondary LE
         // XXX
         break;
     case 0x45: // LSU
@@ -2463,9 +2515,13 @@  void helper_st_asi(target_ulong addr, target_ulong val, int asi, int size)
 
     switch(asi) {
     case 0x10: // As if user primary
+    case 0x11: // As if user secondary
     case 0x18: // As if user primary LE
+    case 0x19: // As if user secondary LE
     case 0x80: // Primary
+    case 0x81: // Secondary
     case 0x88: // Primary LE
+    case 0x89: // Secondary LE
     case 0xe2: // UA2007 Primary block init
     case 0xe3: // UA2007 Secondary block init
         if ((asi & 0x80) && (env->pstate & PS_PRIV)) {
@@ -2487,37 +2543,75 @@  void helper_st_asi(target_ulong addr, target_ulong val, int asi, int size)
                     break;
                 }
             } else {
+                /* secondary space access has lowest asi bit equal to 1 */
+                if (asi & 1) {
+                    switch(size) {
+                    case 1:
+                        stb_kernel_secondary(addr, val);
+                        break;
+                    case 2:
+                        stw_kernel_secondary(addr, val);
+                        break;
+                    case 4:
+                        stl_kernel_secondary(addr, val);
+                        break;
+                    case 8:
+                    default:
+                        stq_kernel_secondary(addr, val);
+                        break;
+                    }
+                } else {
+                    switch(size) {
+                    case 1:
+                        stb_kernel(addr, val);
+                        break;
+                    case 2:
+                        stw_kernel(addr, val);
+                        break;
+                    case 4:
+                        stl_kernel(addr, val);
+                        break;
+                    case 8:
+                    default:
+                        stq_kernel(addr, val);
+                        break;
+                    }
+                }
+            }
+        } else {
+            /* secondary space access has lowest asi bit equal to 1 */
+            if (asi & 1) {
                 switch(size) {
                 case 1:
-                    stb_kernel(addr, val);
+                    stb_user_secondary(addr, val);
                     break;
                 case 2:
-                    stw_kernel(addr, val);
+                    stw_user_secondary(addr, val);
                     break;
                 case 4:
-                    stl_kernel(addr, val);
+                    stl_user_secondary(addr, val);
                     break;
                 case 8:
                 default:
-                    stq_kernel(addr, val);
+                    stq_user_secondary(addr, val);
+                    break;
+                }
+            } else {
+                switch(size) {
+                case 1:
+                    stb_user(addr, val);
+                    break;
+                case 2:
+                    stw_user(addr, val);
+                    break;
+                case 4:
+                    stl_user(addr, val);
+                    break;
+                case 8:
+                default:
+                    stq_user(addr, val);
                     break;
                 }
-            }
-        } else {
-            switch(size) {
-            case 1:
-                stb_user(addr, val);
-                break;
-            case 2:
-                stw_user(addr, val);
-                break;
-            case 4:
-                stl_user(addr, val);
-                break;
-            case 8:
-            default:
-                stq_user(addr, val);
-                break;
             }
         }
         break;
@@ -2550,11 +2644,26 @@  void helper_st_asi(target_ulong addr, target_ulong val, int asi, int size)
         return;
     case 0x04: // Nucleus
     case 0x0c: // Nucleus Little Endian (LE)
-    case 0x11: // As if user secondary
-    case 0x19: // As if user secondary LE
+    {
+        switch(size) {
+        case 1:
+            stb_nucleus(addr, val);
+            break;
+        case 2:
+            stw_nucleus(addr, val);
+            break;
+        case 4:
+            stl_nucleus(addr, val);
+            break;
+        default:
+        case 8:
+            stq_nucleus(addr, val);
+            break;
+        }
+        break;
+    }
+
     case 0x4a: // UPA config
-    case 0x81: // Secondary
-    case 0x89: // Secondary LE
         // XXX
         return;
     case 0x45: // LSU