Merge PowerPC 405 MMU model.


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2554 c046a42c-6fe2-441c-8c8c-71466251a162
diff --git a/target-ppc/helper.c b/target-ppc/helper.c
index c835b1e..c541e71 100644
--- a/target-ppc/helper.c
+++ b/target-ppc/helper.c
@@ -549,8 +549,6 @@
             if (unlikely(PPC_MMU(env) == PPC_FLAGS_MMU_SOFT_6xx)) {
                 /* Software TLB search */
                 ret = ppc6xx_tlb_check(env, ctx, eaddr, rw, type);
-            } else if (unlikely(PPC_MMU(env) == PPC_FLAGS_MMU_SOFT_4xx)) {
-                /* XXX: TODO */
             } else {
 #if defined (DEBUG_MMU)
                 if (loglevel > 0) {
@@ -632,6 +630,115 @@
     return ret;
 }
 
+int mmu4xx_get_physical_address (CPUState *env, mmu_ctx_t *ctx,
+                                 uint32_t address, int rw, int access_type)
+{
+    ppcemb_tlb_t *tlb;
+    target_phys_addr_t raddr;
+    target_ulong mask;
+    int i, ret, zsel, zpr;
+            
+    ret = -6;
+    for (i = 0; i < env->nb_tlb; i++) {
+        tlb = &env->tlb[i].tlbe;
+        /* Check valid flag */
+        if (!(tlb->prot & PAGE_VALID)) {
+            if (loglevel)
+                fprintf(logfile, "%s: TLB %d not valid\n", __func__, i);
+            continue;
+        }
+        mask = ~(tlb->size - 1);
+        if (loglevel) {
+            fprintf(logfile, "%s: TLB %d address %08x PID %04x <=> "
+                    "%08x %08x %04x\n",
+                    __func__, i, address, env->spr[SPR_40x_PID],
+                    tlb->EPN, mask, tlb->PID);
+        }
+        /* Check PID */
+        if (tlb->PID != 0 && tlb->PID != env->spr[SPR_40x_PID])
+            continue;
+        /* Check effective address */
+        if ((address & mask) != tlb->EPN)
+            continue;
+        raddr = (tlb->RPN & mask) | (address & ~mask);
+        zsel = (tlb->attr >> 4) & 0xF;
+        zpr = (env->spr[SPR_40x_ZPR] >> (28 - (2 * zsel))) & 0x3;
+        if (loglevel) {
+            fprintf(logfile, "%s: TLB %d zsel %d zpr %d rw %d attr %08x\n",
+                    __func__, i, zsel, zpr, rw, tlb->attr);
+        }
+        if (access_type == ACCESS_CODE) {
+            /* Check execute enable bit */
+            switch (zpr) {
+            case 0x0:
+                if (msr_pr) {
+                    ret = -3;
+                    ctx->prot = 0;
+                    break;
+                }
+                /* No break here */
+            case 0x1:
+            case 0x2:
+                /* Check from TLB entry */
+                if (!(tlb->prot & PAGE_EXEC)) {
+                    ret = -3;
+                } else {
+                    if (tlb->prot & PAGE_WRITE)
+                        ctx->prot = PAGE_READ | PAGE_WRITE;
+                    else
+                        ctx->prot = PAGE_READ;
+                    ret = 0;
+                }
+                break;
+            case 0x3:
+                /* All accesses granted */
+                ret = 0;
+                ctx->prot = PAGE_READ | PAGE_WRITE;
+                break;
+            }
+        } else {
+            switch (zpr) {
+            case 0x0:
+                if (msr_pr) {
+                    ret = -2;
+                    ctx->prot = 0;
+                    break;
+                }
+                /* No break here */
+            case 0x1:
+            case 0x2:
+                /* Check from TLB entry */
+                /* Check write protection bit */
+                if (rw && !(tlb->prot & PAGE_WRITE)) {
+                    ret = -2;
+                } else {
+                    ret = 2;
+                    if (tlb->prot & PAGE_WRITE)
+                        ctx->prot = PAGE_READ | PAGE_WRITE;
+                    else
+                        ctx->prot = PAGE_READ;
+                }
+                break;
+            case 0x3:
+                /* All accesses granted */
+                ret = 2;
+                ctx->prot = PAGE_READ | PAGE_WRITE;
+                break;
+            }
+        }
+        if (ret >= 0) {
+            ctx->raddr = raddr;
+            if (loglevel) {
+                fprintf(logfile, "%s: access granted " ADDRX " => " REGX
+                        " %d\n", __func__, address, ctx->raddr, ctx->prot);
+            }
+            return i;
+        }
+    }
+    
+    return ret;
+}
+
 static int check_physical (CPUState *env, mmu_ctx_t *ctx,
                            target_ulong eaddr, int rw)
 {
@@ -682,13 +789,26 @@
         /* No address translation */
         ret = check_physical(env, ctx, eaddr, rw);
     } else {
-        /* Try to find a BAT */
-        ret = -1;
-        if (check_BATs)
-            ret = get_bat(env, ctx, eaddr, rw, access_type);
-        if (ret < 0) {
-            /* We didn't match any BAT entry */
-            ret = get_segment(env, ctx, eaddr, rw, access_type);
+        switch (PPC_MMU(env)) {
+        case PPC_FLAGS_MMU_32B:
+        case PPC_FLAGS_MMU_SOFT_6xx:
+            /* Try to find a BAT */
+            ret = -1;
+            if (check_BATs)
+                ret = get_bat(env, ctx, eaddr, rw, access_type);
+            if (ret < 0) {
+                /* We didn't match any BAT entry */
+                ret = get_segment(env, ctx, eaddr, rw, access_type);
+            }
+            break;
+        case PPC_FLAGS_MMU_SOFT_4xx:
+            ret = mmu4xx_get_physical_address(env, ctx, eaddr,
+                                              rw, access_type);
+            break;
+        default:
+            /* XXX: TODO */
+            cpu_abort(env, "MMU model not implemented\n");
+            return -1;
         }
     }
 #if 0
@@ -753,7 +873,10 @@
                     error_code = 1 << 18;
                     goto tlb_miss;
                 } else if (unlikely(PPC_MMU(env) == PPC_FLAGS_MMU_SOFT_4xx)) {
-                    /* XXX: TODO */
+                    exception = EXCP_40x_ITLBMISS;
+                    error_code = 0;
+                    env->spr[SPR_40x_DEAR] = address;
+                    env->spr[SPR_40x_ESR] = 0x00000000;
                 } else {
                     error_code = 0x40000000;
                 }
@@ -799,7 +922,13 @@
                     /* Do not alter DAR nor DSISR */
                     goto out;
                 } else if (unlikely(PPC_MMU(env) == PPC_FLAGS_MMU_SOFT_4xx)) {
-                    /* XXX: TODO */
+                    exception = EXCP_40x_DTLBMISS;
+                    error_code = 0;
+                    env->spr[SPR_40x_DEAR] = address;
+                    if (rw)
+                        env->spr[SPR_40x_ESR] = 0x00800000;
+                    else
+                        env->spr[SPR_40x_ESR] = 0x00000000;
                 } else {
                     error_code = 0x40000000;
                 }
@@ -1518,9 +1647,7 @@
         switch (PPC_EXCP(env)) {
         case PPC_FLAGS_EXCP_40x:
             /* DTLBMISS on 4xx */
-            /* XXX: TODO */
-            cpu_abort(env,
-                      "40x DTLBMISS exception is not implemented yet !\n");
+            msr &= ~0xFFFF0000;
             goto store_next;
         case PPC_FLAGS_EXCP_602:
         case PPC_FLAGS_EXCP_603:
@@ -1538,9 +1665,7 @@
         switch (PPC_EXCP(env)) {
         case PPC_FLAGS_EXCP_40x:
             /* ITLBMISS on 4xx */
-            /* XXX: TODO */
-            cpu_abort(env,
-                      "40x ITLBMISS exception is not implemented yet !\n");
+            msr &= ~0xFFFF0000;
             goto store_next;
         case PPC_FLAGS_EXCP_602:
         case PPC_FLAGS_EXCP_603: