diff mbox series

[RFC,v2,11/12] target/ppc: Create new files for book3s, booke and ppc32 exception code

Message ID 20211220181903.3456898-12-farosas@linux.ibm.com (mailing list archive)
State New, archived
Headers show
Series target/ppc: powerpc_excp improvements | expand

Commit Message

Fabiano Rosas Dec. 20, 2021, 6:19 p.m. UTC
Now that everything is split we can move them to their own files. The
rationale is that hopefully in the future we'll be able to move the
cpu code along and give them each a separate CONFIG option.

Another benefit is that we can now define static routines to replace
any of the generic ones from interrupts.c in case we need it.

book3s is now TARGET_PPC64 only. I also had to move some function
definitions to put them all under an ifdef.

ppc32 is now CONFIG_TCG only. I added a stub for ppc32_excp.

Signed-off-by: Fabiano Rosas <farosas@linux.ibm.com>
---
 target/ppc/cpu.h         |   2 +
 target/ppc/excp_helper.c | 426 +--------------------------------------
 target/ppc/interrupts.c  | 258 +-----------------------
 target/ppc/intr-book3s.c | 381 ++++++++++++++++++++++++++++++++++
 target/ppc/intr-booke.c  | 150 ++++++++++++++
 target/ppc/intr-ppc32.c  | 159 +++++++++++++++
 target/ppc/meson.build   |   3 +
 target/ppc/ppc_intr.h    |  18 +-
 target/ppc/tcg-stub.c    |   6 +
 9 files changed, 724 insertions(+), 679 deletions(-)
 create mode 100644 target/ppc/intr-book3s.c
 create mode 100644 target/ppc/intr-booke.c
 create mode 100644 target/ppc/intr-ppc32.c
diff mbox series

Patch

diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index fc66c3561d..becb5e0eb8 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -1356,6 +1356,8 @@  void ppc_cpu_do_interrupt(CPUState *cpu);
 bool ppc_cpu_exec_interrupt(CPUState *cpu, int int_req);
 void ppc_cpu_do_system_reset(CPUState *cs);
 void ppc_cpu_do_fwnmi_machine_check(CPUState *cs, target_ulong vector);
+void powerpc_set_excp_state(PowerPCCPU *cpu, target_ulong new_nip,
+                            target_ulong new_msr);
 extern const VMStateDescription vmstate_ppc_cpu;
 #endif
 
diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c
index 9c785b75d5..eb8dab1741 100644
--- a/target/ppc/excp_helper.c
+++ b/target/ppc/excp_helper.c
@@ -37,204 +37,8 @@ 
 /* Exception processing */
 #if !defined(CONFIG_USER_ONLY)
 
