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();
+}
