Support for PowerPC BookE exception model.
authorj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
Sat, 31 Mar 2007 12:57:57 +0000 (12:57 +0000)
committerj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
Sat, 31 Mar 2007 12:57:57 +0000 (12:57 +0000)
No need to requeue timer exceptions.
Fix nip saving for 64 bits PowerPC.

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2556 c046a42c-6fe2-441c-8c8c-71466251a162

target-ppc/helper.c

index c541e71..1d973dc 100644 (file)
@@ -1340,11 +1340,12 @@ void ppc_store_msr_32 (CPUPPCState *env, uint32_t value)
 void do_compute_hflags (CPUPPCState *env)
 {
     /* Compute current hflags */
-    env->hflags = (msr_pr << MSR_PR) | (msr_le << MSR_LE) |
-        (msr_fp << MSR_FP) | (msr_fe0 << MSR_FE0) | (msr_fe1 << MSR_FE1) |
-        (msr_vr << MSR_VR) | (msr_ap << MSR_AP) | (msr_sa << MSR_SA) |
-        (msr_se << MSR_SE) | (msr_be << MSR_BE);
+    env->hflags = (msr_cm << MSR_CM) | (msr_vr << MSR_VR) |
+        (msr_ap << MSR_AP) | (msr_sa << MSR_SA) | (msr_pr << MSR_PR) |
+        (msr_fp << MSR_FP) | (msr_fe0 << MSR_FE0) | (msr_se << MSR_SE) |
+        (msr_be << MSR_BE) | (msr_fe1 << MSR_FE1) | (msr_le << MSR_LE);
 #if defined (TARGET_PPC64)
+    /* No care here: PowerPC 64 MSR_SF means the same as MSR_CM for BookE */
     env->hflags |= (msr_sf << (MSR_SF - 32)) | (msr_hv << (MSR_HV - 32));
 #endif
 }
