diff mbox

[07/48] target-ppc: Disentangle get_segment()

Message ID 1363084310-4115-8-git-send-email-david@gibson.dropbear.id.au
State New
Headers show

Commit Message

David Gibson March 12, 2013, 10:31 a.m. UTC
The poorly named get_segment() function handles most of the address
translation logic for hash-based MMUs.  It has many ugly conditionals on
whether the MMU is 32-bit or 64-bit.

This patch splits the function into 32 and 64-bit versions, using the
switch on mmu_type that's already in the caller
(get_physical_address()) to select the right one.  Most of the
original function remains in mmu_helper.c to support the 6xx software
loaded TLB implementations (cleaning those up is a project for another
day).

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 target-ppc/mmu-hash32.c |  149 ++++++++++++++++++++++++++++++++++++++++++++++-
 target-ppc/mmu-hash32.h |    4 +-
 target-ppc/mmu-hash64.c |   92 ++++++++++++++++++++++++++++-
 target-ppc/mmu-hash64.h |    5 +-
 target-ppc/mmu_helper.c |  141 +++++++++++++-------------------------------
 5 files changed, 280 insertions(+), 111 deletions(-)
diff mbox

Patch

diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c
index f852e5c..4bae72a 100644
--- a/target-ppc/mmu-hash32.c
+++ b/target-ppc/mmu-hash32.c
@@ -85,8 +85,8 @@  static int pte_check_hash32(mmu_ctx_t *ctx, target_ulong pte0,
 }
 
 /* PTE table lookup */
-int find_pte32(CPUPPCState *env, mmu_ctx_t *ctx, int h,
-               int rw, int type, int target_page_bits)
+static int find_pte32(CPUPPCState *env, mmu_ctx_t *ctx, int h,
+                      int rw, int type, int target_page_bits)
 {
     hwaddr pteg_off;
     target_ulong pte0, pte1;
@@ -157,3 +157,148 @@  int find_pte32(CPUPPCState *env, mmu_ctx_t *ctx, int h,
     }
     return ret;
 }
