]> git.proxmox.com Git - mirror_qemu.git/commitdiff
Code provision for PowerPC 64 MMU model support.
authorj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
Wed, 19 Sep 2007 04:36:02 +0000 (04:36 +0000)
committerj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
Wed, 19 Sep 2007 04:36:02 +0000 (04:36 +0000)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3186 c046a42c-6fe2-441c-8c8c-71466251a162

target-ppc/helper.c

index b88be5c9b3a2c41a6ef625b9888235ffebbc8699..b6fc803b450b16a384ba3cc6b470f70d6e4794c9 100644 (file)
@@ -77,24 +77,62 @@ static inline void pte_invalidate (target_ulong *pte0)
     *pte0 &= ~0x80000000;
 }
 
+#if defined(TARGET_PPC64)
+static inline int pte64_is_valid (target_ulong pte0)
+{
+    return pte0 & 0x0000000000000001ULL ? 1 : 0;
+}
+
+static inline void pte64_invalidate (target_ulong *pte0)
+{
+    *pte0 &= ~0x0000000000000001ULL;
+}
+#endif
+
 #define PTE_PTEM_MASK 0x7FFFFFBF
 #define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B)
+#if defined(TARGET_PPC64)
+#define PTE64_PTEM_MASK 0xFFFFFFFFFFFFFF80ULL
+#define PTE64_CHECK_MASK (TARGET_PAGE_MASK | 0x7F)
+#endif
 