-#ifdef TARGET_PPC64
-static int powerpc_reset_wakeup(CPUState *cs, CPUPPCState *env, int excp,
-                                target_ulong *msr)
-{
-    /* We no longer are in a PM state */
-    env->resume_as_sreset = false;
-
-    /* Pretend to be returning from doze always as we don't lose state */
-    *msr |= SRR1_WS_NOLOSS;
-
-    /* Machine checks are sent normally */
-    if (excp == POWERPC_EXCP_MCHECK) {
-        return excp;
-    }
-    switch (excp) {
-    case POWERPC_EXCP_RESET:
-        *msr |= SRR1_WAKERESET;
-        break;
-    case POWERPC_EXCP_EXTERNAL:
-        *msr |= SRR1_WAKEEE;
-        break;
-    case POWERPC_EXCP_DECR:
-        *msr |= SRR1_WAKEDEC;
-        break;
-    case POWERPC_EXCP_SDOOR:
-        *msr |= SRR1_WAKEDBELL;
-        break;
-    case POWERPC_EXCP_SDOOR_HV:
-        *msr |= SRR1_WAKEHDBELL;
-        break;
-    case POWERPC_EXCP_HV_MAINT:
-        *msr |= SRR1_WAKEHMI;
-        break;
-    case POWERPC_EXCP_HVIRT:
-        *msr |= SRR1_WAKEHVI;
-        break;
-    default:
-        cpu_abort(cs, "Unsupported exception %d in Power Save mode\n",
-                  excp);
-    }
-    return POWERPC_EXCP_RESET;
-}
-
-/*
- * AIL - Alternate Interrupt Location, a mode that allows interrupts to be
- * taken with the MMU on, and which uses an alternate location (e.g., so the
- * kernel/hv can map the vectors there with an effective address).
- *
- * An interrupt is considered to be taken "with AIL" or "AIL applies" if they
- * are delivered in this way. AIL requires the LPCR to be set to enable this
- * mode, and then a number of conditions have to be true for AIL to apply.
- *
- * First of all, SRESET, MCE, and HMI are always delivered without AIL, because
- * they specifically want to be in real mode (e.g., the MCE might be signaling
- * a SLB multi-hit which requires SLB flush before the MMU can be enabled).
- *
- * After that, behaviour depends on the current MSR[IR], MSR[DR], MSR[HV],
- * whether or not the interrupt changes MSR[HV] from 0 to 1, and the current
- * radix mode (LPCR[HR]).
- *
- * POWER8, POWER9 with LPCR[HR]=0
- * | LPCR[AIL] | MSR[IR||DR] | MSR[HV] | new MSR[HV] | AIL |
- * +-----------+-------------+---------+-------------+-----+
- * | a         | 00/01/10    | x       | x           | 0   |
- * | a         | 11          | 0       | 1           | 0   |
- * | a         | 11          | 1       | 1           | a   |
- * | a         | 11          | 0       | 0           | a   |
- * +-------------------------------------------------------+
- *
- * POWER9 with LPCR[HR]=1
- * | LPCR[AIL] | MSR[IR||DR] | MSR[HV] | new MSR[HV] | AIL |
- * +-----------+-------------+---------+-------------+-----+
- * | a         | 00/01/10    | x       | x           | 0   |
- * | a         | 11          | x       | x           | a   |
- * +-------------------------------------------------------+
- *
- * The difference with POWER9 being that MSR[HV] 0->1 interrupts can be sent to
- * the hypervisor in AIL mode if the guest is radix. This is good for
- * performance but allows the guest to influence the AIL of hypervisor
- * interrupts using its MSR, and also the hypervisor must disallow guest
- * interrupts (MSR[HV] 0->0) from using AIL if the hypervisor does not want to
- * use AIL for its MSR[HV] 0->1 interrupts.
- *
- * POWER10 addresses those issues with a new LPCR[HAIL] bit that is applied to
- * interrupts that begin execution with MSR[HV]=1 (so both MSR[HV] 0->1 and
- * MSR[HV] 1->1).
- *
- * HAIL=1 is equivalent to AIL=3, for interrupts delivered with MSR[HV]=1.
- *
- * POWER10 behaviour is
- * | LPCR[AIL] | LPCR[HAIL] | MSR[IR||DR] | MSR[HV] | new MSR[HV] | AIL |
- * +-----------+------------+-------------+---------+-------------+-----+
- * | a         | h          | 00/01/10    | 0       | 0           | 0   |
- * | a         | h          | 11          | 0       | 0           | a   |
- * | a         | h          | x           | 0       | 1           | h   |
- * | a         | h          | 00/01/10    | 1       | 1           | 0   |
- * | a         | h          | 11          | 1       | 1           | h   |
- * +--------------------------------------------------------------------+
- */
-static inline void ppc_excp_apply_ail(PowerPCCPU *cpu, int excp_model, int excp,
-                                      target_ulong msr,
-                                      target_ulong *new_msr,
-                                      target_ulong *new_nip)
-{
-    CPUPPCState *env = &cpu->env;
-    bool mmu_all_on = ((msr >> MSR_IR) & 1) && ((msr >> MSR_DR) & 1);
-    bool hv_escalation = !(msr & MSR_HVB) && (*new_msr & MSR_HVB);
-    int ail = 0;
-
-    if (excp == POWERPC_EXCP_MCHECK ||
-        excp == POWERPC_EXCP_RESET ||
-        excp == POWERPC_EXCP_HV_MAINT) {
-        /* SRESET, MCE, HMI never apply AIL */
-        return;
-    }
-
-    if (excp_model == POWERPC_EXCP_POWER8 ||
-        excp_model == POWERPC_EXCP_POWER9) {
-        if (!mmu_all_on) {
-            /* AIL only works if MSR[IR] and MSR[DR] are both enabled. */
-            return;
-        }
-        if (hv_escalation && !(env->spr[SPR_LPCR] & LPCR_HR)) {
-            /*
-             * AIL does not work if there is a MSR[HV] 0->1 transition and the
-             * partition is in HPT mode. For radix guests, such interrupts are
-             * allowed to be delivered to the hypervisor in ail mode.
-             */
-            return;
-        }
-
-        ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT;
-        if (ail == 0) {
-            return;
-        }
-        if (ail == 1) {
-            /* AIL=1 is reserved, treat it like AIL=0 */
-            return;
-        }
-
-    } else if (excp_model == POWERPC_EXCP_POWER10) {
-        if (!mmu_all_on && !hv_escalation) {
-            /*
-             * AIL works for HV interrupts even with guest MSR[IR/DR] disabled.
-             * Guest->guest and HV->HV interrupts do require MMU on.
-             */
-            return;
-        }
-
-        if (*new_msr & MSR_HVB) {
-            if (!(env->spr[SPR_LPCR] & LPCR_HAIL)) {
-                /* HV interrupts depend on LPCR[HAIL] */
-                return;
-            }
-            ail = 3; /* HAIL=1 gives AIL=3 behaviour for HV interrupts */
-        } else {
-            ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT;
-        }
-        if (ail == 0) {
-            return;
-        }
-        if (ail == 1 || ail == 2) {
-            /* AIL=1 and AIL=2 are reserved, treat them like AIL=0 */
-            return;
-        }
-    } else {
-        /* Other processors do not support AIL */
-        return;
-    }
-
-    /*
-     * AIL applies, so the new MSR gets IR and DR set, and an offset applied
-     * to the new IP.
-     */
-    *new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
-
-    if (excp != POWERPC_EXCP_SYSCALL_VECTORED) {
-        if (ail == 2) {
-            *new_nip |= 0x0000000000018000ull;
-        } else if (ail == 3) {
-            *new_nip |= 0xc000000000004000ull;
-        }
-    } else {
-        /*
-         * scv AIL is a little different. AIL=2 does not change the address,
-         * only the MSR. AIL=3 replaces the 0x17000 base with 0xc...3000.
-         */
-        if (ail == 3) {
-            *new_nip &= ~0x0000000000017000ull; /* Un-apply the base offset */
-            *new_nip |= 0xc000000000003000ull; /* Apply scv's AIL=3 offset */
-        }
-    }
-}
-#endif /* TARGET_PPC64 */
-
-
-static inline void powerpc_set_excp_state(PowerPCCPU *cpu, target_ulong new_nip,
-                                          target_ulong new_msr)
+inline void powerpc_set_excp_state(PowerPCCPU *cpu, target_ulong new_nip,
+                                   target_ulong new_msr)
 {
     CPUState *cs = CPU(cpu);
     CPUPPCState *env = &cpu->env;
@@ -264,232 +68,6 @@  static inline void powerpc_set_excp_state(PowerPCCPU *cpu, target_ulong new_nip,
     check_tlb_flush(env, false);
 }
 
-#if defined(TARGET_PPC64)
-static inline void book3s_excp(PowerPCCPU *cpu, int excp)
-{
-    CPUState *cs = CPU(cpu);
-    CPUPPCState *env = &cpu->env;
-    int excp_model = env->excp_model;
-    PPCIntrArgs regs;
-    bool ignore;
-
-    regs.msr = env->msr & ~0x783f0000ULL;
-    regs.nip = env->nip;
-
-    /*
-     * new interrupt handler msr preserves existing HV and ME unless
-     * explicitly overriden
-     */
-    regs.new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB);
-
-    /* The Book3S cpus we support are 64 bit only */
-    regs.new_msr |= (target_ulong)1 << MSR_SF;
-
-    regs.sprn_srr0 = SPR_SRR0;
-    regs.sprn_srr1 = SPR_SRR1;
-
-    /*
-     * check for special resume at 0x100 from doze/nap/sleep/winkle on
-     * P7/P8/P9
-     */
-    if (env->resume_as_sreset) {
-        excp = powerpc_reset_wakeup(cs, env, excp, &regs.msr);
-    }
-
-    /*
-     * We don't want to generate an Hypervisor emulation assistance
-     * interrupt if we don't have HVB in msr_mask (PAPR mode).
-     */
-    if (excp == POWERPC_EXCP_HV_EMU && !(env->msr_mask & MSR_HVB)) {
-        excp = POWERPC_EXCP_PROGRAM;
-    }
-
-    regs.new_nip = env->excp_vectors[excp];
-    if (regs.new_nip == (target_ulong)-1ULL) {
-        cpu_abort(cs, "Raised an exception without defined vector %d\n",
-                  excp);
-    }
-
-    /* Setup interrupt-specific registers before injecting */
-    ignore = ppc_intr_prepare(cpu, interrupts_book3s, &regs, excp);
-
-    if (ignore) {
-        /* No further setup is needed for this interrupt */
-        return;
-    }
-
-    /*
-     * Sort out endianness of interrupt, this differs depending on the
-     * CPU, the HV mode, etc...
-     */
-    if (excp_model == POWERPC_EXCP_POWER7) {
-        if (!(regs.new_msr & MSR_HVB) && (env->spr[SPR_LPCR] & LPCR_ILE)) {
-            regs.new_msr |= (target_ulong)1 << MSR_LE;
-        }
-    } else if (excp_model == POWERPC_EXCP_POWER8) {
-        if (regs.new_msr & MSR_HVB) {
-            if (env->spr[SPR_HID0] & HID0_HILE) {
-                regs.new_msr |= (target_ulong)1 << MSR_LE;
-            }
-        } else if (env->spr[SPR_LPCR] & LPCR_ILE) {
-            regs.new_msr |= (target_ulong)1 << MSR_LE;
-        }
-    } else if (excp_model == POWERPC_EXCP_POWER9 ||
-               excp_model == POWERPC_EXCP_POWER10) {
-        if (regs.new_msr & MSR_HVB) {
-            if (env->spr[SPR_HID0] & HID0_POWER9_HILE) {
-                regs.new_msr |= (target_ulong)1 << MSR_LE;
-            }
-        } else if (env->spr[SPR_LPCR] & LPCR_ILE) {
-            regs.new_msr |= (target_ulong)1 << MSR_LE;
-        }
-    } else if (msr_ile) {
-        regs.new_msr |= (target_ulong)1 << MSR_LE;
-    }
-
-    if (excp != POWERPC_EXCP_SYSCALL_VECTORED) {
-        /* Save PC */
-        env->spr[regs.sprn_srr0] = regs.nip;
-
-        /* Save MSR */
-        env->spr[regs.sprn_srr1] = regs.msr;
-    }
-
-    /* This can update regs.new_msr and regs.new_nip if AIL applies */
-    ppc_excp_apply_ail(cpu, excp_model, excp, regs.msr, &regs.new_msr,
-                       &regs.new_nip);
-
-    powerpc_set_excp_state(cpu, regs.new_nip, regs.new_msr);
-}
-#endif /* defined(TARGET_PPC64) */
-
-static inline void booke_excp(PowerPCCPU *cpu, int excp)
-{
-    CPUState *cs = CPU(cpu);
-    CPUPPCState *env = &cpu->env;
-    PPCIntrArgs regs;
-    bool ignore;
-
-    regs.msr = env->msr;
-    regs.nip = env->nip;
-
-    /*
-     * new interrupt handler msr preserves existing HV and ME unless
-     * explicitly overriden
-     */
-    regs.new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB);
-
-    regs.sprn_srr0 = SPR_SRR0;
-    regs.sprn_srr1 = SPR_SRR1;
-
-    /*
-     * Hypervisor emulation assistance interrupt only exists on server
-     * arch 2.05 server or later.
-     */
-    if (excp == POWERPC_EXCP_HV_EMU) {
-        excp = POWERPC_EXCP_PROGRAM;
-    }
-
-#ifdef TARGET_PPC64
-    /*
-     * SPEU and VPU share the same IVOR but they exist in different
-     * processors. SPEU is e500v1/2 only and VPU is e6500 only.
-     */
-    if (env->excp_model == POWERPC_EXCP_BOOKE && excp == POWERPC_EXCP_VPU) {
-        excp = POWERPC_EXCP_SPEU;
-    }
-#endif
-
-    regs.new_nip = env->excp_vectors[excp];
-    if (regs.new_nip == (target_ulong)-1ULL) {
-        cpu_abort(cs, "Raised an exception without defined vector %d\n",
-                  excp);
-    }
-
-    regs.new_nip |= env->excp_prefix;
-
-    /* Setup interrupt-specific registers before injecting */
-    ignore = ppc_intr_prepare(cpu, interrupts_booke, &regs, excp);
-
-    if (ignore) {
-        /* No further setup is needed for this interrupt */
-        return;
-    }
-
-#if defined(TARGET_PPC64)
-    if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) {
-        /* Cat.64-bit: EPCR.ICM is copied to MSR.CM */
-        regs.new_msr |= (target_ulong)1 << MSR_CM;
-    } else {
-        regs.new_nip = (uint32_t)regs.new_nip;
-    }
-#endif
-
-    /* Save PC */
-    env->spr[regs.sprn_srr0] = regs.nip;
-
-    /* Save MSR */
-    env->spr[regs.sprn_srr1] = regs.msr;
-
-    powerpc_set_excp_state(cpu, regs.new_nip, regs.new_msr);
-}
-
-static inline void ppc32_excp(PowerPCCPU *cpu, int excp)
-{
-    CPUState *cs = CPU(cpu);
-    CPUPPCState *env = &cpu->env;
-    PPCIntrArgs regs;
-    bool ignore;
-
-    regs.msr = env->msr & ~0x783f0000ULL;
-    regs.nip = env->nip;
-
-    /*
-     * new interrupt handler msr preserves existing HV and ME unless
-     * explicitly overriden
-     */
-    regs.new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB);
-
-    regs.sprn_srr0 = SPR_SRR0;
-    regs.sprn_srr1 = SPR_SRR1;
-
-    /*
-     * Hypervisor emulation assistance interrupt only exists on server
-     * arch 2.05 server or later.
-     */
-    if (excp == POWERPC_EXCP_HV_EMU) {
-        excp = POWERPC_EXCP_PROGRAM;
-    }
-
-    regs.new_nip = env->excp_vectors[excp];
-    if (regs.new_nip == (target_ulong)-1ULL) {
-        cpu_abort(cs, "Raised an exception without defined vector %d\n",
-                  excp);
-    }
-
-    regs.new_nip |= env->excp_prefix;
-
-    /* Setup interrupt-specific registers before injecting */
-    ignore = ppc_intr_prepare(cpu, interrupts_ppc32, &regs, excp);
-
-    if (ignore) {
-        /* No further setup is needed for this interrupt */
-        return;
-    }
-
-    if (msr_ile) {
-        regs.new_msr |= (target_ulong)1 << MSR_LE;
-    }
-
-    /* Save PC */
-    env->spr[regs.sprn_srr0] = regs.nip;
-
-    /* Save MSR */
-    env->spr[regs.sprn_srr1] = regs.msr;
-
-    powerpc_set_excp_state(cpu, regs.new_nip, regs.new_msr);
-}
-
 static inline void powerpc_excp(PowerPCCPU *cpu, int excp)
 {
     CPUPPCState *env = &cpu->env;
diff --git a/target/ppc/interrupts.c b/target/ppc/interrupts.c
index 743faddfee..6f956029fd 100644
--- a/target/ppc/interrupts.c
+++ b/target/ppc/interrupts.c
@@ -358,6 +358,7 @@  void ppc_intr_system_reset(PowerPCCPU *cpu, PPCIntrArgs *regs, bool *ignore)
     }
 }
 