+
+int get_segment32(CPUPPCState *env, mmu_ctx_t *ctx,
+                  target_ulong eaddr, int rw, int type)
+{
+    hwaddr hash;
+    target_ulong vsid;
+    int ds, pr, target_page_bits;
+    int ret, ret2;
+    target_ulong sr, pgidx;
+
+    pr = msr_pr;
+    ctx->eaddr = eaddr;
+
+    sr = env->sr[eaddr >> 28];
+    ctx->key = (((sr & 0x20000000) && (pr != 0)) ||
+                ((sr & 0x40000000) && (pr == 0))) ? 1 : 0;
+    ds = sr & 0x80000000 ? 1 : 0;
+    ctx->nx = sr & 0x10000000 ? 1 : 0;
+    vsid = sr & 0x00FFFFFF;
+    target_page_bits = TARGET_PAGE_BITS;
+    LOG_MMU("Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip="
+            TARGET_FMT_lx " lr=" TARGET_FMT_lx
+            " ir=%d dr=%d pr=%d %d t=%d\n",
+            eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir,
+            (int)msr_dr, pr != 0 ? 1 : 0, rw, type);
+    pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits;
+    hash = vsid ^ pgidx;
+    ctx->ptem = (vsid << 7) | (pgidx >> 10);
+
+    LOG_MMU("pte segment: key=%d ds %d nx %d vsid " TARGET_FMT_lx "\n",
+            ctx->key, ds, ctx->nx, vsid);
+    ret = -1;
+    if (!ds) {
+        /* Check if instruction fetch is allowed, if needed */
+        if (type != ACCESS_CODE || ctx->nx == 0) {
+            /* Page address translation */
+            LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx
+                    " hash " TARGET_FMT_plx "\n",
+                    env->htab_base, env->htab_mask, hash);
+            ctx->hash[0] = hash;
+            ctx->hash[1] = ~hash;
+
+            /* Initialize real address with an invalid value */
+            ctx->raddr = (hwaddr)-1ULL;
+            LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
+                    " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx
+                    " hash=" TARGET_FMT_plx "\n",
+                    env->htab_base, env->htab_mask, vsid, ctx->ptem,
+                    ctx->hash[0]);
+            /* Primary table lookup */
+            ret = find_pte32(env, ctx, 0, rw, type, target_page_bits);
+            if (ret < 0) {
+                /* Secondary table lookup */
+                LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
+                        " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx
+                        " hash=" TARGET_FMT_plx "\n", env->htab_base,
+                        env->htab_mask, vsid, ctx->ptem, ctx->hash[1]);
+                ret2 = find_pte32(env, ctx, 1, rw, type,
+                                  target_page_bits);
+                if (ret2 != -1) {
+                    ret = ret2;
+                }
+            }
+#if defined(DUMP_PAGE_TABLES)
+            if (qemu_log_enabled()) {
+                hwaddr curaddr;
+                uint32_t a0, a1, a2, a3;
+
+                qemu_log("Page table: " TARGET_FMT_plx " len " TARGET_FMT_plx
+                         "\n", sdr, mask + 0x80);
+                for (curaddr = sdr; curaddr < (sdr + mask + 0x80);
+                     curaddr += 16) {
+                    a0 = ldl_phys(curaddr);
+                    a1 = ldl_phys(curaddr + 4);
+                    a2 = ldl_phys(curaddr + 8);
+                    a3 = ldl_phys(curaddr + 12);
+                    if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) {
+                        qemu_log(TARGET_FMT_plx ": %08x %08x %08x %08x\n",
+                                 curaddr, a0, a1, a2, a3);
+                    }
+                }
+            }
+#endif
+        } else {
+            LOG_MMU("No access allowed\n");
+            ret = -3;
+        }
+    } else {
+        target_ulong sr;
+
+        LOG_MMU("direct store...\n");
+        /* Direct-store segment : absolutely *BUGGY* for now */
+
+        /* Direct-store implies a 32-bit MMU.
+         * Check the Segment Register's bus unit ID (BUID).
+         */
+        sr = env->sr[eaddr >> 28];
+        if ((sr & 0x1FF00000) >> 20 == 0x07f) {
+            /* Memory-forced I/O controller interface access */
+            /* If T=1 and BUID=x'07F', the 601 performs a memory access
+             * to SR[28-31] LA[4-31], bypassing all protection mechanisms.
+             */
+            ctx->raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF);
+            ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+            return 0;
+        }
+
+        switch (type) {
+        case ACCESS_INT:
+            /* Integer load/store : only access allowed */
+            break;
+        case ACCESS_CODE:
+            /* No code fetch is allowed in direct-store areas */
+            return -4;
+        case ACCESS_FLOAT:
+            /* Floating point load/store */
+            return -4;
+        case ACCESS_RES:
+            /* lwarx, ldarx or srwcx. */
+            return -4;
+        case ACCESS_CACHE:
+            /* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */
+            /* Should make the instruction do no-op.
+             * As it already do no-op, it's quite easy :-)
+             */
+            ctx->raddr = eaddr;
+            return 0;
+        case ACCESS_EXT:
+            /* eciwx or ecowx */
+            return -4;
+        default:
+            qemu_log("ERROR: instruction should not need "
+                        "address translation\n");
+            return -4;
+        }
+        if ((rw == 1 || ctx->key != 1) && (rw == 0 || ctx->key != 0)) {
+            ctx->raddr = eaddr;
+            ret = 2;
+        } else {
+            ret = -2;
+        }
+    }
+
+    return ret;
+}
diff --git a/target-ppc/mmu-hash32.h b/target-ppc/mmu-hash32.h
index 6bf8f92..6f9a0c2 100644
--- a/target-ppc/mmu-hash32.h
+++ b/target-ppc/mmu-hash32.h
@@ -4,8 +4,8 @@ 
 #ifndef CONFIG_USER_ONLY
 
 int pte32_is_valid(target_ulong pte0);
-int find_pte32(CPUPPCState *env, mmu_ctx_t *ctx, int h,
-               int rw, int type, int target_page_bits);
+int get_segment32(CPUPPCState *env, mmu_ctx_t *ctx,
+                  target_ulong eaddr, int rw, int type);
 
 #endif /* CONFIG_USER_ONLY */
 
diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c
index a525bd5..23eb480 100644
--- a/target-ppc/mmu-hash64.c
+++ b/target-ppc/mmu-hash64.c
@@ -44,7 +44,7 @@ 
  * SLB handling
  */
 
