Merge PowerPC 405 MMU model.
authorj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
Sat, 31 Mar 2007 11:33:48 +0000 (11:33 +0000)
committerj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
Sat, 31 Mar 2007 11:33:48 +0000 (11:33 +0000)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2554 c046a42c-6fe2-441c-8c8c-71466251a162

target-ppc/helper.c
target-ppc/op_helper.c

index c835b1e..c541e71 100644 (file)
@@ -549,8 +549,6 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx,
             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 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx,
     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 @@ int get_physical_address (CPUState *env, mmu_ctx_t *ctx, target_ulong eaddr,
         /* 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 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw,
                     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 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw,
                     /* 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 @@ void do_interrupt (CPUState *env)
         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 @@ void do_interrupt (CPUState *env)
         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:
index c21a38a..e1fff7f 100644 (file)
@@ -2365,65 +2365,139 @@ void do_load_6xx_tlb (int is_code)
                      way, is_code, CMP, RPN);
 }
 
+static target_ulong booke_tlb_to_page_size (int size)
+{
+    return 1024 << (2 * size);
+}
+
+static int booke_page_size_to_tlb (target_ulong page_size)
+{
+    int size;
+
+    switch (page_size) {
+    case 0x00000400UL:
+        size = 0x0;
+        break;
+    case 0x00001000UL:
+        size = 0x1;
+        break;
+    case 0x00004000UL:
+        size = 0x2;
+        break;
+    case 0x00010000UL:
+        size = 0x3;
+        break;
+    case 0x00040000UL:
+        size = 0x4;
+        break;
+    case 0x00100000UL:
+        size = 0x5;
+        break;
+    case 0x00400000UL:
+        size = 0x6;
+        break;
+    case 0x01000000UL:
+        size = 0x7;
+        break;
+    case 0x04000000UL:
+        size = 0x8;
+        break;
+    case 0x10000000UL:
+        size = 0x9;
+        break;
+    case 0x40000000UL:
+        size = 0xA;
+        break;
+#if defined (TARGET_PPC64)
+    case 0x000100000000ULL:
+        size = 0xB;
+        break;
+    case 0x000400000000ULL:
+        size = 0xC;
+        break;
+    case 0x001000000000ULL:
+        size = 0xD;
+        break;
+    case 0x004000000000ULL:
+        size = 0xE;
+        break;
+    case 0x010000000000ULL:
+        size = 0xF;
+        break;
+#endif
+    default:
+        size = -1;
+        break;
+    }
+
+    return size;
+}
+
 /* Helpers for 4xx TLB management */
 void do_4xx_tlbia (void)
 {
-#if 0
-    ppc_tlb_t *tlb;
-    target_ulong page, end;
+    ppcemb_tlb_t *tlb;
     int i;
 
     for (i = 0; i < 64; i++) {
-        tlb = &env->tlb[i];
+        tlb = &env->tlb[i].tlbe;
         if (tlb->prot & PAGE_VALID) {
+#if 0
             end = tlb->EPN + tlb->size;
             for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE)
                 tlb_flush_page(env, page);
+#endif
             tlb->prot &= ~PAGE_VALID;
         }
     }
-#endif
+    tlb_flush(env, 1);
 }
 
 void do_4xx_tlbre_lo (void)
 {
-#if 0
-    ppc_tlb_t *tlb;
+    ppcemb_tlb_t *tlb;
+    int size;
 
     T0 &= 0x3F;
-    tlb = &env->tlb[T0];
-    T0 = tlb->stor[0];
-    env->spr[SPR_40x_PID] = tlb->pid;
-#endif
+    tlb = &env->tlb[T0].tlbe;
+    T0 = tlb->EPN;
+    if (tlb->prot & PAGE_VALID)
+        T0 |= 0x400;
+    size = booke_page_size_to_tlb(tlb->size);
+    if (size < 0 || size > 0x7)
+        size = 1;
+    T0 |= size << 7;
+    env->spr[SPR_40x_PID] = tlb->PID;
 }
 
 void do_4xx_tlbre_hi (void)
 {
-#if 0
-    ppc_tlb_t *tlb;
+    ppcemb_tlb_t *tlb;
 
     T0 &= 0x3F;
-    tlb = &env->tlb[T0];
-    T0 = tlb->stor[1];
-#endif
+    tlb = &env->tlb[T0].tlbe;
+    T0 = tlb->RPN;
+    if (tlb->prot & PAGE_EXEC)
+        T0 |= 0x200;
+    if (tlb->prot & PAGE_WRITE)
+        T0 |= 0x100;
 }
 
 static int tlb_4xx_search (target_ulong virtual)
 {
-#if 0
-    ppc_tlb_t *tlb;
+    ppcemb_tlb_t *tlb;
     target_ulong base, mask;
     int i, ret;
 
     /* Default return value is no match */
     ret = -1;
     for (i = 0; i < 64; i++) {
-        tlb = &env->tlb[i];
+        tlb = &env->tlb[i].tlbe;
         /* Check TLB validity */
         if (!(tlb->prot & PAGE_VALID))
             continue;
         /* Check TLB PID vs current PID */
-        if (tlb->pid != 0 && tlb->pid != env->spr[SPR_40x_PID])
+        if (tlb->PID != 0 && tlb->PID != env->spr[SPR_40x_PID])
             continue;
         /* Check TLB address vs virtual address */
         base = tlb->EPN;
@@ -2435,9 +2509,6 @@ static int tlb_4xx_search (target_ulong virtual)
     }
 
     return ret;
-#else
-    return -1;
-#endif
 }
 
 void do_4xx_tlbsx (void)
