On 12.02.2011, at 15:54, David Gibson wrote: > This adds emulation support for the recent POWER7 cpu to qemu. It's far > from perfect - it's missing a number of POWER7 features so far, including > any support for VSX or decimal floating point instructions. However, it's > close enough to boot a kernel with the POWER7 PVR. > > Signed-off-by: David Gibson <d...@au1.ibm.com> > --- > hw/ppc.c | 83 ++++++++++++++++++++++++++++++++++ > hw/ppc.h | 1 + > target-ppc/cpu.h | 19 ++++++++ > target-ppc/helper.c | 6 +++ > target-ppc/translate_init.c | 103 +++++++++++++++++++++++++++++++++++++++++++ > 5 files changed, 212 insertions(+), 0 deletions(-) > > diff --git a/hw/ppc.c b/hw/ppc.c > index 968aec1..6975636 100644 > --- a/hw/ppc.c > +++ b/hw/ppc.c > @@ -246,6 +246,89 @@ void ppc970_irq_init (CPUState *env) > env->irq_inputs = (void **)qemu_allocate_irqs(&ppc970_set_irq, env, > PPC970_INPUT_NB); > } > + > +/* POWER7 internal IRQ controller */ > +static void power7_set_irq (void *opaque, int pin, int level) > +{ > + CPUState *env = opaque; > + int cur_level; > + > + LOG_IRQ("%s: env %p pin %d level %d\n", __func__, > + env, pin, level); > + cur_level = (env->irq_input_state >> pin) & 1; > + /* Don't generate spurious events */ > + if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) {
Did you hit this? Qemu's irq framework should already ensure that property. I'm also not sure it's actually correct - if a level interrupt is on, the guest would get another interrupt injected, no? That would be cur_level ==1 && level == 1 IIUC. > + switch (pin) { > + case POWER7_INPUT_INT: > + /* Level sensitive - active high */ > + LOG_IRQ("%s: set the external IRQ state to %d\n", > + __func__, level); > + ppc_set_irq(env, PPC_INTERRUPT_EXT, level); > + break; > + case POWER7_INPUT_THINT: > + /* Level sensitive - active high */ > + LOG_IRQ("%s: set the SMI IRQ state to %d\n", __func__, > + level); > + ppc_set_irq(env, PPC_INTERRUPT_THERM, level); > + break; > + case POWER7_INPUT_MCP: > + /* Negative edge sensitive */ > + /* XXX: TODO: actual reaction may depends on HID0 status > + * 603/604/740/750: check HID0[EMCP] > + */ > + if (cur_level == 1 && level == 0) { > + LOG_IRQ("%s: raise machine check state\n", > + __func__); > + ppc_set_irq(env, PPC_INTERRUPT_MCK, 1); > + } > + break; > + case POWER7_INPUT_CKSTP: POWER7 has checkstop? > + /* Level sensitive - active low */ > + /* XXX: TODO: relay the signal to CKSTP_OUT pin */ > + if (level) { > + LOG_IRQ("%s: stop the CPU\n", __func__); > + env->halted = 1; > + } else { > + LOG_IRQ("%s: restart the CPU\n", __func__); > + env->halted = 0; > + } > + break; > + case POWER7_INPUT_HRESET: Does this ever get triggered? POWER7 is run in lpar only, so there is no hreset, right? > + /* Level sensitive - active low */ > + if (level) { > +#if 0 // XXX: TOFIX > + LOG_IRQ("%s: reset the CPU\n", __func__); > + cpu_reset(env); > +#endif > + } > + break; > + case POWER7_INPUT_SRESET: > + LOG_IRQ("%s: set the RESET IRQ state to %d\n", > + __func__, level); > + ppc_set_irq(env, PPC_INTERRUPT_RESET, level); > + break; > + case POWER7_INPUT_TBEN: > + LOG_IRQ("%s: set the TBEN state to %d\n", __func__, > + level); > + /* XXX: TODO */ Hrm - what is this? > + break; > + default: > + /* Unknown pin - do nothing */ > + LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); > + return; > + } > + if (level) Braces > + env->irq_input_state |= 1 << pin; > + else > + env->irq_input_state &= ~(1 << pin); > + } > +} > + > +void ppcPOWER7_irq_init (CPUState *env) > +{ > + env->irq_inputs = (void **)qemu_allocate_irqs(&power7_set_irq, env, > + POWER7_INPUT_NB); > +} > #endif /* defined(TARGET_PPC64) */ > > /* PowerPC 40x internal IRQ controller */ > diff --git a/hw/ppc.h b/hw/ppc.h > index 34f54cf..3ccf134 100644 > --- a/hw/ppc.h > +++ b/hw/ppc.h > @@ -36,6 +36,7 @@ void ppc40x_irq_init (CPUState *env); > void ppce500_irq_init (CPUState *env); > void ppc6xx_irq_init (CPUState *env); > void ppc970_irq_init (CPUState *env); > +void ppcPOWER7_irq_init (CPUState *env); > > /* PPC machines for OpenBIOS */ > enum { > diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h > index 53b788f..fa3cd7f 100644 > --- a/target-ppc/cpu.h > +++ b/target-ppc/cpu.h > @@ -119,6 +119,8 @@ enum powerpc_mmu_t { > POWERPC_MMU_64B = POWERPC_MMU_64 | 0x00000001, > /* 620 variant (no segment exceptions) */ > POWERPC_MMU_620 = POWERPC_MMU_64 | 0x00000002, > + /* Architecture 2.06 variant */ > + POWERPC_MMU_2_06 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG | 0x00000003, > #endif /* defined(TARGET_PPC64) */ > }; > > @@ -154,6 +156,8 @@ enum powerpc_excp_t { > #if defined(TARGET_PPC64) > /* PowerPC 970 exception model */ > POWERPC_EXCP_970, > + /* POWER7 exception model */ > + POWERPC_EXCP_POWER7, > #endif /* defined(TARGET_PPC64) */ > }; > > @@ -289,6 +293,8 @@ enum powerpc_input_t { > PPC_FLAGS_INPUT_405, > /* PowerPC 970 bus */ > PPC_FLAGS_INPUT_970, > + /* PowerPC POWER7 bus */ > + PPC_FLAGS_INPUT_POWER7, > /* PowerPC 401 bus */ > PPC_FLAGS_INPUT_401, > /* Freescale RCPU bus */ > @@ -1003,6 +1009,7 @@ static inline void cpu_clone_regs(CPUState *env, > target_ulong newsp) > #define SPR_HSPRG1 (0x131) > #define SPR_HDSISR (0x132) > #define SPR_HDAR (0x133) > +#define SPR_SPURR (0x134) > #define SPR_BOOKE_DBCR0 (0x134) > #define SPR_IBCR (0x135) > #define SPR_PURR (0x135) > @@ -1627,6 +1634,18 @@ enum { > PPC970_INPUT_THINT = 6, > PPC970_INPUT_NB, > }; > + > +enum { > + /* POWER7 input pins */ > + POWER7_INPUT_HRESET = 0, > + POWER7_INPUT_SRESET = 1, > + POWER7_INPUT_CKSTP = 2, > + POWER7_INPUT_TBEN = 3, > + POWER7_INPUT_MCP = 4, > + POWER7_INPUT_INT = 5, > + POWER7_INPUT_THINT = 6, > + POWER7_INPUT_NB, > +}; > #endif > > /* Hardware exceptions definitions */ > diff --git a/target-ppc/helper.c b/target-ppc/helper.c > index 158da09..a630148 100644 > --- a/target-ppc/helper.c > +++ b/target-ppc/helper.c > @@ -1192,6 +1192,7 @@ static inline int check_physical(CPUState *env, > mmu_ctx_t *ctx, > #if defined(TARGET_PPC64) > case POWERPC_MMU_620: > case POWERPC_MMU_64B: > + case POWERPC_MMU_2_06: > /* Real address are 60 bits long */ > ctx->raddr &= 0x0FFFFFFFFFFFFFFFULL; > ctx->prot |= PAGE_WRITE; > @@ -1269,6 +1270,7 @@ int get_physical_address (CPUState *env, mmu_ctx_t > *ctx, target_ulong eaddr, > #if defined(TARGET_PPC64) > case POWERPC_MMU_620: > case POWERPC_MMU_64B: > + case POWERPC_MMU_2_06: > #endif > if (ret < 0) { > /* We didn't match any BAT entry or don't have BATs */ > @@ -1368,6 +1370,7 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, > target_ulong address, int rw, > #if defined(TARGET_PPC64) > case POWERPC_MMU_620: > case POWERPC_MMU_64B: > + case POWERPC_MMU_2_06: > #endif > env->exception_index = POWERPC_EXCP_ISI; > env->error_code = 0x40000000; > @@ -1475,6 +1478,7 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, > target_ulong address, int rw, > #if defined(TARGET_PPC64) > case POWERPC_MMU_620: > case POWERPC_MMU_64B: > + case POWERPC_MMU_2_06: > #endif > env->exception_index = POWERPC_EXCP_DSI; > env->error_code = 0; > @@ -1798,6 +1802,7 @@ void ppc_tlb_invalidate_all (CPUPPCState *env) > #if defined(TARGET_PPC64) > case POWERPC_MMU_620: > case POWERPC_MMU_64B: > + case POWERPC_MMU_2_06: > #endif /* defined(TARGET_PPC64) */ > tlb_flush(env, 1); > break; > @@ -1865,6 +1870,7 @@ void ppc_tlb_invalidate_one (CPUPPCState *env, > target_ulong addr) > #if defined(TARGET_PPC64) > case POWERPC_MMU_620: > case POWERPC_MMU_64B: > + case POWERPC_MMU_2_06: > /* tlbie invalidate TLBs for all segments */ > /* XXX: given the fact that there are too many segments to invalidate, > * and we still don't have a tlb_flush_mask(env, n, mask) in > Qemu, > diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c > index c84581e..2faa591 100644 > --- a/target-ppc/translate_init.c > +++ b/target-ppc/translate_init.c > @@ -61,6 +61,7 @@ void glue(glue(ppc, name),_irq_init) (CPUPPCState *env); > PPC_IRQ_INIT_FN(40x); > PPC_IRQ_INIT_FN(6xx); > PPC_IRQ_INIT_FN(970); > +PPC_IRQ_INIT_FN(POWER7); > PPC_IRQ_INIT_FN(e500); > > /* Generic callbacks: > @@ -3087,6 +3088,35 @@ static void init_excp_970 (CPUPPCState *env) > env->hreset_vector = 0x0000000000000100ULL; > #endif > } > + > +static void init_excp_POWER7 (CPUPPCState *env) > +{ > +#if !defined(CONFIG_USER_ONLY) > + env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; > + env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200; > + env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300; > + env->excp_vectors[POWERPC_EXCP_DSEG] = 0x00000380; > + env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400; > + env->excp_vectors[POWERPC_EXCP_ISEG] = 0x00000480; > + env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500; > + env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600; > + env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700; > + env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800; > + env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900; > + env->excp_vectors[POWERPC_EXCP_HDECR] = 0x00000980; > + env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00; > + env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00; > + env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00; > + env->excp_vectors[POWERPC_EXCP_VPU] = 0x00000F20; > + env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300; > + env->excp_vectors[POWERPC_EXCP_MAINT] = 0x00001600; > + env->excp_vectors[POWERPC_EXCP_VPUA] = 0x00001700; > + env->excp_vectors[POWERPC_EXCP_THERM] = 0x00001800; > + env->hreset_excp_prefix = 0x00000000FFF00000ULL; > + /* Hardware reset vector */ > + env->hreset_vector = 0x0000000000000100ULL; > +#endif > +} > #endif > > /*****************************************************************************/ > @@ -6268,6 +6298,74 @@ static void init_proc_970MP (CPUPPCState *env) > vscr_init(env, 0x00010000); > } > > +/* POWER7 (actually a somewhat hacked 970FX for now...) */ > +#define POWERPC_INSNS_POWER7 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | > \ > + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | > \ > + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | > \ > + PPC_FLOAT_STFIWX | > \ > + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | > \ > + PPC_MEM_SYNC | PPC_MEM_EIEIO | > \ > + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | > \ > + PPC_64B | PPC_ALTIVEC | > \ > + PPC_SEGMENT_64B | PPC_SLBI | > \ > + PPC_POPCNTB | PPC_POPCNTWD) > +#define POWERPC_MSRM_POWER7 (0x800000000204FF36ULL) > +#define POWERPC_MMU_POWER7 (POWERPC_MMU_2_06) > +#define POWERPC_EXCP_POWER7 (POWERPC_EXCP_POWER7) > +#define POWERPC_INPUT_POWER7 (PPC_FLAGS_INPUT_POWER7) > +#define POWERPC_BFDM_POWER7 (bfd_mach_ppc64) > +#define POWERPC_FLAG_POWER7 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | > \ > + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | > \ > + POWERPC_FLAG_BUS_CLK) > +#define check_pow_POWER7 check_pow_nocheck > + > +static void init_proc_POWER7 (CPUPPCState *env) > +{ > + gen_spr_ne_601(env); > + gen_spr_7xx(env); > + /* Time base */ > + gen_tbl(env); > + /* PURR & SPURR: Hack - treat these as aliases for the TB for now */ > + spr_register(env, SPR_PURR, "PURR", > + &spr_read_purr, SPR_NOACCESS, > + &spr_read_purr, SPR_NOACCESS, > + 0x00000000); > + spr_register(env, SPR_SPURR, "SPURR", > + &spr_read_purr, SPR_NOACCESS, > + &spr_read_purr, SPR_NOACCESS, > + 0x00000000); > + /* Memory management */ > + /* XXX : not implemented */ > + spr_register(env, SPR_MMUCFG, "MMUCFG", > + SPR_NOACCESS, SPR_NOACCESS, > + &spr_read_generic, SPR_NOACCESS, > + 0x00000000); /* TOFIX */ > + /* XXX : not implemented */ > + spr_register(env, SPR_CTRL, "SPR_CTRLT", > + SPR_NOACCESS, SPR_NOACCESS, > + &spr_read_generic, &spr_write_generic, > + 0x80800000); > + spr_register(env, SPR_UCTRL, "SPR_CTRLF", > + SPR_NOACCESS, SPR_NOACCESS, > + &spr_read_generic, &spr_write_generic, > + 0x80800000); > + spr_register(env, SPR_VRSAVE, "SPR_VRSAVE", > + &spr_read_generic, &spr_write_generic, > + &spr_read_generic, &spr_write_generic, > + 0x00000000); > +#if !defined(CONFIG_USER_ONLY) > + env->slb_nr = 32; POWER7 has 64, no? Please check this :). > +#endif > + init_excp_POWER7(env); > + env->dcache_line_size = 128; > + env->icache_line_size = 128; > + /* Allocate hardware IRQ controller */ > + ppcPOWER7_irq_init(env); > + /* Can't find information on what this should be on reset. This > + * value is the one used by 74xx processors. */ > + vscr_init(env, 0x00010000); > +} > + > /* PowerPC 620 > */ > #define POWERPC_INSNS_620 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | > \ > PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | > \ > @@ -6990,6 +7088,8 @@ enum { > CPU_POWERPC_POWER6 = 0x003E0000, > CPU_POWERPC_POWER6_5 = 0x0F000001, /* POWER6 in POWER5 mode */ > CPU_POWERPC_POWER6A = 0x0F000002, > +#define CPU_POWERPC_POWER7 CPU_POWERPC_POWER7_v20 > + CPU_POWERPC_POWER7_v20 = 0x003F0200, > CPU_POWERPC_970 = 0x00390202, > #define CPU_POWERPC_970FX CPU_POWERPC_970FX_v31 > CPU_POWERPC_970FX_v10 = 0x00391100, > @@ -8792,6 +8892,9 @@ static const ppc_def_t ppc_defs[] = { > /* POWER6A > */ > POWERPC_DEF("POWER6A", CPU_POWERPC_POWER6A, POWER6), > #endif > + /* POWER7 > */ > + POWERPC_DEF("POWER7", CPU_POWERPC_POWER7, POWER7), > + POWERPC_DEF("POWER7_v2.0", CPU_POWERPC_POWER7_v20, > POWER7), > /* PowerPC 970 > */ > POWERPC_DEF("970", CPU_POWERPC_970, 970), > /* PowerPC 970FX (G5) > */ Alex