-ppc_slb_t *slb_lookup(CPUPPCState *env, target_ulong eaddr)
+static ppc_slb_t *slb_lookup(CPUPPCState *env, target_ulong eaddr)
 {
     uint64_t esid_256M, esid_1T;
     int n;
@@ -276,8 +276,8 @@  static int pte64_check(mmu_ctx_t *ctx, target_ulong pte0,
 }
 
 /* PTE table lookup */
-int find_pte64(CPUPPCState *env, mmu_ctx_t *ctx, int h,
-               int rw, int type, int target_page_bits)
+static int find_pte64(CPUPPCState *env, mmu_ctx_t *ctx, int h,
+                      int rw, int type, int target_page_bits)
 {
     hwaddr pteg_off;
     target_ulong pte0, pte1;
@@ -349,3 +349,89 @@  int find_pte64(CPUPPCState *env, mmu_ctx_t *ctx, int h,
     }
     return ret;
 }
+
+int get_segment64(CPUPPCState *env, mmu_ctx_t *ctx,
+                  target_ulong eaddr, int rw, int type)
+{
+    hwaddr hash;
+    target_ulong vsid;
+    int pr, target_page_bits;
+    int ret, ret2;
+
+    pr = msr_pr;
+    ctx->eaddr = eaddr;
+    ppc_slb_t *slb;
+    target_ulong pageaddr;
+    int segment_bits;
+
+    LOG_MMU("Check SLBs\n");
+    slb = slb_lookup(env, eaddr);
+    if (!slb) {
+        return -5;
+    }
+
+    if (slb->vsid & SLB_VSID_B) {
+        vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT_1T;
+        segment_bits = 40;
+    } else {
+        vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT;
+        segment_bits = 28;
+    }
+
+    target_page_bits = (slb->vsid & SLB_VSID_L)
+        ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS;
+    ctx->key = !!(pr ? (slb->vsid & SLB_VSID_KP)
+                  : (slb->vsid & SLB_VSID_KS));
+    ctx->nx = !!(slb->vsid & SLB_VSID_N);
+
+    pageaddr = eaddr & ((1ULL << segment_bits)
+                            - (1ULL << target_page_bits));
+    if (slb->vsid & SLB_VSID_B) {
+        hash = vsid ^ (vsid << 25) ^ (pageaddr >> target_page_bits);
+    } else {
+        hash = vsid ^ (pageaddr >> target_page_bits);
+    }
+    /* Only 5 bits of the page index are used in the AVPN */
+    ctx->ptem = (slb->vsid & SLB_VSID_PTEM) |
+        ((pageaddr >> 16) & ((1ULL << segment_bits) - 0x80));
+
+    LOG_MMU("pte segment: key=%d nx %d vsid " TARGET_FMT_lx "\n",
+            ctx->key, ctx->nx, vsid);
+    ret = -1;
+
+    /* Check if instruction fetch is allowed, if needed */
+    if (type != ACCESS_CODE || ctx->nx == 0) {
+        /* Page address translation */
+        LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx
+                " hash " TARGET_FMT_plx "\n",
+                env->htab_base, env->htab_mask, hash);
+        ctx->hash[0] = hash;
+        ctx->hash[1] = ~hash;
+
+        /* Initialize real address with an invalid value */
+        ctx->raddr = (hwaddr)-1ULL;
+        LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
+                " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx
+                " hash=" TARGET_FMT_plx "\n",
+                env->htab_base, env->htab_mask, vsid, ctx->ptem,
+                ctx->hash[0]);
+        /* Primary table lookup */
+        ret = find_pte64(env, ctx, 0, rw, type, target_page_bits);
+        if (ret < 0) {
+            /* Secondary table lookup */
+            LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
+                    " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx
+                    " hash=" TARGET_FMT_plx "\n", env->htab_base,
+                    env->htab_mask, vsid, ctx->ptem, ctx->hash[1]);
+            ret2 = find_pte64(env, ctx, 1, rw, type, target_page_bits);
+            if (ret2 != -1) {
+                ret = ret2;
+            }
+        }
+    } else {
+        LOG_MMU("No access allowed\n");
+        ret = -3;
+    }
+
+    return ret;
+}
diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h
index 7b9713d..690c1d8 100644
--- a/target-ppc/mmu-hash64.h
+++ b/target-ppc/mmu-hash64.h
@@ -4,11 +4,10 @@ 
 #ifndef CONFIG_USER_ONLY
 
 #ifdef TARGET_PPC64