+#ifdef TARGET_PPC64
 void ppc_intr_hv(PowerPCCPU *cpu, PPCIntrArgs *regs, bool *ignore)
 {
     CPUPPCState *env = &cpu->env;
@@ -376,20 +377,19 @@  void ppc_intr_hv_insn_storage(PowerPCCPU *cpu, PPCIntrArgs *regs, bool *ignore)
     ppc_intr_hv(cpu, regs, ignore);
 }
 
-void ppc_intr_facility_unavail(PowerPCCPU *cpu, PPCIntrArgs *regs, bool *ignore)
-{
-#ifdef TARGET_PPC64
-    CPUPPCState *env = &cpu->env;
-    env->spr[SPR_FSCR] |= ((target_ulong)env->error_code << 56);
-#endif
-}
-
 void ppc_intr_hv_facility_unavail(PowerPCCPU *cpu, PPCIntrArgs *regs, bool *ignore)
 {
-#ifdef TARGET_PPC64
     CPUPPCState *env = &cpu->env;
     env->spr[SPR_FSCR] |= ((target_ulong)env->error_code << FSCR_IC_POS);
     ppc_intr_hv(cpu, regs, ignore);
+}
+#endif /* TARGET PPC64 */
+
+void ppc_intr_facility_unavail(PowerPCCPU *cpu, PPCIntrArgs *regs, bool *ignore)
+{
+#ifdef TARGET_PPC64
+    CPUPPCState *env = &cpu->env;
+    env->spr[SPR_FSCR] |= ((target_ulong)env->error_code << 56);
 #endif
 }
 
@@ -456,246 +456,6 @@  void ppc_intr_tlb_miss(PowerPCCPU *cpu, PPCIntrArgs *regs, bool *ignore)
     }
 }
 