-static int pte_check (mmu_ctx_t *ctx,
-                      target_ulong pte0, target_ulong pte1, int h, int rw)
+static inline int _pte_check (mmu_ctx_t *ctx, int is_64b,
+                              target_ulong pte0, target_ulong pte1,
+                              int h, int rw)
 {
-    int access, ret;
+    target_ulong ptem, mmask;
+    int access, ret, pteh, ptev;
 
     access = 0;
     ret = -1;
     /* Check validity and table match */
-    if (pte_is_valid(pte0) && (h == ((pte0 >> 6) & 1))) {
+#if defined(TARGET_PPC64)
+    if (is_64b) {
+        ptev = pte64_is_valid(pte0);
+        pteh = (pte0 >> 1) & 1;
+    } else
+#endif
+    {
+        ptev = pte_is_valid(pte0);
+        pteh = (pte0 >> 6) & 1;
+    }
+    if (ptev && h == pteh) {
         /* Check vsid & api */
-        if ((pte0 & PTE_PTEM_MASK) == ctx->ptem) {
+#if defined(TARGET_PPC64)
+        if (is_64b) {
+            ptem = pte0 & PTE64_PTEM_MASK;
+            mmask = PTE64_CHECK_MASK;
+        } else
+#endif
+        {
+            ptem = pte0 & PTE_PTEM_MASK;
+            mmask = PTE_CHECK_MASK;
+        }
+        if (ptem == ctx->ptem) {
             if (ctx->raddr != (target_ulong)-1) {
                 /* all matches should have equal RPN, WIMG & PP */
-                if ((ctx->raddr & PTE_CHECK_MASK) != (pte1 & PTE_CHECK_MASK)) {
-                    if (loglevel > 0)
+                if ((ctx->raddr & mmask) != (pte1 & mmask)) {
+                    if (loglevel != 0)
                         fprintf(logfile, "Bad RPN/WIMG/PP\n");
                     return -3;
                 }
@@ -143,6 +181,20 @@ static int pte_check (mmu_ctx_t *ctx,
     return ret;
 }
 
+static int pte32_check (mmu_ctx_t *ctx,
+                        target_ulong pte0, target_ulong pte1, int h, int rw)
+{
+    return _pte_check(ctx, 0, pte0, pte1, h, rw);
+}
+
+#if defined(TARGET_PPC64)
+static int pte64_check (mmu_ctx_t *ctx,
+                        target_ulong pte0, target_ulong pte1, int h, int rw)
+{
+    return _pte_check(ctx, 1, pte0, pte1, h, rw);
+}
+#endif
+
 static int pte_update_flags (mmu_ctx_t *ctx, target_ulong *pte1p,
                              int ret, int rw)
 {
@@ -305,7 +357,7 @@ static int ppc6xx_tlb_check (CPUState *env, mmu_ctx_t *ctx,
                     rw ? 'S' : 'L', access_type == ACCESS_CODE ? 'I' : 'D');
         }
 #endif
-        switch (pte_check(ctx, tlb->pte0, tlb->pte1, 0, rw)) {
+        switch (pte32_check(ctx, tlb->pte0, tlb->pte1, 0, rw)) {
         case -3:
             /* TLB inconsistency */
             return -1;
@@ -440,26 +492,36 @@ static int get_bat (CPUState *env, mmu_ctx_t *ctx,
 }
 
 /* PTE table lookup */
-static int find_pte (mmu_ctx_t *ctx, int h, int rw)
+static inline int _find_pte (mmu_ctx_t *ctx, int is_64b, int h, int rw)
 {
     target_ulong base, pte0, pte1;
     int i, good = -1;
-    int ret;
+    int ret, r;
 
     ret = -1; /* No entry found */
     base = ctx->pg_addr[h];
     for (i = 0; i < 8; i++) {
-        pte0 = ldl_phys(base + (i * 8));
-        pte1 =  ldl_phys(base + (i * 8) + 4);
+#if defined(TARGET_PPC64)
+        if (is_64b) {
+            pte0 = ldq_phys(base + (i * 16));
+            pte1 =  ldq_phys(base + (i * 16) + 8);
+            r = pte64_check(ctx, pte0, pte1, h, rw);
+        } else
+#endif
+        {
+            pte0 = ldl_phys(base + (i * 8));
+            pte1 =  ldl_phys(base + (i * 8) + 4);
+            r = pte32_check(ctx, pte0, pte1, h, rw);
+        }
 #if defined (DEBUG_MMU)
-        if (loglevel > 0) {
+        if (loglevel != 0) {
             fprintf(logfile, "Load pte from 0x" ADDRX " => 0x" ADDRX
                     " 0x" ADDRX " %d %d %d 0x" ADDRX "\n",
                     base + (i * 8), pte0, pte1,
-                    pte0 >> 31, h, (pte0 >> 6) & 1, ctx->ptem);
+                    (int)(pte0 >> 31), h, (int)((pte0 >> 6) & 1), ctx->ptem);
         }
 #endif
-        switch (pte_check(ctx, pte0, pte1, h, rw)) {
+        switch (r) {
         case -3:
             /* PTE inconsistency */
             return -1;
@@ -494,59 +556,183 @@ static int find_pte (mmu_ctx_t *ctx, int h, int rw)
 #endif
         /* Update page flags */
         pte1 = ctx->raddr;
-        if (pte_update_flags(ctx, &pte1, ret, rw) == 1)
-            stl_phys_notdirty(base + (good * 8) + 4, pte1);
+        if (pte_update_flags(ctx, &pte1, ret, rw) == 1) {
+#if defined(TARGET_PPC64)
+            if (is_64b) {
+                stq_phys_notdirty(base + (good * 16) + 8, pte1);
+            } else
+#endif
+            {
+                stl_phys_notdirty(base + (good * 8) + 4, pte1);
+            }
+        }
     }
 
     return ret;
 }
 
+static int find_pte32 (mmu_ctx_t *ctx, int h, int rw)
+{
+    return _find_pte(ctx, 0, h, rw);
+}
+
+#if defined(TARGET_PPC64)
+static int find_pte64 (mmu_ctx_t *ctx, int h, int rw)
+{
+    return _find_pte(ctx, 1, h, rw);
+}
+#endif
+
+static inline int find_pte (CPUState *env, mmu_ctx_t *ctx, int h, int rw)
+{
+#if defined(TARGET_PPC64)
+    if (PPC_MMU(env) == PPC_FLAGS_MMU_64B ||
+        PPC_MMU(env) == PPC_FLAGS_MMU_64BRIDGE)
+        return find_pte64(ctx, h, rw);
+#endif
+
+    return find_pte32(ctx, h, rw);
+}
+
 static inline target_phys_addr_t get_pgaddr (target_phys_addr_t sdr1,
+                                             int sdr_sh,
                                              target_phys_addr_t hash,
                                              target_phys_addr_t mask)
 {
-    return (sdr1 & 0xFFFF0000) | (hash & mask);
+    return (sdr1 & ((target_ulong)(-1ULL) << sdr_sh)) | (hash & mask);
 }
 
+#if defined(TARGET_PPC64)
+static int slb_lookup (CPUState *env, target_ulong eaddr,
+                       target_ulong *vsid, target_ulong *page_mask, int *attr)
+{
+    target_phys_addr_t sr_base;
+    target_ulong mask;
+    uint64_t tmp64;
+    uint32_t tmp;
+    int n, ret;
+    int slb_nr;
+
+    ret = -5;
+    sr_base = env->spr[SPR_ASR];
+    mask = 0x0000000000000000ULL; /* Avoid gcc warning */
+#if 0 /* XXX: Fix this */
+    slb_nr = env->slb_nr;
+#else
+    slb_nr = 32;
+#endif
+    for (n = 0; n < slb_nr; n++) {
+        tmp64 = ldq_phys(sr_base);
+        if (tmp64 & 0x0000000008000000ULL) {
+            /* SLB entry is valid */
+            switch (tmp64 & 0x0000000006000000ULL) {
+            case 0x0000000000000000ULL:
+                /* 256 MB segment */
+                mask = 0xFFFFFFFFF0000000ULL;
+                break;
+            case 0x0000000002000000ULL:
+                /* 1 TB segment */
+                mask = 0xFFFF000000000000ULL;
+                break;
+            case 0x0000000004000000ULL:
+            case 0x0000000006000000ULL:
+                /* Reserved => segment is invalid */
+                continue;
+            }
+            if ((eaddr & mask) == (tmp64 & mask)) {
+                /* SLB match */
+                tmp = ldl_phys(sr_base + 8);
+                *vsid = ((tmp64 << 24) | (tmp >> 8)) & 0x0003FFFFFFFFFFFFULL;
+                *page_mask = ~mask;
+                *attr = tmp & 0xFF;
+                ret = 0;
+                break;
+            }
+        }
+        sr_base += 12;
+    }
+
+    return ret;
+}
+#endif /* defined(TARGET_PPC64) */
+
 /* Perform segment based translation */
 static int get_segment (CPUState *env, mmu_ctx_t *ctx,
                         target_ulong eaddr, int rw, int type)
 {
-    target_phys_addr_t sdr, hash, mask;
-    target_ulong sr, vsid, pgidx;
-    int ret = -1, ret2;
-
-    sr = env->sr[eaddr >> 28];
-#if defined (DEBUG_MMU)
-    if (loglevel > 0) {
-        fprintf(logfile, "Check segment v=0x" ADDRX " %d 0x" ADDRX " nip=0x"
-                ADDRX " lr=0x" ADDRX " ir=%d dr=%d pr=%d %d t=%d\n",
-                eaddr, eaddr >> 28, sr, env->nip,
-                env->lr, msr_ir, msr_dr, msr_pr, rw, type);
-    }
+    target_phys_addr_t sdr, hash, mask, sdr_mask;
+    target_ulong sr, vsid, vsid_mask, pgidx, page_mask;
+#if defined(TARGET_PPC64)
+    int attr;
 #endif
-    ctx->key = (((sr & 0x20000000) && msr_pr == 1) ||
-                ((sr & 0x40000000) && msr_pr == 0)) ? 1 : 0;
-    if ((sr & 0x80000000) == 0) {
+    int ds, nx, vsid_sh, sdr_sh;
+    int ret, ret2;
+
+#if defined(TARGET_PPC64)
+    if (PPC_MMU(env) == PPC_FLAGS_MMU_64B) {
+        ret = slb_lookup(env, eaddr, &vsid, &page_mask, &attr);
+        if (ret < 0)
+            return ret;
+        ctx->key = ((attr & 0x40) && msr_pr == 1) ||
+            ((attr & 0x80) && msr_pr == 0) ? 1 : 0;
+        ds = 0;
+        nx = attr & 0x20 ? 1 : 0;
+        vsid_mask = 0x00003FFFFFFFFF80ULL;
+        vsid_sh = 7;
+        sdr_sh = 18;
+        sdr_mask = 0x3FF80;
+    } else
+#endif /* defined(TARGET_PPC64) */
+    {
+        sr = env->sr[eaddr >> 28];
+        page_mask = 0x0FFFFFFF;
+        ctx->key = (((sr & 0x20000000) && msr_pr == 1) ||
+                    ((sr & 0x40000000) && msr_pr == 0)) ? 1 : 0;
+        ds = sr & 0x80000000 ? 1 : 0;
+        nx = sr & 0x10000000 ? 1 : 0;
+        vsid = sr & 0x00FFFFFF;
+        vsid_mask = 0x01FFFFC0;
+        vsid_sh = 6;
+        sdr_sh = 16;
+        sdr_mask = 0xFFC0;
 #if defined (DEBUG_MMU)
-        if (loglevel > 0)
+        if (loglevel != 0) {
+            fprintf(logfile, "Check segment v=0x" ADDRX " %d 0x" ADDRX
+                    " nip=0x" ADDRX " lr=0x" ADDRX
+                    " ir=%d dr=%d pr=%d %d t=%d\n",
+                    eaddr, (int)(eaddr >> 28), sr, env->nip,
+                    env->lr, msr_ir, msr_dr, msr_pr, rw, type);
+        }
+        if (!ds && loglevel != 0) {
             fprintf(logfile, "pte segment: key=%d n=0x" ADDRX "\n",
                     ctx->key, sr & 0x10000000);
+        }
 #endif
+    }
+    ret = -1;
+    if (!ds) {
         /* Check if instruction fetch is allowed, if needed */
-        if (type != ACCESS_CODE || (sr & 0x10000000) == 0) {
+        if (type != ACCESS_CODE || nx == 0) {
             /* Page address translation */
-            pgidx = (eaddr >> TARGET_PAGE_BITS) & 0xFFFF;
-            vsid = sr & 0x00FFFFFF;
-            hash = ((vsid ^ pgidx) & 0x0007FFFF) << 6;
+            pgidx = (eaddr & page_mask) >> TARGET_PAGE_BITS;
+            hash = ((vsid ^ pgidx) << vsid_sh) & vsid_mask;
             /* Primary table address */
             sdr = env->sdr1;
-            mask = ((sdr & 0x000001FF) << 16) | 0xFFC0;
-            ctx->pg_addr[0] = get_pgaddr(sdr, hash, mask);
+            mask = ((sdr & 0x000001FF) << sdr_sh) | sdr_mask;
+            ctx->pg_addr[0] = get_pgaddr(sdr, sdr_sh, hash, mask);
             /* Secondary table address */
-            hash = (~hash) & 0x01FFFFC0;
-            ctx->pg_addr[1] = get_pgaddr(sdr, hash, mask);
-            ctx->ptem = (vsid << 7) | (pgidx >> 10);
+            hash = (~hash) & vsid_mask;
+            ctx->pg_addr[1] = get_pgaddr(sdr, sdr_sh, hash, mask);
+#if defined(TARGET_PPC64)
+            if (PPC_MMU(env) == PPC_FLAGS_MMU_64B ||
+                PPC_MMU(env) == PPC_FLAGS_MMU_64BRIDGE) {
+                /* Only 5 bits of the page index are used in the AVPN */
+                ctx->ptem = (vsid << 12) | ((pgidx >> 4) & 0x0F80);
+            } else
+#endif
+            {
+                ctx->ptem = (vsid << 7) | (pgidx >> 10);
+            }
             /* Initialize real address with an invalid value */
             ctx->raddr = (target_ulong)-1;
             if (unlikely(PPC_MMU(env) == PPC_FLAGS_MMU_SOFT_6xx)) {
@@ -562,7 +748,7 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx,
                 }
 #endif
                 /* Primary table lookup */
-                ret = find_pte(ctx, 0, rw);
+                ret = find_pte(env, ctx, 0, rw);
                 if (ret < 0) {
                     /* Secondary table lookup */
 #if defined (DEBUG_MMU)
@@ -574,7 +760,7 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx,
                                 (uint32_t)hash, ctx->pg_addr[1]);
                     }
 #endif
-                    ret2 = find_pte(ctx, 1, rw);
+                    ret2 = find_pte(env, ctx, 1, rw);
                     if (ret2 != -1)
                         ret = ret2;
                 }
@@ -835,29 +1021,54 @@ static int check_physical (CPUState *env, mmu_ctx_t *ctx,
     ctx->raddr = eaddr;
     ctx->prot = PAGE_READ;
     ret = 0;
-    if (unlikely(msr_pe != 0 && PPC_MMU(env) == PPC_FLAGS_MMU_403)) {
-        /* 403 family add some particular protections,
-         * using PBL/PBU registers for accesses with no translation.
-         */
-        in_plb =
-            /* Check PLB validity */
-            (env->pb[0] < env->pb[1] &&
-             /* and address in plb area */
-             eaddr >= env->pb[0] && eaddr < env->pb[1]) ||
-            (env->pb[2] < env->pb[3] &&
-             eaddr >= env->pb[2] && eaddr < env->pb[3]) ? 1 : 0;
-        if (in_plb ^ msr_px) {
-            /* Access in protected area */
-            if (rw == 1) {
-                /* Access is not allowed */
-                ret = -2;
+    switch (PPC_MMU(env)) {
+    case PPC_FLAGS_MMU_32B:
+    case PPC_FLAGS_MMU_SOFT_6xx:
+    case PPC_FLAGS_MMU_601:
+    case PPC_FLAGS_MMU_SOFT_4xx:
+        ctx->prot |= PAGE_WRITE;
+        break;
+#if defined(TARGET_PPC64)
+    case PPC_FLAGS_MMU_64B:
+    case PPC_FLAGS_MMU_64BRIDGE:
+#endif
+        /* Real address are 60 bits long */
+        ctx->raddr &= 0x0FFFFFFFFFFFFFFFUL;
+        ctx->prot |= PAGE_WRITE;
+        break;
+    case PPC_FLAGS_MMU_403:
+        if (unlikely(msr_pe != 0)) {
+            /* 403 family add some particular protections,
+             * using PBL/PBU registers for accesses with no translation.
+             */
+            in_plb =
+                /* Check PLB validity */
+                (env->pb[0] < env->pb[1] &&
+                 /* and address in plb area */
+                 eaddr >= env->pb[0] && eaddr < env->pb[1]) ||
+                (env->pb[2] < env->pb[3] &&
+                 eaddr >= env->pb[2] && eaddr < env->pb[3]) ? 1 : 0;
+            if (in_plb ^ msr_px) {
+                /* Access in protected area */
+                if (rw == 1) {
+                    /* Access is not allowed */
+                    ret = -2;
+                }
+            } else {
+                /* Read-write access is allowed */
+                ctx->prot |= PAGE_WRITE;
             }
-        } else {
-            /* Read-write access is allowed */
-            ctx->prot |= PAGE_WRITE;
         }
-    } else {
+    case PPC_FLAGS_MMU_BOOKE:
         ctx->prot |= PAGE_WRITE;
+        break;
+    case PPC_FLAGS_MMU_BOOKE_FSL:
+        /* XXX: TODO */
+        cpu_abort(env, "BookE FSL MMU model not implemented\n");
+        break;
+    default:
+        cpu_abort(env, "Unknown or invalid MMU model\n");
+        return -1;
     }
 
     return ret;