Patchwork [13/15] Add POWER7 support for ppc

login
register
mail settings
Submitter David Gibson
Date Feb. 12, 2011, 2:54 p.m.
Message ID <1297522467-5975-14-git-send-email-david@gibson.dropbear.id.au>
Download mbox | patch
Permalink /patch/82928/
State New
Headers show

Comments

David Gibson - Feb. 12, 2011, 2:54 p.m.
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 <dwg@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(-)
Alexander Graf - Feb. 12, 2011, 4:09 p.m.
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 <dwg@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
David Gibson - Feb. 13, 2011, 9:39 a.m.
On Sat, Feb 12, 2011 at 05:09:39PM +0100, Alexander Graf wrote:
> On 12.02.2011, at 15:54, David Gibson wrote:
[snip]
> > +    /* 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.

[snip]
> > +        case POWER7_INPUT_CKSTP:
> 
> POWER7 has checkstop?

[snip]
> > +        case POWER7_INPUT_HRESET:
> 
> Does this ever get triggered? POWER7 is run in lpar only, so there is no hreset, right?

[snip]
> > +        case POWER7_INPUT_TBEN:
> > +            LOG_IRQ("%s: set the TBEN state to %d\n", __func__,
> > +                        level);
> > +            /* XXX: TODO */
> 
> Hrm - what is this?

Ah, drat.  I forgot about this.  The POWER7 interrupt stuff I copied
from 970 and them modified minimally to get it working.  I meant to
get around to auditing this stuff to see what was actually relevant to
POWER7.  I'll address this for the next version.

[snip]
> > +#if !defined(CONFIG_USER_ONLY)
> > +    env->slb_nr = 32;
> 
> POWER7 has 64, no? Please check this :).

Nope.  POWER4 and POWER5 have 64, but POWER7 has 32.  This one I did
check and change.
Alexander Graf - Feb. 13, 2011, 12:37 p.m.
On 13.02.2011, at 10:39, David Gibson wrote:

> On Sat, Feb 12, 2011 at 05:09:39PM +0100, Alexander Graf wrote:
>> On 12.02.2011, at 15:54, David Gibson wrote:
> [snip]
>>> +    /* 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.
> 
> [snip]
>>> +        case POWER7_INPUT_CKSTP:
>> 
>> POWER7 has checkstop?
> 
> [snip]
>>> +        case POWER7_INPUT_HRESET:
>> 
>> Does this ever get triggered? POWER7 is run in lpar only, so there is no hreset, right?
> 
> [snip]
>>> +        case POWER7_INPUT_TBEN:
>>> +            LOG_IRQ("%s: set the TBEN state to %d\n", __func__,
>>> +                        level);
>>> +            /* XXX: TODO */
>> 
>> Hrm - what is this?
> 
> Ah, drat.  I forgot about this.  The POWER7 interrupt stuff I copied
> from 970 and them modified minimally to get it working.  I meant to
> get around to auditing this stuff to see what was actually relevant to
> POWER7.  I'll address this for the next version.
> 
> [snip]
>>> +#if !defined(CONFIG_USER_ONLY)
>>> +    env->slb_nr = 32;
>> 
>> POWER7 has 64, no? Please check this :).
> 
> Nope.  POWER4 and POWER5 have 64, but POWER7 has 32.  This one I did
> check and change.

Oh? Interesting. Good to know :)


Alex

Patch

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)) {
+        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:
+            /* 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:
+            /* 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 */
+            break;
+        default:
+            /* Unknown pin - do nothing */
+            LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin);
+            return;
+        }
+        if (level)
+            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;
+#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)                                                    */