-PPCInterrupt interrupts_ppc32[POWERPC_EXCP_NB] = {
-    [POWERPC_EXCP_ALIGN] = {
-        "Alignment", ppc_intr_alignment
-    },
-
-    [POWERPC_EXCP_CRITICAL] = {
-        "Critical input", ppc_intr_critical
-    },
-
-    [POWERPC_EXCP_DEBUG] = {
-        "Debug", ppc_intr_debug
-    },
-
-    [POWERPC_EXCP_DLTLB] = {
-        "Data load TLB error", ppc_intr_tlb_miss
-    },
-
-    [POWERPC_EXCP_DSI] = {
-        "Data storage", ppc_intr_data_storage
-    },
-
-    [POWERPC_EXCP_DSTLB] = {
-        "Data store TLB error", ppc_intr_tlb_miss
-    },
-
-    [POWERPC_EXCP_EXTERNAL] = {
-        "External", ppc_intr_external
-    },
-
-    [POWERPC_EXCP_FIT] = {
-        "Fixed-interval timer", ppc_intr_fit
-    },
-
-    [POWERPC_EXCP_IFTLB] = {
-        "Insn fetch TLB error", ppc_intr_tlb_miss
-    },
-
-    [POWERPC_EXCP_ISI] = {
-        "Instruction storage", ppc_intr_insn_storage
-    },
-
-    [POWERPC_EXCP_MCHECK] = {
-        "Machine check", ppc_intr_machine_check
-    },
-
-    [POWERPC_EXCP_PIT] = {
-        "Programmable interval timer", ppc_intr_programmable_timer
-    },
-
-    [POWERPC_EXCP_PROGRAM] = {
-        "Program", ppc_intr_program
-    },
-
-    [POWERPC_EXCP_RESET] = {
-        "System reset", ppc_intr_system_reset
-    },
-
-    [POWERPC_EXCP_SYSCALL] = {
-        "System call", ppc_intr_system_call
-    },
-
-    [POWERPC_EXCP_VPU] = {
-        "Vector unavailable", ppc_intr_facility_unavail
-    },
-
-    [POWERPC_EXCP_WDT] = {
-        "Watchdog timer", ppc_intr_watchdog
-    },
-
-    [POWERPC_EXCP_DECR]  = { "Decrementer",                ppc_intr_noop },
-    [POWERPC_EXCP_DTLB]  = { "Data TLB error",             ppc_intr_noop },
-    [POWERPC_EXCP_FPU]   = { "Floating-point unavailable", ppc_intr_noop },
-    [POWERPC_EXCP_ITLB]  = { "Instruction TLB error",      ppc_intr_noop },
-    [POWERPC_EXCP_TRACE] = { "Trace",                      ppc_intr_noop },
-
-/* Not implemented */
-    [POWERPC_EXCP_DABR]     = { "Data address breakpoint" },
-    [POWERPC_EXCP_DTLBE]    = { "Data TLB error" },
-    [POWERPC_EXCP_EMUL]     = { "Emulation trap" },
-    [POWERPC_EXCP_FPA]      = { "Floating-point assist" },
-    [POWERPC_EXCP_IABR]     = { "Insn address breakpoint" },
-    [POWERPC_EXCP_IO]       = { "IO error" },
-    [POWERPC_EXCP_ITLBE]    = { "Instruction TLB error" },
-    [POWERPC_EXCP_MEXTBR]   = { "Maskable external" },
-    [POWERPC_EXCP_NMEXTBR]  = { "Non-maskable external" },
-    [POWERPC_EXCP_PERFM]    = { "Performance counter" },
-    [POWERPC_EXCP_RUNM]     = { "Run mode" },
-    [POWERPC_EXCP_SMI]      = { "System management" },
-    [POWERPC_EXCP_THERM]    = { "Thermal management" },
-    [POWERPC_EXCP_VPUA]     = { "Vector assist" },
-};
-
-PPCInterrupt interrupts_book3s[POWERPC_EXCP_NB] = {
-    [POWERPC_EXCP_ALIGN] = {
-        "Alignment", ppc_intr_alignment
-    },
-
-    [POWERPC_EXCP_DSI] = {
-        "Data storage", ppc_intr_data_storage
-    },
-
-    [POWERPC_EXCP_EXTERNAL] = {
-        "External", ppc_intr_external
-    },
-
-    [POWERPC_EXCP_FU] = {
-        "Facility unavailable", ppc_intr_facility_unavail
-    },
-
-    [POWERPC_EXCP_HISI] = {
-        "Hypervisor instruction storage", ppc_intr_hv_insn_storage
-    },
-
-    [POWERPC_EXCP_HV_FU] = {
-        "Hypervisor facility unavailable", ppc_intr_hv_facility_unavail
-    },
-
-    [POWERPC_EXCP_ISI] = {
-        "Instruction storage", ppc_intr_insn_storage
-    },
-
-    [POWERPC_EXCP_MCHECK] = {
-        "Machine check", ppc_intr_machine_check
-    },
-
-    [POWERPC_EXCP_PROGRAM] = {
-        "Program", ppc_intr_program
-    },
-
-    [POWERPC_EXCP_RESET] = {
-        "System reset", ppc_intr_system_reset
-    },
-
-    [POWERPC_EXCP_SYSCALL] = {
-        "System call", ppc_intr_system_call
-    },
-
-    [POWERPC_EXCP_SYSCALL_VECTORED] = {
-        "System call vectored", ppc_intr_system_call_vectored
-    },
-
-    [POWERPC_EXCP_VPU] = {
-        "Vector unavailable", ppc_intr_facility_unavail
-    },
-
-    [POWERPC_EXCP_VSXU] = {
-        "VSX unavailable", ppc_intr_facility_unavail
-    },
-
-    [POWERPC_EXCP_HDECR]    = { "Hypervisor decrementer",         ppc_intr_hv },
-    [POWERPC_EXCP_HDSI]     = { "Hypervisor data storage",        ppc_intr_hv },
-    [POWERPC_EXCP_HVIRT]    = { "Hypervisor virtualization",      ppc_intr_hv },
-    [POWERPC_EXCP_HV_EMU]   = { "Hypervisor emulation assist",    ppc_intr_hv },
-    [POWERPC_EXCP_SDOOR_HV] = { "Hypervisor doorbell",            ppc_intr_hv },
-
-    [POWERPC_EXCP_DECR]  = { "Decrementer",                ppc_intr_noop },
-    [POWERPC_EXCP_DSEG]  = { "Data segment",               ppc_intr_noop },
-    [POWERPC_EXCP_FPU]   = { "Floating-point unavailable", ppc_intr_noop },
-    [POWERPC_EXCP_ISEG]  = { "Instruction segment",        ppc_intr_noop },
-    [POWERPC_EXCP_ITLB]  = { "Instruction TLB error",      ppc_intr_noop },
-    [POWERPC_EXCP_TRACE] = { "Trace",                      ppc_intr_noop },
-
-/* Not implemented */
-    [POWERPC_EXCP_HV_MAINT] = { "Hypervisor maintenance" },
-    [POWERPC_EXCP_IABR]     = { "Insn address breakpoint" },
-    [POWERPC_EXCP_MAINT]    = { "Maintenance" },
-    [POWERPC_EXCP_PERFM]    = { "Performance counter" },
-    [POWERPC_EXCP_SDOOR]    = { "Server doorbell" },
-    [POWERPC_EXCP_THERM]    = { "Thermal management" },
-    [POWERPC_EXCP_VPUA]     = { "Vector assist" },
-};
-
-PPCInterrupt interrupts_booke[POWERPC_EXCP_NB] = {
-    [POWERPC_EXCP_ALIGN] = {
-        "Alignment", ppc_intr_alignment
-    },
-
-    [POWERPC_EXCP_CRITICAL] = {
-        "Critical input", ppc_intr_critical
-    },
-
-    [POWERPC_EXCP_DEBUG] = {
-        "Debug", ppc_intr_debug
-    },
-
-    [POWERPC_EXCP_DLTLB] = {
-        "Data load TLB error", ppc_intr_tlb_miss
-    },
-
-    [POWERPC_EXCP_DSI] = {
-        "Data storage", ppc_intr_data_storage
-    },
-
-    [POWERPC_EXCP_EXTERNAL] = {
-        "External", ppc_intr_external
-    },
-
-    [POWERPC_EXCP_FIT] = {
-        "Fixed-interval timer", ppc_intr_fit
-    },
-
-    [POWERPC_EXCP_ISI] = {
-        "Instruction storage", ppc_intr_insn_storage
-    },
-
-    [POWERPC_EXCP_MCHECK] = {
-        "Machine check", ppc_intr_machine_check
-    },
-
-    [POWERPC_EXCP_PROGRAM] = {
-        "Program", ppc_intr_program
-    },
-
-    [POWERPC_EXCP_RESET] = {
-        "System reset", ppc_intr_system_reset
-    },
-
-    [POWERPC_EXCP_SPEU] = {
-        "SPE/embedded FP unavailable/VPU", ppc_intr_spe_unavailable
-    },
-
-    [POWERPC_EXCP_SYSCALL] = {
-        "System call", ppc_intr_system_call
-    },
-
-    [POWERPC_EXCP_WDT] = {
-        "Watchdog timer", ppc_intr_watchdog
-    },
-
-    [POWERPC_EXCP_APU]   = { "Aux. processor unavailable", ppc_intr_noop },
-    [POWERPC_EXCP_DECR]  = { "Decrementer",                ppc_intr_noop },
-    [POWERPC_EXCP_DTLB]  = { "Data TLB error",             ppc_intr_noop },
-    [POWERPC_EXCP_FPU]   = { "Floating-point unavailable", ppc_intr_noop },
-    [POWERPC_EXCP_ITLB]  = { "Instruction TLB error",      ppc_intr_noop },
-
-/* Not impleemented */
-    [POWERPC_EXCP_EFPDI]    = { "Embedded floating-point data" },
-    [POWERPC_EXCP_EFPRI]    = { "Embedded floating-point round" },
-};
-
 int ppc_intr_prepare(PowerPCCPU *cpu, PPCInterrupt *interrupts,
                      PPCIntrArgs *regs, int excp)
 {
diff --git a/target/ppc/intr-book3s.c b/target/ppc/intr-book3s.c
new file mode 100644
index 0000000000..cd279de346
--- /dev/null
+++ b/target/ppc/intr-book3s.c
@@ -0,0 +1,381 @@ 
+/*
+ * PowerPC interrupt dispatching for Book3S CPUs
+ *
+ * Copyright (C) 2021 IBM Corporation.
+ *
+ * This code is licensed under the GPL version 2 or later. See the
+ * COPYING file in the top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "ppc_intr.h"
+
+static PPCInterrupt interrupts_book3s[POWERPC_EXCP_NB] = {
+    [POWERPC_EXCP_ALIGN] = {
+        "Alignment", ppc_intr_alignment
+    },
+
+    [POWERPC_EXCP_DSI] = {
+        "Data storage", ppc_intr_data_storage
+    },
+
+    [POWERPC_EXCP_EXTERNAL] = {
+        "External", ppc_intr_external
+    },
+
+    [POWERPC_EXCP_FU] = {
+        "Facility unavailable", ppc_intr_facility_unavail
+    },
+
+    [POWERPC_EXCP_HISI] = {
+        "Hypervisor instruction storage", ppc_intr_hv_insn_storage
+    },
+
+    [POWERPC_EXCP_HV_FU] = {
+        "Hypervisor facility unavailable", ppc_intr_hv_facility_unavail
+    },
+
+    [POWERPC_EXCP_ISI] = {
+        "Instruction storage", ppc_intr_insn_storage
+    },
+
+    [POWERPC_EXCP_MCHECK] = {
+        "Machine check", ppc_intr_machine_check
+    },
+
+    [POWERPC_EXCP_PROGRAM] = {
+        "Program", ppc_intr_program
+    },
+
+    [POWERPC_EXCP_RESET] = {
+        "System reset", ppc_intr_system_reset
+    },
+
+    [POWERPC_EXCP_SYSCALL] = {
+        "System call", ppc_intr_system_call
+    },
+
+    [POWERPC_EXCP_SYSCALL_VECTORED] = {
+        "System call vectored", ppc_intr_system_call_vectored
+    },
+
+    [POWERPC_EXCP_VPU] = {
+        "Vector unavailable", ppc_intr_facility_unavail
+    },
+
+    [POWERPC_EXCP_VSXU] = {
+        "VSX unavailable", ppc_intr_facility_unavail
+    },
+
+    [POWERPC_EXCP_HDECR]    = { "Hypervisor decrementer",         ppc_intr_hv },
+    [POWERPC_EXCP_HDSI]     = { "Hypervisor data storage",        ppc_intr_hv },
+    [POWERPC_EXCP_HVIRT]    = { "Hypervisor virtualization",      ppc_intr_hv },
+    [POWERPC_EXCP_HV_EMU]   = { "Hypervisor emulation assist",    ppc_intr_hv },
+    [POWERPC_EXCP_SDOOR_HV] = { "Hypervisor doorbell",            ppc_intr_hv },
+
+    [POWERPC_EXCP_DECR]  = { "Decrementer",                ppc_intr_noop },
+    [POWERPC_EXCP_DSEG]  = { "Data segment",               ppc_intr_noop },
+    [POWERPC_EXCP_FPU]   = { "Floating-point unavailable", ppc_intr_noop },
+    [POWERPC_EXCP_ISEG]  = { "Instruction segment",        ppc_intr_noop },
+    [POWERPC_EXCP_ITLB]  = { "Instruction TLB error",      ppc_intr_noop },
+    [POWERPC_EXCP_TRACE] = { "Trace",                      ppc_intr_noop },
+
+/* Not implemented */
+    [POWERPC_EXCP_HV_MAINT] = { "Hypervisor maintenance" },
+    [POWERPC_EXCP_IABR]     = { "Insn address breakpoint" },
+    [POWERPC_EXCP_MAINT]    = { "Maintenance" },
+    [POWERPC_EXCP_PERFM]    = { "Performance counter" },
+    [POWERPC_EXCP_SDOOR]    = { "Server doorbell" },
+    [POWERPC_EXCP_THERM]    = { "Thermal management" },
+    [POWERPC_EXCP_VPUA]     = { "Vector assist" },
+};
+
+static int powerpc_reset_wakeup(CPUState *cs, CPUPPCState *env, int excp,
+                                target_ulong *msr)
+{
+    /* We no longer are in a PM state */
+    env->resume_as_sreset = false;
+
+    /* Pretend to be returning from doze always as we don't lose state */
+    *msr |= SRR1_WS_NOLOSS;
+
+    /* Machine checks are sent normally */
+    if (excp == POWERPC_EXCP_MCHECK) {
+        return excp;
+    }
+    switch (excp) {
+    case POWERPC_EXCP_RESET:
+        *msr |= SRR1_WAKERESET;
+        break;
+    case POWERPC_EXCP_EXTERNAL:
+        *msr |= SRR1_WAKEEE;
+        break;
+    case POWERPC_EXCP_DECR:
+        *msr |= SRR1_WAKEDEC;
+        break;
+    case POWERPC_EXCP_SDOOR:
+        *msr |= SRR1_WAKEDBELL;
+        break;
+    case POWERPC_EXCP_SDOOR_HV:
+        *msr |= SRR1_WAKEHDBELL;
+        break;
+    case POWERPC_EXCP_HV_MAINT:
+        *msr |= SRR1_WAKEHMI;
+        break;
+    case POWERPC_EXCP_HVIRT:
+        *msr |= SRR1_WAKEHVI;
+        break;
+    default:
+        cpu_abort(cs, "Unsupported exception %d in Power Save mode\n",
+                  excp);
+    }
+    return POWERPC_EXCP_RESET;
+}
+
+/*
+ * AIL - Alternate Interrupt Location, a mode that allows interrupts to be
+ * taken with the MMU on, and which uses an alternate location (e.g., so the
+ * kernel/hv can map the vectors there with an effective address).
+ *
+ * An interrupt is considered to be taken "with AIL" or "AIL applies" if they
+ * are delivered in this way. AIL requires the LPCR to be set to enable this
+ * mode, and then a number of conditions have to be true for AIL to apply.
+ *
+ * First of all, SRESET, MCE, and HMI are always delivered without AIL, because
+ * they specifically want to be in real mode (e.g., the MCE might be signaling
+ * a SLB multi-hit which requires SLB flush before the MMU can be enabled).
+ *
+ * After that, behaviour depends on the current MSR[IR], MSR[DR], MSR[HV],
+ * whether or not the interrupt changes MSR[HV] from 0 to 1, and the current
+ * radix mode (LPCR[HR]).
+ *
+ * POWER8, POWER9 with LPCR[HR]=0
+ * | LPCR[AIL] | MSR[IR||DR] | MSR[HV] | new MSR[HV] | AIL |
+ * +-----------+-------------+---------+-------------+-----+
+ * | a         | 00/01/10    | x       | x           | 0   |
+ * | a         | 11          | 0       | 1           | 0   |
+ * | a         | 11          | 1       | 1           | a   |
+ * | a         | 11          | 0       | 0           | a   |
+ * +-------------------------------------------------------+
+ *
+ * POWER9 with LPCR[HR]=1
+ * | LPCR[AIL] | MSR[IR||DR] | MSR[HV] | new MSR[HV] | AIL |
+ * +-----------+-------------+---------+-------------+-----+
+ * | a         | 00/01/10    | x       | x           | 0   |
+ * | a         | 11          | x       | x           | a   |
+ * +-------------------------------------------------------+
+ *
+ * The difference with POWER9 being that MSR[HV] 0->1 interrupts can be sent to
+ * the hypervisor in AIL mode if the guest is radix. This is good for
+ * performance but allows the guest to influence the AIL of hypervisor
+ * interrupts using its MSR, and also the hypervisor must disallow guest
+ * interrupts (MSR[HV] 0->0) from using AIL if the hypervisor does not want to
+ * use AIL for its MSR[HV] 0->1 interrupts.
+ *
+ * POWER10 addresses those issues with a new LPCR[HAIL] bit that is applied to
+ * interrupts that begin execution with MSR[HV]=1 (so both MSR[HV] 0->1 and
+ * MSR[HV] 1->1).
+ *
+ * HAIL=1 is equivalent to AIL=3, for interrupts delivered with MSR[HV]=1.
+ *
+ * POWER10 behaviour is
+ * | LPCR[AIL] | LPCR[HAIL] | MSR[IR||DR] | MSR[HV] | new MSR[HV] | AIL |
+ * +-----------+------------+-------------+---------+-------------+-----+
+ * | a         | h          | 00/01/10    | 0       | 0           | 0   |
+ * | a         | h          | 11          | 0       | 0           | a   |
+ * | a         | h          | x           | 0       | 1           | h   |
+ * | a         | h          | 00/01/10    | 1       | 1           | 0   |
+ * | a         | h          | 11          | 1       | 1           | h   |
+ * +--------------------------------------------------------------------+
+ */
+static inline void ppc_excp_apply_ail(PowerPCCPU *cpu, int excp_model, int excp,
+                                      target_ulong msr,
+                                      target_ulong *new_msr,
+                                      target_ulong *new_nip)
+{
+    CPUPPCState *env = &cpu->env;
+    bool mmu_all_on = ((msr >> MSR_IR) & 1) && ((msr >> MSR_DR) & 1);
+    bool hv_escalation = !(msr & MSR_HVB) && (*new_msr & MSR_HVB);
+    int ail = 0;
+
+    if (excp == POWERPC_EXCP_MCHECK ||
+        excp == POWERPC_EXCP_RESET ||
+        excp == POWERPC_EXCP_HV_MAINT) {
+        /* SRESET, MCE, HMI never apply AIL */
+        return;
+    }
+
+    if (excp_model == POWERPC_EXCP_POWER8 ||
+        excp_model == POWERPC_EXCP_POWER9) {
+        if (!mmu_all_on) {
+            /* AIL only works if MSR[IR] and MSR[DR] are both enabled. */
+            return;
+        }
+        if (hv_escalation && !(env->spr[SPR_LPCR] & LPCR_HR)) {
+            /*
+             * AIL does not work if there is a MSR[HV] 0->1 transition and the
+             * partition is in HPT mode. For radix guests, such interrupts are
+             * allowed to be delivered to the hypervisor in ail mode.
+             */
+            return;
+        }
+
+        ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT;
+        if (ail == 0) {
+            return;
+        }
+        if (ail == 1) {
+            /* AIL=1 is reserved, treat it like AIL=0 */
+            return;
+        }
+
+    } else if (excp_model == POWERPC_EXCP_POWER10) {
+        if (!mmu_all_on && !hv_escalation) {
+            /*
+             * AIL works for HV interrupts even with guest MSR[IR/DR] disabled.
+             * Guest->guest and HV->HV interrupts do require MMU on.
+             */
+            return;
+        }
+
+        if (*new_msr & MSR_HVB) {
+            if (!(env->spr[SPR_LPCR] & LPCR_HAIL)) {
+                /* HV interrupts depend on LPCR[HAIL] */
+                return;
+            }
+            ail = 3; /* HAIL=1 gives AIL=3 behaviour for HV interrupts */
+        } else {
+            ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT;
+        }
+        if (ail == 0) {
+            return;
+        }
+        if (ail == 1 || ail == 2) {
+            /* AIL=1 and AIL=2 are reserved, treat them like AIL=0 */
+            return;
+        }
+    } else {
+        /* Other processors do not support AIL */
+        return;
+    }
+
+    /*
+     * AIL applies, so the new MSR gets IR and DR set, and an offset applied
+     * to the new IP.
+     */
+    *new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
+
+    if (excp != POWERPC_EXCP_SYSCALL_VECTORED) {
+        if (ail == 2) {
+            *new_nip |= 0x0000000000018000ull;
+        } else if (ail == 3) {
+            *new_nip |= 0xc000000000004000ull;
+        }
+    } else {
+        /*
+         * scv AIL is a little different. AIL=2 does not change the address,
+         * only the MSR. AIL=3 replaces the 0x17000 base with 0xc...3000.
+         */
+        if (ail == 3) {
+            *new_nip &= ~0x0000000000017000ull; /* Un-apply the base offset */
+            *new_nip |= 0xc000000000003000ull; /* Apply scv's AIL=3 offset */
+        }
+    }
+}
+
+void book3s_excp(PowerPCCPU *cpu, int excp)
+{
+    CPUState *cs = CPU(cpu);
+    CPUPPCState *env = &cpu->env;
+    int excp_model = env->excp_model;
+    PPCIntrArgs regs;
+    bool ignore;
+
+    regs.msr = env->msr & ~0x783f0000ULL;
+    regs.nip = env->nip;
+
+    /*
+     * new interrupt handler msr preserves existing HV and ME unless
+     * explicitly overriden
+     */
+    regs.new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB);
+
+    /* The Book3S cpus we support are 64 bit only */
+    regs.new_msr |= (target_ulong)1 << MSR_SF;
+
+    regs.sprn_srr0 = SPR_SRR0;
+    regs.sprn_srr1 = SPR_SRR1;
+
+    /*
+     * check for special resume at 0x100 from doze/nap/sleep/winkle on
+     * P7/P8/P9
+     */
+    if (env->resume_as_sreset) {
+        excp = powerpc_reset_wakeup(cs, env, excp, &regs.msr);
+    }
+
+    /*
+     * We don't want to generate an Hypervisor emulation assistance
+     * interrupt if we don't have HVB in msr_mask (PAPR mode).
+     */
+    if (excp == POWERPC_EXCP_HV_EMU && !(env->msr_mask & MSR_HVB)) {
+        excp = POWERPC_EXCP_PROGRAM;
+    }
+
+    regs.new_nip = env->excp_vectors[excp];
+    if (regs.new_nip == (target_ulong)-1ULL) {
+        cpu_abort(cs, "Raised an exception without defined vector %d\n",
+                  excp);
+    }
+
+    /* Setup interrupt-specific registers before injecting */
+    ignore = ppc_intr_prepare(cpu, interrupts_book3s, &regs, excp);
+
+    if (ignore) {
+        /* No further setup is needed for this interrupt */
+        return;
+    }
+
+    /*
+     * Sort out endianness of interrupt, this differs depending on the
+     * CPU, the HV mode, etc...
+     */
+    if (excp_model == POWERPC_EXCP_POWER7) {
+        if (!(regs.new_msr & MSR_HVB) && (env->spr[SPR_LPCR] & LPCR_ILE)) {
+            regs.new_msr |= (target_ulong)1 << MSR_LE;
+        }
+    } else if (excp_model == POWERPC_EXCP_POWER8) {
+        if (regs.new_msr & MSR_HVB) {
+            if (env->spr[SPR_HID0] & HID0_HILE) {
+                regs.new_msr |= (target_ulong)1 << MSR_LE;
+            }
+        } else if (env->spr[SPR_LPCR] & LPCR_ILE) {
+            regs.new_msr |= (target_ulong)1 << MSR_LE;
+        }
+    } else if (excp_model == POWERPC_EXCP_POWER9 ||
+               excp_model == POWERPC_EXCP_POWER10) {
+        if (regs.new_msr & MSR_HVB) {
+            if (env->spr[SPR_HID0] & HID0_POWER9_HILE) {
+                regs.new_msr |= (target_ulong)1 << MSR_LE;
+            }
+        } else if (env->spr[SPR_LPCR] & LPCR_ILE) {
+            regs.new_msr |= (target_ulong)1 << MSR_LE;
+        }
+    } else if (msr_ile) {
+        regs.new_msr |= (target_ulong)1 << MSR_LE;
+    }
+
+    if (excp != POWERPC_EXCP_SYSCALL_VECTORED) {
+        /* Save PC */
+        env->spr[regs.sprn_srr0] = regs.nip;
+
+        /* Save MSR */
+        env->spr[regs.sprn_srr1] = regs.msr;
+    }
+
+    /* This can update regs.new_msr and regs.new_nip if AIL applies */
+    ppc_excp_apply_ail(cpu, excp_model, excp, regs.msr, &regs.new_msr,
+                       &regs.new_nip);
+
+    powerpc_set_excp_state(cpu, regs.new_nip, regs.new_msr);
+}
diff --git a/target/ppc/intr-booke.c b/target/ppc/intr-booke.c
new file mode 100644
index 0000000000..598d372069
--- /dev/null
+++ b/target/ppc/intr-booke.c
@@ -0,0 +1,150 @@ 
+/*
+ * PowerPC exception dispatching for BookE CPUs
+ *
+ * Copyright (C) 2021 IBM Corporation.
+ *
+ * This code is licensed under the GPL version 2 or later. See the
+ * COPYING file in the top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "ppc_intr.h"
+
+static PPCInterrupt interrupts_booke[POWERPC_EXCP_NB] = {
+    [POWERPC_EXCP_ALIGN] = {
+        "Alignment", ppc_intr_alignment
+    },
+
+    [POWERPC_EXCP_CRITICAL] = {
+        "Critical input", ppc_intr_critical
+    },
+
+    [POWERPC_EXCP_DEBUG] = {
+        "Debug", ppc_intr_debug
+    },
+
+    [POWERPC_EXCP_DLTLB] = {
+        "Data load TLB error", ppc_intr_tlb_miss
+    },
+
+    [POWERPC_EXCP_DSI] = {
+        "Data storage", ppc_intr_data_storage
+    },
+
+    [POWERPC_EXCP_EXTERNAL] = {
+        "External", ppc_intr_external
+    },
+
+    [POWERPC_EXCP_FIT] = {
+        "Fixed-interval timer", ppc_intr_fit
+    },
+
+    [POWERPC_EXCP_ISI] = {
+        "Instruction storage", ppc_intr_insn_storage
+    },
+
+    [POWERPC_EXCP_MCHECK] = {
+        "Machine check", ppc_intr_machine_check
+    },
+
+    [POWERPC_EXCP_PROGRAM] = {
+        "Program", ppc_intr_program
+    },
+
+    [POWERPC_EXCP_RESET] = {
+        "System reset", ppc_intr_system_reset
+    },
+
+    [POWERPC_EXCP_SPEU] = {
+        "SPE/embedded FP unavailable/VPU", ppc_intr_spe_unavailable
+    },
+
+    [POWERPC_EXCP_SYSCALL] = {
+        "System call", ppc_intr_system_call
+    },
+
+    [POWERPC_EXCP_WDT] = {
+        "Watchdog timer", ppc_intr_watchdog
+    },
+
+    [POWERPC_EXCP_APU]   = { "Aux. processor unavailable", ppc_intr_noop },
+    [POWERPC_EXCP_DECR]  = { "Decrementer",                ppc_intr_noop },
+    [POWERPC_EXCP_DTLB]  = { "Data TLB error",             ppc_intr_noop },
+    [POWERPC_EXCP_FPU]   = { "Floating-point unavailable", ppc_intr_noop },
+    [POWERPC_EXCP_ITLB]  = { "Instruction TLB error",      ppc_intr_noop },
+
+/* Not impleemented */
+    [POWERPC_EXCP_EFPDI]    = { "Embedded floating-point data" },
+    [POWERPC_EXCP_EFPRI]    = { "Embedded floating-point round" },
+};
+
+void booke_excp(PowerPCCPU *cpu, int excp)
+{
+    CPUState *cs = CPU(cpu);
+    CPUPPCState *env = &cpu->env;
+    PPCIntrArgs regs;
+    bool ignore;
+
+    regs.msr = env->msr;
+    regs.nip = env->nip;
+
+    /*
+     * new interrupt handler msr preserves existing HV and ME unless
+     * explicitly overriden
+     */
+    regs.new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB);
+
+    regs.sprn_srr0 = SPR_SRR0;
+    regs.sprn_srr1 = SPR_SRR1;
+
+    /*
+     * Hypervisor emulation assistance interrupt only exists on server
+     * arch 2.05 server or later.
+     */
+    if (excp == POWERPC_EXCP_HV_EMU) {
+        excp = POWERPC_EXCP_PROGRAM;
+    }
+
+#ifdef TARGET_PPC64
+    /*
+     * SPEU and VPU share the same IVOR but they exist in different
+     * processors. SPEU is e500v1/2 only and VPU is e6500 only.
+     */
+    if (env->excp_model == POWERPC_EXCP_BOOKE && excp == POWERPC_EXCP_VPU) {
+        excp = POWERPC_EXCP_SPEU;
+    }
+#endif
+
+    regs.new_nip = env->excp_vectors[excp];
+    if (regs.new_nip == (target_ulong)-1ULL) {
+        cpu_abort(cs, "Raised an exception without defined vector %d\n",
+                  excp);
+    }
+
+    regs.new_nip |= env->excp_prefix;
+
+    /* Setup interrupt-specific registers before injecting */
+    ignore = ppc_intr_prepare(cpu, interrupts_booke, &regs, excp);
+
+    if (ignore) {
+        /* No further setup is needed for this interrupt */
+        return;
+    }
+
+#if defined(TARGET_PPC64)
+    if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) {
+        /* Cat.64-bit: EPCR.ICM is copied to MSR.CM */
+        regs.new_msr |= (target_ulong)1 << MSR_CM;
+    } else {
+        regs.new_nip = (uint32_t)regs.new_nip;
+    }
+#endif
+
+    /* Save PC */
+    env->spr[regs.sprn_srr0] = regs.nip;
+
+    /* Save MSR */
+    env->spr[regs.sprn_srr1] = regs.msr;
+
+    powerpc_set_excp_state(cpu, regs.new_nip, regs.new_msr);
+}
diff --git a/target/ppc/intr-ppc32.c b/target/ppc/intr-ppc32.c
new file mode 100644
index 0000000000..a4b89da536
--- /dev/null
+++ b/target/ppc/intr-ppc32.c
@@ -0,0 +1,159 @@ 
+/*
+ * PowerPC exception dispatching for 32bit CPUs
+ *
+ * Copyright (C) 2021 IBM Corporation.
+ *
+ * This code is licensed under the GPL version 2 or later. See the
+ * COPYING file in the top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "ppc_intr.h"
+
+static PPCInterrupt interrupts_ppc32[POWERPC_EXCP_NB] = {
+    [POWERPC_EXCP_ALIGN] = {
+        "Alignment", ppc_intr_alignment
+    },
+
+    [POWERPC_EXCP_CRITICAL] = {
+        "Critical input", ppc_intr_critical
+    },
+
+    [POWERPC_EXCP_DEBUG] = {
+        "Debug", ppc_intr_debug
+    },
+
+    [POWERPC_EXCP_DLTLB] = {
+        "Data load TLB error", ppc_intr_tlb_miss
+    },
+
+    [POWERPC_EXCP_DSI] = {
+        "Data storage", ppc_intr_data_storage
+    },
+
+    [POWERPC_EXCP_DSTLB] = {
+        "Data store TLB error", ppc_intr_tlb_miss
+    },
+
+    [POWERPC_EXCP_EXTERNAL] = {
+        "External", ppc_intr_external
+    },
+
+    [POWERPC_EXCP_FIT] = {
+        "Fixed-interval timer", ppc_intr_fit
+    },
+
+    [POWERPC_EXCP_IFTLB] = {
+        "Insn fetch TLB error", ppc_intr_tlb_miss
+    },
+
+    [POWERPC_EXCP_ISI] = {
+        "Instruction storage", ppc_intr_insn_storage
+    },
+
+    [POWERPC_EXCP_MCHECK] = {
+        "Machine check", ppc_intr_machine_check
+    },
+
+    [POWERPC_EXCP_PIT] = {
+        "Programmable interval timer", ppc_intr_programmable_timer
+    },
+
+    [POWERPC_EXCP_PROGRAM] = {
+        "Program", ppc_intr_program
+    },
+
+    [POWERPC_EXCP_RESET] = {
+        "System reset", ppc_intr_system_reset
+    },
+
+    [POWERPC_EXCP_SYSCALL] = {
+        "System call", ppc_intr_system_call
+    },
+
+    [POWERPC_EXCP_VPU] = {
+        "Vector unavailable", ppc_intr_facility_unavail
+    },
+
+    [POWERPC_EXCP_WDT] = {
+        "Watchdog timer", ppc_intr_watchdog
+    },
+
+    [POWERPC_EXCP_DECR]  = { "Decrementer",                ppc_intr_noop },
+    [POWERPC_EXCP_DTLB]  = { "Data TLB error",             ppc_intr_noop },
+    [POWERPC_EXCP_FPU]   = { "Floating-point unavailable", ppc_intr_noop },
+    [POWERPC_EXCP_ITLB]  = { "Instruction TLB error",      ppc_intr_noop },
+    [POWERPC_EXCP_TRACE] = { "Trace",                      ppc_intr_noop },
+
+/* Not implemented */
+    [POWERPC_EXCP_DABR]     = { "Data address breakpoint" },
+    [POWERPC_EXCP_DTLBE]    = { "Data TLB error" },
+    [POWERPC_EXCP_EMUL]     = { "Emulation trap" },
+    [POWERPC_EXCP_FPA]      = { "Floating-point assist" },
+    [POWERPC_EXCP_IABR]     = { "Insn address breakpoint" },
+    [POWERPC_EXCP_IO]       = { "IO error" },
+    [POWERPC_EXCP_ITLBE]    = { "Instruction TLB error" },
+    [POWERPC_EXCP_MEXTBR]   = { "Maskable external" },
+    [POWERPC_EXCP_NMEXTBR]  = { "Non-maskable external" },
+    [POWERPC_EXCP_PERFM]    = { "Performance counter" },
+    [POWERPC_EXCP_RUNM]     = { "Run mode" },
+    [POWERPC_EXCP_SMI]      = { "System management" },
+    [POWERPC_EXCP_THERM]    = { "Thermal management" },
+    [POWERPC_EXCP_VPUA]     = { "Vector assist" },
+};
+
+void ppc32_excp(PowerPCCPU *cpu, int excp)
+{
+    CPUState *cs = CPU(cpu);
+    CPUPPCState *env = &cpu->env;
+    PPCIntrArgs regs;
+    bool ignore;
+
+    regs.msr = env->msr & ~0x783f0000ULL;
+    regs.nip = env->nip;
+
+    /*
+     * new interrupt handler msr preserves existing HV and ME unless
+     * explicitly overriden
+     */
+    regs.new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB);
+
+    regs.sprn_srr0 = SPR_SRR0;
+    regs.sprn_srr1 = SPR_SRR1;
+
+    /*
+     * Hypervisor emulation assistance interrupt only exists on server
+     * arch 2.05 server or later.
+     */
+    if (excp == POWERPC_EXCP_HV_EMU) {
+        excp = POWERPC_EXCP_PROGRAM;
+    }
+
+    regs.new_nip = env->excp_vectors[excp];
+    if (regs.new_nip == (target_ulong)-1ULL) {
+        cpu_abort(cs, "Raised an exception without defined vector %d\n",
+                  excp);
+    }
+
+    regs.new_nip |= env->excp_prefix;
+
+    /* Setup interrupt-specific registers before injecting */
+    ignore = ppc_intr_prepare(cpu, interrupts_ppc32, &regs, excp);
+
+    if (ignore) {
+        /* No further setup is needed for this interrupt */
+        return;
+    }
+
+    if (msr_ile) {
+        regs.new_msr |= (target_ulong)1 << MSR_LE;
+    }
+
+    /* Save PC */
+    env->spr[regs.sprn_srr0] = regs.nip;
+
+    /* Save MSR */
+    env->spr[regs.sprn_srr1] = regs.msr;
+
+    powerpc_set_excp_state(cpu, regs.new_nip, regs.new_msr);
+}
diff --git a/target/ppc/meson.build b/target/ppc/meson.build
index 53b8e0a98e..9ec335d438 100644
--- a/target/ppc/meson.build
+++ b/target/ppc/meson.build
@@ -40,9 +40,11 @@  ppc_softmmu_ss.add(files(
   'mmu_common.c',
   'monitor.c',
   'interrupts.c',
+  'intr-booke.c',
 ))
 ppc_softmmu_ss.add(when: 'CONFIG_TCG', if_true: files(
   'mmu_helper.c',
+  'intr-ppc32.c',
 ), if_false: files(
   'tcg-stub.c',
 ))