@@ -1374,14 +1375,16 @@ static void dump_syscall(CPUState *env)
 
 void do_interrupt (CPUState *env)
 {
-    target_ulong msr, *srr_0, *srr_1;
-    int excp;
+    target_ulong msr, *srr_0, *srr_1, *asrr_0, *asrr_1;
+    int excp, idx;
 
     excp = env->exception_index;
     msr = do_load_msr(env);
     /* The default is to use SRR0 & SRR1 to save the exception context */
     srr_0 = &env->spr[SPR_SRR0];
     srr_1 = &env->spr[SPR_SRR1];
+    asrr_0 = NULL;
+    asrr_1 = NULL;
 #if defined (DEBUG_EXCEPTIONS)
     if ((excp == EXCP_PROGRAM || excp == EXCP_DSI) && msr_pr == 1) {
         if (loglevel != 0) {
@@ -1397,26 +1400,44 @@ void do_interrupt (CPUState *env)
                 env->nip, excp, env->error_code);
     }
     msr_pow = 0;
+    idx = -1;
     /* Generate informations in save/restore registers */
     switch (excp) {
     /* Generic PowerPC exceptions */
     case EXCP_RESET: /* 0x0100 */
-        if (PPC_EXCP(env) != PPC_FLAGS_EXCP_40x) {
+        switch (PPC_EXCP(env)) {
+        case PPC_FLAGS_EXCP_40x:
+            srr_0 = &env->spr[SPR_40x_SRR2];
+            srr_1 = &env->spr[SPR_40x_SRR3];
+            break;
+        case PPC_FLAGS_EXCP_BOOKE:
+            idx = 0;
+            srr_0 = &env->spr[SPR_BOOKE_CSRR0];
+            srr_1 = &env->spr[SPR_BOOKE_CSRR1];
+            break;
+        default:
             if (msr_ip)
                 excp += 0xFFC00;
             excp |= 0xFFC00000;
-        } else {
-            srr_0 = &env->spr[SPR_40x_SRR2];
-            srr_1 = &env->spr[SPR_40x_SRR3];
+            break;
         }
         goto store_next;
     case EXCP_MACHINE_CHECK: /* 0x0200 */
-        if (msr_me == 0) {
-            cpu_abort(env, "Machine check exception while not allowed\n");
-        }
-        if (unlikely(PPC_EXCP(env) == PPC_FLAGS_EXCP_40x)) {
+        switch (PPC_EXCP(env)) {
+        case PPC_FLAGS_EXCP_40x:
             srr_0 = &env->spr[SPR_40x_SRR2];
             srr_1 = &env->spr[SPR_40x_SRR3];
+            break;
+        case PPC_FLAGS_EXCP_BOOKE:
+            idx = 1;
+            srr_0 = &env->spr[SPR_BOOKE_MCSRR0];
+            srr_1 = &env->spr[SPR_BOOKE_MCSRR1];
+            asrr_0 = &env->spr[SPR_BOOKE_CSRR0];
+            asrr_1 = &env->spr[SPR_BOOKE_CSRR1];
+            msr_ce = 0;
+            break;
+        default:
+            break;
         }
         msr_me = 0;
         break;
@@ -1425,6 +1446,7 @@ void do_interrupt (CPUState *env)
         /* data location address has been stored
          * when the fault has been detected
          */
+        idx = 2;
         msr &= ~0xFFFF0000;
 #if defined (DEBUG_EXCEPTIONS)
         if (loglevel) {
@@ -1438,6 +1460,7 @@ void do_interrupt (CPUState *env)
         goto store_next;
     case EXCP_ISI: /* 0x0400 */
         /* Store exception cause */
+        idx = 3;
         msr &= ~0xFFFF0000;
         msr |= env->error_code;
 #if defined (DEBUG_EXCEPTIONS)
@@ -1448,20 +1471,12 @@ void do_interrupt (CPUState *env)
 #endif
         goto store_next;
     case EXCP_EXTERNAL: /* 0x0500 */
-        if (msr_ee == 0) {
-#if defined (DEBUG_EXCEPTIONS)
-            if (loglevel > 0) {
-                fprintf(logfile, "Skipping hardware interrupt\n");
-            }
-#endif
-            /* Requeue it */
-            env->interrupt_request |= CPU_INTERRUPT_HARD;
-            return;
-        }
+        idx = 4;
         goto store_next;
     case EXCP_ALIGN: /* 0x0600 */
         if (likely(PPC_EXCP(env) != PPC_FLAGS_EXCP_601)) {
             /* Store exception cause */
+            idx = 5;
             /* Get rS/rD and rA from faulting opcode */
             env->spr[SPR_DSISR] |=
                 (ldl_code((env->nip - 4)) & 0x03FF0000) >> 16;
@@ -1476,6 +1491,7 @@ void do_interrupt (CPUState *env)
         }
         goto store_current;
     case EXCP_PROGRAM: /* 0x0700 */
+        idx = 6;
         msr &= ~0xFFFF0000;
         switch (env->error_code & ~0xF) {
         case EXCP_FP:
@@ -1501,6 +1517,7 @@ void do_interrupt (CPUState *env)
             msr |= 0x00040000;
             break;
         case EXCP_TRAP:
+            idx = 15;
             msr |= 0x00020000;
             break;
         default:
@@ -1510,18 +1527,13 @@ void do_interrupt (CPUState *env)
         msr |= 0x00010000;
         goto store_current;
     case EXCP_NO_FP: /* 0x0800 */
+        idx = 7;
         msr &= ~0xFFFF0000;
         goto store_current;
     case EXCP_DECR:
-        if (msr_ee == 0) {
-#if 1
-            /* Requeue it */
-            env->interrupt_request |= CPU_INTERRUPT_TIMER;
-#endif
-            return;
-        }
         goto store_next;
     case EXCP_SYSCALL: /* 0x0C00 */
+        idx = 8;
         /* NOTE: this is a temporary hack to support graphics OSI
            calls from the MOL driver */
         if (env->gpr[3] == 0x113724fa && env->gpr[4] == 0x77810f9b &&
@@ -1557,13 +1569,6 @@ void do_interrupt (CPUState *env)
                   "Instruction segment exception is not implemented yet !\n");
         goto store_next;
     case EXCP_HDECR: /* 0x0980 */
-        if (msr_ee == 0) {
-#if 1
-            /* Requeue it */
-            env->interrupt_request |= CPU_INTERRUPT_TIMER;
-#endif
-            return;
-        }
         /* XXX: TODO */
         cpu_abort(env, "Hypervisor decrementer exception is not implemented "
                   "yet !\n");
@@ -1581,6 +1586,7 @@ void do_interrupt (CPUState *env)
         }
         return;
     case 0x0F20:
+        idx = 9;
         switch (PPC_EXCP(env)) {
         case PPC_FLAGS_EXCP_40x:
             /* APU unavailable on 405 */
@@ -1600,11 +1606,13 @@ void do_interrupt (CPUState *env)
         }
         return;
     case 0x1000:
+        idx = 10;
         switch (PPC_EXCP(env)) {
         case PPC_FLAGS_EXCP_40x:
             /* PIT on 4xx */
-            /* XXX: TODO */
-            cpu_abort(env, "40x PIT exception is not implemented yet !\n");
+            msr &= ~0xFFFF0000;
+            if (loglevel != 0)
+                fprintf(logfile, "PIT exception\n");
             goto store_next;
         case PPC_FLAGS_EXCP_602:
         case PPC_FLAGS_EXCP_603:
@@ -1619,11 +1627,13 @@ void do_interrupt (CPUState *env)
         }
         return;
     case 0x1010:
+        idx = 11;
         switch (PPC_EXCP(env)) {
         case PPC_FLAGS_EXCP_40x:
             /* FIT on 4xx */
-            /* XXX: TODO */
-            cpu_abort(env, "40x FIT exception is not implemented yet !\n");
+            msr &= ~0xFFFF0000;
+            if (loglevel != 0)
+                fprintf(logfile, "FIT exception\n");
             goto store_next;
         default:
             cpu_abort(env, "Invalid exception 0x1010 !\n");
@@ -1631,19 +1641,25 @@ void do_interrupt (CPUState *env)
         }
         return;
     case 0x1020:
+        idx = 12;
         switch (PPC_EXCP(env)) {
         case PPC_FLAGS_EXCP_40x:
             /* Watchdog on 4xx */
-            /* XXX: TODO */
-            cpu_abort(env,
-                      "40x watchdog exception is not implemented yet !\n");
+            msr &= ~0xFFFF0000;
+            if (loglevel != 0)
+                fprintf(logfile, "WDT exception\n");
             goto store_next;
+        case PPC_FLAGS_EXCP_BOOKE:
+            srr_0 = &env->spr[SPR_BOOKE_CSRR0];
+            srr_1 = &env->spr[SPR_BOOKE_CSRR1];
+            break;
         default:
             cpu_abort(env, "Invalid exception 0x1020 !\n");
             break;
         }
         return;
     case 0x1100:
+        idx = 13;
         switch (PPC_EXCP(env)) {
         case PPC_FLAGS_EXCP_40x:
             /* DTLBMISS on 4xx */
@@ -1662,6 +1678,7 @@ void do_interrupt (CPUState *env)
         }
         return;
     case 0x1200:
+        idx = 14;
         switch (PPC_EXCP(env)) {
         case PPC_FLAGS_EXCP_40x:
             /* ITLBMISS on 4xx */
@@ -1838,6 +1855,10 @@ void do_interrupt (CPUState *env)
             cpu_abort(env,
                       "601 run mode exception is not implemented yet !\n");
             goto store_next;
+        case PPC_FLAGS_EXCP_BOOKE:
+            srr_0 = &env->spr[SPR_BOOKE_CSRR0];
+            srr_1 = &env->spr[SPR_BOOKE_CSRR1];
+            break;
         default:
             cpu_abort(env, "Invalid exception 0x1800 !\n");
             break;
@@ -1852,15 +1873,19 @@ void do_interrupt (CPUState *env)
         return;
     store_current:
         /* save current instruction location */
-        *srr_0 = (env->nip - 4) & 0xFFFFFFFFULL;
+        *srr_0 = env->nip - 4;
         break;
     store_next:
         /* save next instruction location */
-        *srr_0 = env->nip & 0xFFFFFFFFULL;
+        *srr_0 = env->nip;
         break;
     }
     /* Save msr */
     *srr_1 = msr;
+    if (asrr_0 != NULL)
+        *asrr_0 = *srr_0;
+    if (asrr_1 != NULL)
+        *asrr_1 = *srr_1;
     /* If we disactivated any translation, flush TLBs */
     if (msr_ir || msr_dr) {
         tlb_flush(env, 1);
@@ -1877,10 +1902,28 @@ void do_interrupt (CPUState *env)
     msr_dr = 0;
     msr_ri = 0;
     msr_le = msr_ile;
-    msr_sf = msr_isf;
+    if (PPC_EXCP(env) == PPC_FLAGS_EXCP_BOOKE) {
+        msr_cm = msr_icm;
+        if (idx == -1 || (idx >= 16 && idx < 32)) {
+            cpu_abort(env, "Invalid exception index for excp %d %08x idx %d\n",
+                      excp, excp, idx);
+        }
+#if defined(TARGET_PPC64)
+        if (msr_cm)
+            env->nip = (uint64_t)env->spr[SPR_BOOKE_IVPR];
+        else
+#endif
+            env->nip = (uint32_t)env->spr[SPR_BOOKE_IVPR];
+        if (idx < 16)
+            env->nip |= env->spr[SPR_BOOKE_IVOR0 + idx];
+        else if (idx < 38)
+            env->nip |= env->spr[SPR_BOOKE_IVOR32 + idx - 32];
+    } else {
+        msr_sf = msr_isf;
+        env->nip = excp;
+    }
     do_compute_hflags(env);
     /* Jump to handler */
-    env->nip = excp;
     env->exception_index = EXCP_NONE;
 }