-ppc_slb_t *slb_lookup(CPUPPCState *env, target_ulong eaddr);
 void dump_slb(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env);
 int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs);
-int find_pte64(CPUPPCState *env, mmu_ctx_t *ctx, int h,
-               int rw, int type, int target_page_bits);
+int get_segment64(CPUPPCState *env, mmu_ctx_t *ctx,
+                  target_ulong eaddr, int rw, int type);
 #endif
 
 #endif /* CONFIG_USER_ONLY */
diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c
index 1301391..db5c15a 100644
--- a/target-ppc/mmu_helper.c
+++ b/target-ppc/mmu_helper.c
@@ -507,87 +507,35 @@  hwaddr get_pteg_offset(CPUPPCState *env, hwaddr hash, int pte_size)
     return (hash * pte_size * 8) & env->htab_mask;
 }
 
-static inline int find_pte(CPUPPCState *env, mmu_ctx_t *ctx, int h, int rw,
-                           int type, int target_page_bits)
-{
-#if defined(TARGET_PPC64)
-    if (env->mmu_model & POWERPC_MMU_64) {
-        return find_pte64(env, ctx, h, rw, type, target_page_bits);
-    }
-#endif
-
-    return find_pte32(env, ctx, h, rw, type, target_page_bits);
-}
-
 /* Perform segment based translation */