@@ -53,6 +55,7 @@  ppc_softmmu_ss.add(when: 'TARGET_PPC64', if_true: files(
   'mmu-hash64.c',
   'mmu-radix64.c',
   'power8-pmu.c',
+  'intr-book3s.c',
 ))
 
 target_arch += {'ppc': ppc_ss}
diff --git a/target/ppc/ppc_intr.h b/target/ppc/ppc_intr.h
index a12b3a9e4d..a0362f4248 100644
--- a/target/ppc/ppc_intr.h
+++ b/target/ppc/ppc_intr.h
@@ -27,9 +27,6 @@  void ppc_intr_embedded_doorbell_crit(PowerPCCPU *cpu, PPCIntrArgs *regs, bool *i
 void ppc_intr_external(PowerPCCPU *cpu, PPCIntrArgs *regs, bool *ignore);
 void ppc_intr_facility_unavail(PowerPCCPU *cpu, PPCIntrArgs *regs, bool *ignore);
 void ppc_intr_fit(PowerPCCPU *cpu, PPCIntrArgs *regs, bool *ignore);
-void ppc_intr_hv(PowerPCCPU *cpu, PPCIntrArgs *regs, bool *ignore);
-void ppc_intr_hv_facility_unavail(PowerPCCPU *cpu, PPCIntrArgs *regs, bool *ignore);
-void ppc_intr_hv_insn_storage(PowerPCCPU *cpu, PPCIntrArgs *regs, bool *ignore);
 void ppc_intr_insn_storage(PowerPCCPU *cpu, PPCIntrArgs *regs, bool *ignore);
 void ppc_intr_machine_check(PowerPCCPU *cpu, PPCIntrArgs *regs, bool *ignore);
 void ppc_intr_noop(PowerPCCPU *cpu, PPCIntrArgs *regs, bool *ignore);
@@ -42,11 +39,20 @@  void ppc_intr_system_reset(PowerPCCPU *cpu, PPCIntrArgs *regs, bool *ignore);
 void ppc_intr_tlb_miss(PowerPCCPU *cpu, PPCIntrArgs *regs, bool *ignore);
 void ppc_intr_watchdog(PowerPCCPU *cpu, PPCIntrArgs *regs, bool *ignore);
 
+#ifdef TARGET_PPC64
+void ppc_intr_hv(PowerPCCPU *cpu, PPCIntrArgs *regs, bool *ignore);
+void ppc_intr_hv_facility_unavail(PowerPCCPU *cpu, PPCIntrArgs *regs, bool *ignore);
+void ppc_intr_hv_insn_storage(PowerPCCPU *cpu, PPCIntrArgs *regs, bool *ignore);
+#endif
+
 int ppc_intr_prepare(PowerPCCPU *cpu, PPCInterrupt *interrupts,
                      PPCIntrArgs *regs, int excp);
 
-extern PPCInterrupt interrupts_ppc32[POWERPC_EXCP_NB];
-extern PPCInterrupt interrupts_booke[POWERPC_EXCP_NB];
-extern PPCInterrupt interrupts_book3s[POWERPC_EXCP_NB];
+void ppc32_excp(PowerPCCPU *cpu, int excp);
+void booke_excp(PowerPCCPU *cpu, int excp);
+
+#ifdef TARGET_PPC64
+void book3s_excp(PowerPCCPU *cpu, int excp);
+#endif
 
 #endif /* PPC_INTR_H */
diff --git a/target/ppc/tcg-stub.c b/target/ppc/tcg-stub.c
index aadcf59d26..2b40258b01 100644
--- a/target/ppc/tcg-stub.c
+++ b/target/ppc/tcg-stub.c
@@ -20,6 +20,7 @@ 
 #include "cpu.h"
 #include "internal.h"
 #include "hw/ppc/spapr.h"
+#include "ppc_intr.h"
 
 void create_ppc_opcodes(PowerPCCPU *cpu, Error **errp)
 {
@@ -43,3 +44,8 @@  target_ulong softmmu_resize_hpt_commit(PowerPCCPU *cpu,
 {
     g_assert_not_reached();
 }
+
+void ppc32_excp(PowerPCCPU *cpu, int excp)
+{
+    g_assert_not_reached();
+}