@@ -2457,47 +2528,44 @@ void do_4xx_tlbsx_ (void)
 
 void do_4xx_tlbwe_lo (void)
 {
-#if 0
-    ppc_tlb_t *tlb;
+    ppcemb_tlb_t *tlb;
     target_ulong page, end;
 
     T0 &= 0x3F;
-    tlb = &env->tlb[T0];
+    tlb = &env->tlb[T0].tlbe;
     /* Invalidate previous TLB (if it's valid) */
     if (tlb->prot & PAGE_VALID) {
         end = tlb->EPN + tlb->size;
         for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE)
             tlb_flush_page(env, page);
     }
-    tlb->size = 1024 << (2 * ((T1 >> 7) & 0x7));
+    tlb->size = booke_tlb_to_page_size((T1 >> 7) & 0x7);
     tlb->EPN = (T1 & 0xFFFFFC00) & ~(tlb->size - 1);
     if (T1 & 0x400)
         tlb->prot |= PAGE_VALID;
     else
         tlb->prot &= ~PAGE_VALID;
-    tlb->pid = env->spr[SPR_BOOKE_PID]; /* PID */
+    tlb->PID = env->spr[SPR_BOOKE_PID]; /* PID */
+    tlb->attr = T1 & 0xFF;
     /* Invalidate new TLB (if valid) */
     if (tlb->prot & PAGE_VALID) {
         end = tlb->EPN + tlb->size;
         for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE)
             tlb_flush_page(env, page);
     }
-#endif
 }
 
 void do_4xx_tlbwe_hi (void)
 {
-#if 0
-    ppc_tlb_t *tlb;
+    ppcemb_tlb_t *tlb;
 
     T0 &= 0x3F;
-    tlb = &env->tlb[T0];
+    tlb = &env->tlb[T0].tlbe;
     tlb->RPN = T1 & 0xFFFFFC00;
     tlb->prot = PAGE_READ;
     if (T1 & 0x200)
         tlb->prot |= PAGE_EXEC;
     if (T1 & 0x100)
         tlb->prot |= PAGE_WRITE;
-#endif
 }
 #endif /* !CONFIG_USER_ONLY */