-static inline int get_segment(CPUPPCState *env, mmu_ctx_t *ctx,
-                              target_ulong eaddr, int rw, int type)
+static inline int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx,
+                                      target_ulong eaddr, int rw, int type)
 {
     hwaddr hash;
     target_ulong vsid;
     int ds, pr, target_page_bits;
-    int ret, ret2;
+    int ret;
+    target_ulong sr, pgidx;
 
     pr = msr_pr;
     ctx->eaddr = eaddr;
-#if defined(TARGET_PPC64)
-    if (env->mmu_model & POWERPC_MMU_64) {
-        ppc_slb_t *slb;
-        target_ulong pageaddr;
-        int segment_bits;
-
-        LOG_MMU("Check SLBs\n");
-        slb = slb_lookup(env, eaddr);
-        if (!slb) {
-            return -5;
-        }
-
-        if (slb->vsid & SLB_VSID_B) {
-            vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT_1T;
-            segment_bits = 40;
-        } else {
-            vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT;
-            segment_bits = 28;
-        }
-
-        target_page_bits = (slb->vsid & SLB_VSID_L)
-            ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS;
-        ctx->key = !!(pr ? (slb->vsid & SLB_VSID_KP)
-                      : (slb->vsid & SLB_VSID_KS));
-        ds = 0;
-        ctx->nx = !!(slb->vsid & SLB_VSID_N);
 
-        pageaddr = eaddr & ((1ULL << segment_bits)
-                            - (1ULL << target_page_bits));
-        if (slb->vsid & SLB_VSID_B) {
-            hash = vsid ^ (vsid << 25) ^ (pageaddr >> target_page_bits);
-        } else {
-            hash = vsid ^ (pageaddr >> target_page_bits);
-        }
-        /* Only 5 bits of the page index are used in the AVPN */
-        ctx->ptem = (slb->vsid & SLB_VSID_PTEM) |
-            ((pageaddr >> 16) & ((1ULL << segment_bits) - 0x80));
-    } else
-#endif /* defined(TARGET_PPC64) */
-    {
-        target_ulong sr, pgidx;
+    sr = env->sr[eaddr >> 28];
+    ctx->key = (((sr & 0x20000000) && (pr != 0)) ||
+                ((sr & 0x40000000) && (pr == 0))) ? 1 : 0;
+    ds = sr & 0x80000000 ? 1 : 0;
+    ctx->nx = sr & 0x10000000 ? 1 : 0;
+    vsid = sr & 0x00FFFFFF;
+    target_page_bits = TARGET_PAGE_BITS;
+    LOG_MMU("Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip="
+            TARGET_FMT_lx " lr=" TARGET_FMT_lx
+            " ir=%d dr=%d pr=%d %d t=%d\n",
+            eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir,
+            (int)msr_dr, pr != 0 ? 1 : 0, rw, type);
+    pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits;
+    hash = vsid ^ pgidx;
+    ctx->ptem = (vsid << 7) | (pgidx >> 10);
 
-        sr = env->sr[eaddr >> 28];
-        ctx->key = (((sr & 0x20000000) && (pr != 0)) ||
-                    ((sr & 0x40000000) && (pr == 0))) ? 1 : 0;
-        ds = sr & 0x80000000 ? 1 : 0;
-        ctx->nx = sr & 0x10000000 ? 1 : 0;
-        vsid = sr & 0x00FFFFFF;
-        target_page_bits = TARGET_PAGE_BITS;
-        LOG_MMU("Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip="
-                TARGET_FMT_lx " lr=" TARGET_FMT_lx
-                " ir=%d dr=%d pr=%d %d t=%d\n",
-                eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir,
-                (int)msr_dr, pr != 0 ? 1 : 0, rw, type);
-        pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits;
-        hash = vsid ^ pgidx;
-        ctx->ptem = (vsid << 7) | (pgidx >> 10);
-    }
     LOG_MMU("pte segment: key=%d ds %d nx %d vsid " TARGET_FMT_lx "\n",
             ctx->key, ds, ctx->nx, vsid);
     ret = -1;
@@ -603,31 +551,8 @@  static inline int get_segment(CPUPPCState *env, mmu_ctx_t *ctx,
 
             /* Initialize real address with an invalid value */
             ctx->raddr = (hwaddr)-1ULL;
-            if (unlikely(env->mmu_model == POWERPC_MMU_SOFT_6xx ||
-                         env->mmu_model == POWERPC_MMU_SOFT_74xx)) {
-                /* Software TLB search */
-                ret = ppc6xx_tlb_check(env, ctx, eaddr, rw, type);
-            } else {
-                LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
-                        " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx
-                        " hash=" TARGET_FMT_plx "\n",
-                        env->htab_base, env->htab_mask, vsid, ctx->ptem,
-                        ctx->hash[0]);
-                /* Primary table lookup */
-                ret = find_pte(env, ctx, 0, rw, type, target_page_bits);
-                if (ret < 0) {
-                    /* Secondary table lookup */
-                    LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
-                            " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx
-                            " hash=" TARGET_FMT_plx "\n", env->htab_base,
-                            env->htab_mask, vsid, ctx->ptem, ctx->hash[1]);
-                    ret2 = find_pte(env, ctx, 1, rw, type,
-                                    target_page_bits);
-                    if (ret2 != -1) {
-                        ret = ret2;
-                    }
-                }
-            }
+            /* Software TLB search */
+            ret = ppc6xx_tlb_check(env, ctx, eaddr, rw, type);
 #if defined(DUMP_PAGE_TABLES)
             if (qemu_log_enabled()) {
                 hwaddr curaddr;
@@ -1415,22 +1340,36 @@  static int get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx,
         switch (env->mmu_model) {
         case POWERPC_MMU_32B:
         case POWERPC_MMU_601:
+            /* Try to find a BAT */
+            if (env->nb_BATs != 0) {
+                ret = get_bat(env, ctx, eaddr, rw, access_type);
+            }
+            if (ret < 0) {
+                /* We didn't match any BAT entry or don't have BATs */
+                ret = get_segment32(env, ctx, eaddr, rw, access_type);
+            }
+            break;
+
         case POWERPC_MMU_SOFT_6xx:
         case POWERPC_MMU_SOFT_74xx:
             /* Try to find a BAT */
             if (env->nb_BATs != 0) {
                 ret = get_bat(env, ctx, eaddr, rw, access_type);
             }
+            if (ret < 0) {
+                /* We didn't match any BAT entry or don't have BATs */
+                ret = get_segment_6xx_tlb(env, ctx, eaddr, rw, access_type);
+            }
+            break;
+
 #if defined(TARGET_PPC64)
         case POWERPC_MMU_64B:
         case POWERPC_MMU_2_06:
         case POWERPC_MMU_2_06d:
-#endif
-            if (ret < 0) {
-                /* We didn't match any BAT entry or don't have BATs */
-                ret = get_segment(env, ctx, eaddr, rw, access_type);
-            }
+            ret = get_segment64(env, ctx, eaddr, rw, access_type);
             break;
+#endif
+
         case POWERPC_MMU_SOFT_4xx:
         case POWERPC_MMU_SOFT_4xx_Z:
             ret = mmu40x_get_physical_address(env, ctx, eaddr,