diff mbox

[5/6] PPC: Implement e500 (FSL) MMU

Message ID 1304348602-28907-6-git-send-email-agraf@suse.de
State New
Headers show

Commit Message

Alexander Graf May 2, 2011, 3:03 p.m. UTC
Most of the code to support e500 style MMUs is already in place, but
we're missing on some of the special TLB0-TLB1 handling code and slightly

Comments

Scott Wood May 3, 2011, 7:25 p.m. UTC | #1
On Mon, 2 May 2011 17:03:21 +0200
Alexander Graf <agraf@suse.de> wrote:

> Most of the code to support e500 style MMUs is already in place, but
> we're missing on some of the special TLB0-TLB1 handling code and slightly
> different TLB modification.
> 
> This patch adds support for the FSL style MMU.
> 
> Signed-off-by: Alexander Graf <agraf@suse.de>

You beat me to (part of) it... :-)

How is this going to interact with the KVM MMU API, where it was suggested
that qemu use that representation as its native structure?  Should we just
change the representation at that point, for both types of booke mmu (so 4xx
would be represented with MAS registers internally, even though a
different interface is exposed to the guest)?

> @@ -678,8 +887,9 @@ struct CPUPPCState {
>      int nb_BATs;
>      target_ulong DBAT[2][8];
>      target_ulong IBAT[2][8];
> -    /* PowerPC TLB registers (for 4xx and 60x software driven TLBs) */
> +    /* PowerPC TLB registers (for 4xx, e500 and 60x software driven TLBs) */
>      int nb_tlb;      /* Total number of TLB                                  */
> +    int nb_tlbs[4];  /* Number of respective TLB entries (e500)              */

Looks like "number of tlbs", not "number of tlb entries"... Was less
confusing when there was only one tlb array.

> diff --git a/target-ppc/helper.c b/target-ppc/helper.c
> index 5e4030b..261c1a9 100644
> --- a/target-ppc/helper.c
> +++ b/target-ppc/helper.c
> @@ -1153,48 +1153,144 @@ void store_40x_sler (CPUPPCState *env, uint32_t val)
>      env->spr[SPR_405_SLER] = val;
>  }
>  
> +static inline int mmubooke_check_tlb (CPUState *env, ppcemb_tlb_t *tlb,
> +                                      target_phys_addr_t *raddr, int *prot,
> +                                      target_ulong address, int rw,
> +                                      int access_type, int i)
> +{
> +    int ret, _prot;
> +
> +    if (ppcemb_tlb_check(env, tlb, raddr, address,
> +                         env->spr[SPR_BOOKE_PID],
> +                         !env->nb_pids, i) >= 0) {
> +        goto found_tlb;
> +    }
> +
> +    if ((env->nb_pids > 1) && env->spr[SPR_BOOKE_PID1] &&
> +        ppcemb_tlb_check(env, tlb, raddr, address,
> +                         env->spr[SPR_BOOKE_PID1], 0, i) >= 0) {
> +        goto found_tlb;
> +    }
> +
> +    if ((env->nb_pids > 2) && env->spr[SPR_BOOKE_PID2] &&
> +        ppcemb_tlb_check(env, tlb, raddr, address,
> +                         env->spr[SPR_BOOKE_PID2], 0, i) >= 0) {
> +        goto found_tlb;
> +    }

Maybe PID1/PID2 should be moved into ppcemb_tlb_check, and eliminate the
nb_pids checks by putting zero in PID1/PID2 on chips that don't have it?
I'm assuming this is performance sensitive for non-KVM.

> +static int mmue500_get_physical_address (CPUState *env, mmu_ctx_t *ctx,
> +                                         target_ulong address, int rw,
> +                                         int access_type)
> +{
> +    ppcemb_tlb_t *tlb;
> +    target_phys_addr_t raddr;
> +    int i, ret;
> +    uint32_t ea = (address >> MAS2_EPN_SHIFT) & 0x7f;
> +
> +    ret = -1;
> +    raddr = (target_phys_addr_t)-1ULL;
> +
> +    /* check TLB1 - hits often because the kernel is here */

I'd optimize for userspace instead -- that's where the real work is more
likely to happen.  Plus, compare what's likely to be 5-6 iterations before
finding the entry for a kernel large-page entry if we check TLB0 first,
versus 17+ iterations (65+ on e500mc) for finding a TLB0 entry if TLB1 is
checked first -- based on Linux's TLB1 usage patterns.

> +    for (i = env->nb_tlbs[0]; i < env->nb_tlb; i++) {
> +        tlb = &env->tlb[i].tlbe;
> +        ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, rw,
> +                                 access_type, i);
> +        if (!ret) {
> +            goto found_tlb;
>          }
>      }
> -    if (ret >= 0)
> +
> +    /* check possible TLB0 entries */
> +    for (i = 0; i < env->nb_ways; i++) {
> +        tlb = &env->tlb[ea | (i << 7)].tlbe;

Do we have to hardcode 7 (and 0x7f) here?

And if possible I'd rearrange the tlb so that ways within a set are
contiguous.  Better for cache utilization, and is what the KVM MMU API will
want.

> @@ -1348,6 +1446,44 @@ target_phys_addr_t cpu_get_phys_page_debug (CPUState *env, target_ulong addr)
>      return ctx.raddr & TARGET_PAGE_MASK;
>  }
>  
> +static void e500_update_mas_tlb_miss(CPUState *env, target_ulong address,
> +                                     int rw)
> +{
> +    env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK;
> +    env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK;
> +    env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK;
> +    env->spr[SPR_BOOKE_MAS6] = 0;
> +
> +    /* AS */
> +    if (((rw == 2) && msr_ir) || ((rw != 2) && msr_dr)) {
> +        env->spr[SPR_BOOKE_MAS1] |= MAS1_TS;
> +        env->spr[SPR_BOOKE_MAS6] |= MAS6_SAS;
> +    }
> +
> +    env->spr[SPR_BOOKE_MAS1] |= MAS1_VALID;
> +    env->spr[SPR_BOOKE_MAS2] |= address & MAS2_EPN_MASK;
> +
> +    switch (env->spr[SPR_BOOKE_MAS4] & MAS4_TIDSELD_PIDZ) {
> +    case MAS4_TIDSELD_PID0:
> +        env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID] << MAS1_TID_SHIFT;
> +        break;
> +    case MAS4_TIDSELD_PID1:
> +        env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID1] << MAS1_TID_SHIFT;
> +        break;
> +    case MAS4_TIDSELD_PID2:
> +        env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID2] << MAS1_TID_SHIFT;
> +        break;
> +    }
> +
> +    env->spr[SPR_BOOKE_MAS6] |= env->spr[SPR_BOOKE_PID] << 16;
> +
> +    /* next victim logic */
> +    env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT;
> +    env->last_way++;
> +    env->last_way &= 3;
> +    env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT;
> +}

MAS3/7 should be zeroed according to the architecture.

Better victim selection would check for empty ways, and perhaps keep
last_way on a per-set basis.

> @@ -1820,12 +1956,8 @@ void ppc_tlb_invalidate_all (CPUPPCState *env)
>          cpu_abort(env, "MPC8xx MMU model is not implemented\n");
>          break;
>      case POWERPC_MMU_BOOKE:
> -        tlb_flush(env, 1);
> -        break;
>      case POWERPC_MMU_BOOKE_FSL:
> -        /* XXX: TODO */
> -        if (!kvm_enabled())
> -            cpu_abort(env, "BookE MMU model is not implemented\n");
> +        tlb_flush(env, 1);
>          break;
>      case POWERPC_MMU_32B:
>      case POWERPC_MMU_601:

This flushes env->tlb_table, but don't we still need to clear out env->tlb?

> diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c
> index d5db484..3c0b061 100644
> --- a/target-ppc/op_helper.c
> +++ b/target-ppc/op_helper.c
> @@ -4206,4 +4206,422 @@ target_ulong helper_440_tlbsx (target_ulong address)
>      return ppcemb_tlb_search(env, address, env->spr[SPR_440_MMUCR] & 0xFF);
>  }
>  
> +/* PowerPC e500 TLB management */
> +
> +static inline int e500_tlb_num(CPUState *env, ppcemb_tlb_t *tlb)
> +{
> +    ulong tlbel = (ulong)tlb;
> +    ulong tlbl = (ulong)env->tlb;
> +
> +    return (tlbel - tlbl) / sizeof(env->tlb[0]);
> +}
> +
> +static inline ppcemb_tlb_t *e500_cur_tlb(CPUState *env)

This is a bit big to be inlined -  let the compiler decide such things.

> +{
> +    uint32_t tlbncfg = 0;
> +    int esel = (env->spr[SPR_BOOKE_MAS0] & MAS0_ESEL_MASK) >> MAS0_ESEL_SHIFT;
> +    int ea = (env->spr[SPR_BOOKE_MAS2] & MAS2_EPN_MASK) >> MAS2_EPN_SHIFT;
> +    int tlb;
> +    int r;
> +
> +    tlb = (env->spr[SPR_BOOKE_MAS0] & MAS0_TLBSEL_MASK) >> MAS0_TLBSEL_SHIFT;
> +    tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlb];
> +
> +    if ((tlbncfg & TLBnCFG_HES) && (env->spr[SPR_BOOKE_MAS0] & MAS0_HES)) {
> +        cpu_abort(env, "we don't support HES yet\n");
> +    }
> +
> +    if (tlb == 1) {
> +        r = env->nb_tlbs[0] + (esel % env->nb_tlbs[1]);

Ouch, division by non-constant.

There's no need for it anyway; esel >= env_nb_tlbs[1] is undefined
behavior.  Just crop the value arbitrarily (or error out, if possible) to
avoid dereferencing a bad array index.

> +    } else {
> +        esel &= env->nb_ways - 1;
> +        esel <<= 7;
> +        ea &= 0x7f;
> +        r = (esel | ea) % env->nb_tlbs[0];

More unnecessary division -- use "& (env->nb_tlbs[0] - 1)", or variable-ize
the 7 and 0x7f so that we know that we won't produce an out-of-bounds value.

> +static const target_phys_addr_t e500_tlb_sizes[] = {
> +    0,
> +    4 * 1024,
> +    16 * 1024,
> +    64 * 1024,
> +    256 * 1024,
> +    1024 * 1024,
> +    4 * 1024 * 1024,
> +    16 * 1024 * 1024,
> +    64 * 1024 * 1024,
> +    256 * 1024 * 1024,
> +    1024 * 1024 * 1024,
> +    (target_phys_addr_t)(4ULL * 1024ULL * 1024ULL * 1024ULL),
> +};

FWIW, power-of-2 page sizes are architected, and may appear in future
e500-family chips.  The TSIZE field is extended by one bit on the right
(previously reserved and should-be-zero).

> +static inline target_phys_addr_t e500_tlb_to_page_size(int size)

unsigned int

> +{
> +    target_phys_addr_t r;
> +
> +    if (size > 11) {

if (size >= ARRAY_SIZE(e500_tlb_sizes)) {

> +        /* should not happen */
> +        r = 1024 * 1024 * 1024;

Error message?

+    switch (env->spr[SPR_BOOKE_MAS0] & MAS0_WQ_MASK) {
+    case MAS0_WQ_ALWAYS:
+        /* good to go, write that entry */
+        break;
+    case MAS0_WQ_COND:
+        /* XXX check if reserved */
+        if (0) {
+            return;
+        }
+        break;
+    case MAS0_WQ_CLR_RSRV:
+        /* XXX clear entry */
+        return;
+    default:
+        /* no idea what to do */
+        return;
+    }

If the plan is to support such things, might want a more generic name than
"e500" -- we're really talking about isa 2.06 book3e.  The first chip that
implements this stuff will probably not be from Freescale, much less
called "e500". :-)

> +    if (msr_gs) {
> +        cpu_abort(env, "missing HV implementation\n");

What's the policy on aborting versus error-message versus no-op if the
guest invokes undefined/unimplemented behavior?

> +    } else {
> +        rpn = ((uint64_t)env->spr[SPR_BOOKE_MAS7] << 32) |
> +              (env->spr[SPR_BOOKE_MAS3] & 0xfffff000);
> +    }

Not sure why this is in an else-branch versus msr_gs.

> +    tlb->RPN = rpn;
> +
> +    tlb->PID = (env->spr[SPR_BOOKE_MAS1] & MAS1_TID_MASK) >> MAS1_TID_SHIFT;
> +    tlb->size = e500_tlb_to_page_size((env->spr[SPR_BOOKE_MAS1]
> +                      & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT);

e500 manuals document that tsize is ignored in tlb0.

> +    tlb->EPN = (uint32_t)(env->spr[SPR_BOOKE_MAS2] & MAS2_EPN_MASK);
> +    tlb->attr = env->spr[SPR_BOOKE_MAS2] & (MAS2_ACM | MAS2_VLE | MAS2_W |
> +                                            MAS2_I | MAS2_M | MAS2_G | MAS2_E)
> +                << 1;
> +    tlb->attr |= env->spr[SPR_BOOKE_MAS1] & MAS1_IPROT;
> +    tlb->attr |= (env->spr[SPR_BOOKE_MAS3] &
> +                  ((MAS3_U0 | MAS3_U1 | MAS3_U2 | MAS3_U3)) << 8);

Might be nice to #define the encoding of these bits into attr/etc, versus
magic shifts (though it's moot if we're going to switch to a MAS-based
representation).

> +    if (tlb->size == TARGET_PAGE_SIZE) {
> +        tlb_flush_page(env, tlb->EPN);
> +    } else {
> +        tlb_flush(env, 1);
> +    }

Need to check whether the previous entry was a large page needing a full
flush.

> +void helper_e500_tlbre(void)
> +{
> +    ppcemb_tlb_t *tlb = NULL;
> +
> +    tlb = e500_cur_tlb(env);
> +    e500_tlb_to_mas(env, tlb, e500_tlb_num(env, tlb));
> +}
> +
> +/* Generic TLB check function for embedded PowerPC implementations */
> +static inline int ppcemb_tlb_check(CPUState *env, ppcemb_tlb_t *tlb,
> +                                   target_phys_addr_t *raddrp,
> +                                   target_ulong address, uint32_t pid, int ext,
> +                                   int i)

Why is this both here and in helper.c?

> +void helper_e500_tlbsx(target_ulong address_hi, target_ulong address_lo)
> +{
> +    ppcemb_tlb_t *tlb = NULL;
> +    int i;
> +    target_phys_addr_t raddr;
> +    uint32_t spid, sas;
> +    uint32_t ea = (address_lo >> MAS2_EPN_SHIFT) & 0x7f;
> +
> +    spid = (env->spr[SPR_BOOKE_MAS6] & MAS6_SPID_MASK) >> MAS6_SPID_SHIFT;
> +    sas = env->spr[SPR_BOOKE_MAS6] & MAS6_SAS;
> +
> +    /* check possible TLB0 entries */
> +    for (i = 0; i < env->nb_ways; i++) {
> +        tlb = &env->tlb[ea | (i << 7)].tlbe;
> +
> +        if (ppcemb_tlb_check(env, tlb, &raddr, address_lo, spid, 0, i) < 0) {
> +            continue;
> +        }
> +
> +        if (sas != (tlb->attr & MAS6_SAS)) {
> +            continue;
> +        }
> +
> +        e500_tlb_to_mas(env, tlb, ea | (i << 7));
> +        return;
> +    }
> +
> +    /* check TLB1 */
> +    for (i = env->nb_tlbs[0]; i < (env->nb_tlbs[0] + env->nb_tlbs[1]); i++) {
> +        tlb = &env->tlb[i].tlbe;
> +
> +        if (ppcemb_tlb_check(env, tlb, &raddr, address_lo, spid, 0, i) < 0) {
> +            continue;
> +        }
> +
> +        if (sas != (tlb->attr & MAS6_SAS)) {
> +            continue;
> +        }
> +
> +        e500_tlb_to_mas(env, tlb, i);
> +        return;
> +    }

This has a lot of overlap with mmue500_get_physical_address()...

> +
> +    /* no entry found, fill with defaults */
> +    env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK;
> +    env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK;
> +    env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK;
> +
> +    if (env->spr[SPR_BOOKE_MAS6] & MAS6_SAS) {
> +        env->spr[SPR_BOOKE_MAS1] |= MAS1_TS;
> +    }
> +
> +    env->spr[SPR_BOOKE_MAS1] |= (env->spr[SPR_BOOKE_MAS6] >> 16)
> +                                << MAS1_TID_SHIFT;
> +
> +    /* next victim logic */
> +    env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT;
> +    env->last_way++;
> +    env->last_way &= 3;
> +    env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT;

...and this with e500_update_mas_tlb_miss().

> @@ -8433,7 +8505,7 @@ GEN_HANDLER2(icbt_40x, "icbt", 0x1F, 0x06, 0x08, 0x03E00001, PPC_40x_ICBT),
>  GEN_HANDLER(iccci, 0x1F, 0x06, 0x1E, 0x00000001, PPC_4xx_COMMON),
>  GEN_HANDLER(icread, 0x1F, 0x06, 0x1F, 0x03E00001, PPC_4xx_COMMON),
>  GEN_HANDLER2(rfci_40x, "rfci", 0x13, 0x13, 0x01, 0x03FF8001, PPC_40x_EXCP),
> -GEN_HANDLER(rfci, 0x13, 0x13, 0x01, 0x03FF8001, PPC_BOOKE),
> +GEN_HANDLER_E(rfci, 0x13, 0x13, 0x01, 0x03FF8001, PPC_BOOKE, PPC2_BOOKE_FSL),

What is PPC2_BOOKE_FSL supposed to indicate?

rfci is basic booke.  It's in the 440.

> +GEN_HANDLER2_E(icbt_440, "icbt", 0x1F, 0x16, 0x00, 0x03E00001,
> +               PPC_BOOKE, PPC2_BOOKE_FSL),

"icbt_440" is FSL-specific? :-)

> +static void spr_write_booke_pid (void *opaque, int sprn, int gprn)
> +{
> +    gen_store_spr(sprn, cpu_gpr[gprn]);
> +    /* switching context, so need to flush tlb */
> +    gen_helper_tlbia();
> +}

Well, we want to flush the non-guest-visible TLB that doesn't understand
PIDs -- but I'd expect "tlbia" to flush the architected TLB.  8xx, at
least, has both tlbia and an architected TLB.

Plus, we need ppc_tlb_invalidate_all() to clear the architected TLB, unless
we call something different on reset.

> @@ -1578,45 +1614,38 @@ static void gen_spr_BookE_FSL (CPUPPCState *env, uint32_t mas_mask)
>                   SPR_NOACCESS, SPR_NOACCESS,
>                   &spr_read_generic, SPR_NOACCESS,
>                   0x00000000); /* TOFIX */
> -    /* XXX : not implemented */
> -    spr_register(env, SPR_MMUCSR0, "MMUCSR0",
> -                 SPR_NOACCESS, SPR_NOACCESS,
> -                 &spr_read_generic, &spr_write_generic,
> -                 0x00000000); /* TOFIX */
>      switch (env->nb_ways) {
>      case 4:
> -        /* XXX : not implemented */
>          spr_register(env, SPR_BOOKE_TLB3CFG, "TLB3CFG",
>                       SPR_NOACCESS, SPR_NOACCESS,
>                       &spr_read_generic, SPR_NOACCESS,
> -                     0x00000000); /* TOFIX */
> +                     tlbncfg[3]);
>          /* Fallthru */
>      case 3:
> -        /* XXX : not implemented */
>          spr_register(env, SPR_BOOKE_TLB2CFG, "TLB2CFG",
>                       SPR_NOACCESS, SPR_NOACCESS,
>                       &spr_read_generic, SPR_NOACCESS,
> -                     0x00000000); /* TOFIX */
> +                     tlbncfg[2]);
>          /* Fallthru */

In some places you use nb_ways as the associativity of TLB0.  But here it
seems to be the number of TLB arrays?

> @@ -4334,8 +4373,23 @@ static void init_proc_e500 (CPUPPCState *env)
>      /* Memory management */
>  #if !defined(CONFIG_USER_ONLY)
>      env->nb_pids = 3;
> +    env->nb_ways = 2;
> +    env->id_tlbs = 0;
> +    if ((env->spr[SPR_PVR] & 0x00010000)) {
> +        /* e500v2 */
> +        env->nb_tlbs[0] = 512;
> +        tlbncfg[0] = gen_tlbncfg(4, 1, 1, 0, env->nb_tlbs[0]);
> +        tlbncfg[1] = gen_tlbncfg(16, 1, 12, TLBnCFG_AVAIL | TLBnCFG_IPROT, 16);
> +    } else {
> +        /* e500v1 */
> +        env->nb_tlbs[0] = 256;
> +        tlbncfg[0] = gen_tlbncfg(2, 1, 1, 0, env->nb_tlbs[0]);
> +        tlbncfg[1] = gen_tlbncfg(16, 1, 9, TLBnCFG_AVAIL | TLBnCFG_IPROT, 16);
> +    }

It would be nice to be more precise with PVR testing, and complain if an
unrecognized PVR is used (e.g. e500mc).

-Scott
Alexander Graf May 6, 2011, 10:01 a.m. UTC | #2
On 03.05.2011, at 21:25, Scott Wood wrote:

> On Mon, 2 May 2011 17:03:21 +0200
> Alexander Graf <agraf@suse.de> wrote:
> 
>> Most of the code to support e500 style MMUs is already in place, but
>> we're missing on some of the special TLB0-TLB1 handling code and slightly
>> different TLB modification.
>> 
>> This patch adds support for the FSL style MMU.
>> 
>> Signed-off-by: Alexander Graf <agraf@suse.de>
> 
> You beat me to (part of) it... :-)
> 
> How is this going to interact with the KVM MMU API, where it was suggested
> that qemu use that representation as its native structure?  Should we just
> change the representation at that point, for both types of booke mmu (so 4xx
> would be represented with MAS registers internally, even though a
> different interface is exposed to the guest)?

Ugh - I completely forgot about that one. I guess we'll just have to redo the internal TLB resolution code at that point, yes. I somehow prefer to work my way from working solution to working solution though, so I don't think I'll move it over to a MAS based approach just yet.

> 
>> @@ -678,8 +887,9 @@ struct CPUPPCState {
>>     int nb_BATs;
>>     target_ulong DBAT[2][8];
>>     target_ulong IBAT[2][8];
>> -    /* PowerPC TLB registers (for 4xx and 60x software driven TLBs) */
>> +    /* PowerPC TLB registers (for 4xx, e500 and 60x software driven TLBs) */
>>     int nb_tlb;      /* Total number of TLB                                  */
>> +    int nb_tlbs[4];  /* Number of respective TLB entries (e500)              */
> 
> Looks like "number of tlbs", not "number of tlb entries"... Was less
> confusing when there was only one tlb array.

I certainly agree, but this way I don't have to change non-e500 code :). I actually started off creating a new TLB struct with 4 entries (the 4 TLBs allowed by the 2.06 spec), but quickly gave up as it's a _lot_ of code using these O_o.

Either way, it's all different now anyways :).

>> diff --git a/target-ppc/helper.c b/target-ppc/helper.c
>> index 5e4030b..261c1a9 100644
>> --- a/target-ppc/helper.c
>> +++ b/target-ppc/helper.c
>> @@ -1153,48 +1153,144 @@ void store_40x_sler (CPUPPCState *env, uint32_t val)
>>     env->spr[SPR_405_SLER] = val;
>> }
>> 
>> +static inline int mmubooke_check_tlb (CPUState *env, ppcemb_tlb_t *tlb,
>> +                                      target_phys_addr_t *raddr, int *prot,
>> +                                      target_ulong address, int rw,
>> +                                      int access_type, int i)
>> +{
>> +    int ret, _prot;
>> +
>> +    if (ppcemb_tlb_check(env, tlb, raddr, address,
>> +                         env->spr[SPR_BOOKE_PID],
>> +                         !env->nb_pids, i) >= 0) {
>> +        goto found_tlb;
>> +    }
>> +
>> +    if ((env->nb_pids > 1) && env->spr[SPR_BOOKE_PID1] &&
>> +        ppcemb_tlb_check(env, tlb, raddr, address,
>> +                         env->spr[SPR_BOOKE_PID1], 0, i) >= 0) {
>> +        goto found_tlb;
>> +    }
>> +
>> +    if ((env->nb_pids > 2) && env->spr[SPR_BOOKE_PID2] &&
>> +        ppcemb_tlb_check(env, tlb, raddr, address,
>> +                         env->spr[SPR_BOOKE_PID2], 0, i) >= 0) {
>> +        goto found_tlb;
>> +    }
> 
> Maybe PID1/PID2 should be moved into ppcemb_tlb_check, and eliminate the
> nb_pids checks by putting zero in PID1/PID2 on chips that don't have it?
> I'm assuming this is performance sensitive for non-KVM.

PID1/PID2 are already 0 on chips that don't have them, so we could eliminate the check on nb_pids, yup. The small branch here really isn't too bad on performance though, so it's more of a readability thing than performance. We're currently killing performance anyways by flushing qemu's internal TLB on every MSR_IR/DR switch.

> 
>> +static int mmue500_get_physical_address (CPUState *env, mmu_ctx_t *ctx,
>> +                                         target_ulong address, int rw,
>> +                                         int access_type)
>> +{
>> +    ppcemb_tlb_t *tlb;
>> +    target_phys_addr_t raddr;
>> +    int i, ret;
>> +    uint32_t ea = (address >> MAS2_EPN_SHIFT) & 0x7f;
>> +
>> +    ret = -1;
>> +    raddr = (target_phys_addr_t)-1ULL;
>> +
>> +    /* check TLB1 - hits often because the kernel is here */
> 
> I'd optimize for userspace instead -- that's where the real work is more
> likely to happen.  Plus, compare what's likely to be 5-6 iterations before
> finding the entry for a kernel large-page entry if we check TLB0 first,
> versus 17+ iterations (65+ on e500mc) for finding a TLB0 entry if TLB1 is
> checked first -- based on Linux's TLB1 usage patterns.

Good point!

> 
>> +    for (i = env->nb_tlbs[0]; i < env->nb_tlb; i++) {
>> +        tlb = &env->tlb[i].tlbe;
>> +        ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, rw,
>> +                                 access_type, i);
>> +        if (!ret) {
>> +            goto found_tlb;
>>         }
>>     }
>> -    if (ret >= 0)
>> +
>> +    /* check possible TLB0 entries */
>> +    for (i = 0; i < env->nb_ways; i++) {
>> +        tlb = &env->tlb[ea | (i << 7)].tlbe;
> 
> Do we have to hardcode 7 (and 0x7f) here?

I'm not sure - the spec isn't exactly obvious on this part. How do I find out the mask from TLBnCFG? But then again I could just take (nb_tlbs[n] - (1 << nb_ways[n])) - 1. I'll give that a try :).

> And if possible I'd rearrange the tlb so that ways within a set are
> contiguous.  Better for cache utilization, and is what the KVM MMU API will
> want.

Ok.

> 
>> @@ -1348,6 +1446,44 @@ target_phys_addr_t cpu_get_phys_page_debug (CPUState *env, target_ulong addr)
>>     return ctx.raddr & TARGET_PAGE_MASK;
>> }
>> 
>> +static void e500_update_mas_tlb_miss(CPUState *env, target_ulong address,
>> +                                     int rw)
>> +{
>> +    env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK;
>> +    env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK;
>> +    env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK;
>> +    env->spr[SPR_BOOKE_MAS6] = 0;
>> +
>> +    /* AS */
>> +    if (((rw == 2) && msr_ir) || ((rw != 2) && msr_dr)) {
>> +        env->spr[SPR_BOOKE_MAS1] |= MAS1_TS;
>> +        env->spr[SPR_BOOKE_MAS6] |= MAS6_SAS;
>> +    }
>> +
>> +    env->spr[SPR_BOOKE_MAS1] |= MAS1_VALID;
>> +    env->spr[SPR_BOOKE_MAS2] |= address & MAS2_EPN_MASK;
>> +
>> +    switch (env->spr[SPR_BOOKE_MAS4] & MAS4_TIDSELD_PIDZ) {
>> +    case MAS4_TIDSELD_PID0:
>> +        env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID] << MAS1_TID_SHIFT;
>> +        break;
>> +    case MAS4_TIDSELD_PID1:
>> +        env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID1] << MAS1_TID_SHIFT;
>> +        break;
>> +    case MAS4_TIDSELD_PID2:
>> +        env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID2] << MAS1_TID_SHIFT;
>> +        break;
>> +    }
>> +
>> +    env->spr[SPR_BOOKE_MAS6] |= env->spr[SPR_BOOKE_PID] << 16;
>> +
>> +    /* next victim logic */
>> +    env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT;
>> +    env->last_way++;
>> +    env->last_way &= 3;
>> +    env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT;
>> +}
> 
> MAS3/7 should be zeroed according to the architecture.

Ah, missed that one :)

> Better victim selection would check for empty ways, and perhaps keep
> last_way on a per-set basis.

Does the hardware do this?

> 
>> @@ -1820,12 +1956,8 @@ void ppc_tlb_invalidate_all (CPUPPCState *env)
>>         cpu_abort(env, "MPC8xx MMU model is not implemented\n");
>>         break;
>>     case POWERPC_MMU_BOOKE:
>> -        tlb_flush(env, 1);
>> -        break;
>>     case POWERPC_MMU_BOOKE_FSL:
>> -        /* XXX: TODO */
>> -        if (!kvm_enabled())
>> -            cpu_abort(env, "BookE MMU model is not implemented\n");
>> +        tlb_flush(env, 1);
>>         break;
>>     case POWERPC_MMU_32B:
>>     case POWERPC_MMU_601:
> 
> This flushes env->tlb_table, but don't we still need to clear out env->tlb?

I guess so, yes. That would break my PID -> tlb_flush patch though. Hrm. This way we don't reset the TLB on reset. I'll fix it up :).

> 
>> diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c
>> index d5db484..3c0b061 100644
>> --- a/target-ppc/op_helper.c
>> +++ b/target-ppc/op_helper.c
>> @@ -4206,4 +4206,422 @@ target_ulong helper_440_tlbsx (target_ulong address)
>>     return ppcemb_tlb_search(env, address, env->spr[SPR_440_MMUCR] & 0xFF);
>> }
>> 
>> +/* PowerPC e500 TLB management */
>> +
>> +static inline int e500_tlb_num(CPUState *env, ppcemb_tlb_t *tlb)
>> +{
>> +    ulong tlbel = (ulong)tlb;
>> +    ulong tlbl = (ulong)env->tlb;
>> +
>> +    return (tlbel - tlbl) / sizeof(env->tlb[0]);
>> +}
>> +
>> +static inline ppcemb_tlb_t *e500_cur_tlb(CPUState *env)
> 
> This is a bit big to be inlined -  let the compiler decide such things.

*shrug* ok :).

> 
>> +{
>> +    uint32_t tlbncfg = 0;
>> +    int esel = (env->spr[SPR_BOOKE_MAS0] & MAS0_ESEL_MASK) >> MAS0_ESEL_SHIFT;
>> +    int ea = (env->spr[SPR_BOOKE_MAS2] & MAS2_EPN_MASK) >> MAS2_EPN_SHIFT;
>> +    int tlb;
>> +    int r;
>> +
>> +    tlb = (env->spr[SPR_BOOKE_MAS0] & MAS0_TLBSEL_MASK) >> MAS0_TLBSEL_SHIFT;
>> +    tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlb];
>> +
>> +    if ((tlbncfg & TLBnCFG_HES) && (env->spr[SPR_BOOKE_MAS0] & MAS0_HES)) {
>> +        cpu_abort(env, "we don't support HES yet\n");
>> +    }
>> +
>> +    if (tlb == 1) {
>> +        r = env->nb_tlbs[0] + (esel % env->nb_tlbs[1]);
> 
> Ouch, division by non-constant.
> 
> There's no need for it anyway; esel >= env_nb_tlbs[1] is undefined
> behavior.  Just crop the value arbitrarily (or error out, if possible) to
> avoid dereferencing a bad array index.

I just rewrote all of this anyways, getting rid of the division along the way.

> 
>> +    } else {
>> +        esel &= env->nb_ways - 1;
>> +        esel <<= 7;
>> +        ea &= 0x7f;
>> +        r = (esel | ea) % env->nb_tlbs[0];
> 
> More unnecessary division -- use "& (env->nb_tlbs[0] - 1)", or variable-ize
> the 7 and 0x7f so that we know that we won't produce an out-of-bounds value.
> 
>> +static const target_phys_addr_t e500_tlb_sizes[] = {
>> +    0,
>> +    4 * 1024,
>> +    16 * 1024,
>> +    64 * 1024,
>> +    256 * 1024,
>> +    1024 * 1024,
>> +    4 * 1024 * 1024,
>> +    16 * 1024 * 1024,
>> +    64 * 1024 * 1024,
>> +    256 * 1024 * 1024,
>> +    1024 * 1024 * 1024,
>> +    (target_phys_addr_t)(4ULL * 1024ULL * 1024ULL * 1024ULL),
>> +};
> 
> FWIW, power-of-2 page sizes are architected, and may appear in future
> e500-family chips.  The TSIZE field is extended by one bit on the right
> (previously reserved and should-be-zero).

Yeah, I've seen that mentioned as MAV 2.0 or so change. This is the exact list of supported page sizes for an e500v2 though. Should we just support all of the possible one and ignore the chip's deficiencies?

> 
>> +static inline target_phys_addr_t e500_tlb_to_page_size(int size)
> 
> unsigned int

Why?

> 
>> +{
>> +    target_phys_addr_t r;
>> +
>> +    if (size > 11) {
> 
> if (size >= ARRAY_SIZE(e500_tlb_sizes)) {
> 
>> +        /* should not happen */
>> +        r = 1024 * 1024 * 1024;
> 
> Error message?
> 
> +    switch (env->spr[SPR_BOOKE_MAS0] & MAS0_WQ_MASK) {
> +    case MAS0_WQ_ALWAYS:
> +        /* good to go, write that entry */
> +        break;
> +    case MAS0_WQ_COND:
> +        /* XXX check if reserved */
> +        if (0) {
> +            return;
> +        }
> +        break;
> +    case MAS0_WQ_CLR_RSRV:
> +        /* XXX clear entry */
> +        return;
> +    default:
> +        /* no idea what to do */
> +        return;
> +    }
> 
> If the plan is to support such things, might want a more generic name than
> "e500" -- we're really talking about isa 2.06 book3e.  The first chip that
> implements this stuff will probably not be from Freescale, much less
> called "e500". :-)

Good point :). I'll rename it booke206.

> 
>> +    if (msr_gs) {
>> +        cpu_abort(env, "missing HV implementation\n");
> 
> What's the policy on aborting versus error-message versus no-op if the
> guest invokes undefined/unimplemented behavior?

The less likely a failure is, the more I find aborting/error-message useful. For KVM, we want to keep the abort cases very very very low. For TCG, I prefer to see when something goes wrong.

So general rule of thumb:

  Guest user space should never be able to kill the VM
  Guest kernel can kill the VM anyways, so it can also do so by issuing unimplemented things
  If the VM would work just fine without the unimplemented thing, don't abort

> 
>> +    } else {
>> +        rpn = ((uint64_t)env->spr[SPR_BOOKE_MAS7] << 32) |
>> +              (env->spr[SPR_BOOKE_MAS3] & 0xfffff000);
>> +    }
> 
> Not sure why this is in an else-branch versus msr_gs.

Because that's what the spec says:

if category E.HV.LRAT supported & (MSRGS=1) & (MAS1V=1) then
  rpn <- translate_logical_to_real(MAS7RPNU || MAS3RPNL, MAS8TLPID)
else if MAS7 implemented then
  rpn <- MAS7RPNU || MAS3RPNL
else rpn <- 320 || MAS3RPNL

> 
>> +    tlb->RPN = rpn;
>> +
>> +    tlb->PID = (env->spr[SPR_BOOKE_MAS1] & MAS1_TID_MASK) >> MAS1_TID_SHIFT;
>> +    tlb->size = e500_tlb_to_page_size((env->spr[SPR_BOOKE_MAS1]
>> +                      & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT);
> 
> e500 manuals document that tsize is ignored in tlb0.

Yup, thanks :).

> 
>> +    tlb->EPN = (uint32_t)(env->spr[SPR_BOOKE_MAS2] & MAS2_EPN_MASK);
>> +    tlb->attr = env->spr[SPR_BOOKE_MAS2] & (MAS2_ACM | MAS2_VLE | MAS2_W |
>> +                                            MAS2_I | MAS2_M | MAS2_G | MAS2_E)
>> +                << 1;
>> +    tlb->attr |= env->spr[SPR_BOOKE_MAS1] & MAS1_IPROT;
>> +    tlb->attr |= (env->spr[SPR_BOOKE_MAS3] &
>> +                  ((MAS3_U0 | MAS3_U1 | MAS3_U2 | MAS3_U3)) << 8);
> 
> Might be nice to #define the encoding of these bits into attr/etc, versus
> magic shifts (though it's moot if we're going to switch to a MAS-based
> representation).

Yeah, this will be moot once we switch. No need to worry about it now.

> 
>> +    if (tlb->size == TARGET_PAGE_SIZE) {
>> +        tlb_flush_page(env, tlb->EPN);
>> +    } else {
>> +        tlb_flush(env, 1);
>> +    }
> 
> Need to check whether the previous entry was a large page needing a full
> flush.

Right, not the current one.

> 
>> +void helper_e500_tlbre(void)
>> +{
>> +    ppcemb_tlb_t *tlb = NULL;
>> +
>> +    tlb = e500_cur_tlb(env);
>> +    e500_tlb_to_mas(env, tlb, e500_tlb_num(env, tlb));
>> +}
>> +
>> +/* Generic TLB check function for embedded PowerPC implementations */
>> +static inline int ppcemb_tlb_check(CPUState *env, ppcemb_tlb_t *tlb,
>> +                                   target_phys_addr_t *raddrp,
>> +                                   target_ulong address, uint32_t pid, int ext,
>> +                                   int i)
> 
> Why is this both here and in helper.c?

Removed this version and exported the helper.c one.

> 
>> +void helper_e500_tlbsx(target_ulong address_hi, target_ulong address_lo)
>> +{
>> +    ppcemb_tlb_t *tlb = NULL;
>> +    int i;
>> +    target_phys_addr_t raddr;
>> +    uint32_t spid, sas;
>> +    uint32_t ea = (address_lo >> MAS2_EPN_SHIFT) & 0x7f;
>> +
>> +    spid = (env->spr[SPR_BOOKE_MAS6] & MAS6_SPID_MASK) >> MAS6_SPID_SHIFT;
>> +    sas = env->spr[SPR_BOOKE_MAS6] & MAS6_SAS;
>> +
>> +    /* check possible TLB0 entries */
>> +    for (i = 0; i < env->nb_ways; i++) {
>> +        tlb = &env->tlb[ea | (i << 7)].tlbe;
>> +
>> +        if (ppcemb_tlb_check(env, tlb, &raddr, address_lo, spid, 0, i) < 0) {
>> +            continue;
>> +        }
>> +
>> +        if (sas != (tlb->attr & MAS6_SAS)) {
>> +            continue;
>> +        }
>> +
>> +        e500_tlb_to_mas(env, tlb, ea | (i << 7));
>> +        return;
>> +    }
>> +
>> +    /* check TLB1 */
>> +    for (i = env->nb_tlbs[0]; i < (env->nb_tlbs[0] + env->nb_tlbs[1]); i++) {
>> +        tlb = &env->tlb[i].tlbe;
>> +
>> +        if (ppcemb_tlb_check(env, tlb, &raddr, address_lo, spid, 0, i) < 0) {
>> +            continue;
>> +        }
>> +
>> +        if (sas != (tlb->attr & MAS6_SAS)) {
>> +            continue;
>> +        }
>> +
>> +        e500_tlb_to_mas(env, tlb, i);
>> +        return;
>> +    }
> 
> This has a lot of overlap with mmue500_get_physical_address()...

It doesn't look as bad now :).

> 
>> +
>> +    /* no entry found, fill with defaults */
>> +    env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK;
>> +    env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK;
>> +    env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK;
>> +
>> +    if (env->spr[SPR_BOOKE_MAS6] & MAS6_SAS) {
>> +        env->spr[SPR_BOOKE_MAS1] |= MAS1_TS;
>> +    }
>> +
>> +    env->spr[SPR_BOOKE_MAS1] |= (env->spr[SPR_BOOKE_MAS6] >> 16)
>> +                                << MAS1_TID_SHIFT;
>> +
>> +    /* next victim logic */
>> +    env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT;
>> +    env->last_way++;
>> +    env->last_way &= 3;
>> +    env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT;
> 
> ...and this with e500_update_mas_tlb_miss().

Yes, I need to think of a good way to generalize NV still. It isn't defined that there's only a single NV available in the architecture either...

> 
>> @@ -8433,7 +8505,7 @@ GEN_HANDLER2(icbt_40x, "icbt", 0x1F, 0x06, 0x08, 0x03E00001, PPC_40x_ICBT),
>> GEN_HANDLER(iccci, 0x1F, 0x06, 0x1E, 0x00000001, PPC_4xx_COMMON),
>> GEN_HANDLER(icread, 0x1F, 0x06, 0x1F, 0x03E00001, PPC_4xx_COMMON),
>> GEN_HANDLER2(rfci_40x, "rfci", 0x13, 0x13, 0x01, 0x03FF8001, PPC_40x_EXCP),
>> -GEN_HANDLER(rfci, 0x13, 0x13, 0x01, 0x03FF8001, PPC_BOOKE),
>> +GEN_HANDLER_E(rfci, 0x13, 0x13, 0x01, 0x03FF8001, PPC_BOOKE, PPC2_BOOKE_FSL),
> 
> What is PPC2_BOOKE_FSL supposed to indicate?
> 
> rfci is basic booke.  It's in the 440.

It's also in e500, right? The flags there are a mask. Basically it means "this opcode is available on PPC_BOOKE and PPC2_BOOKE_FSL capable CPUs".

> 
>> +GEN_HANDLER2_E(icbt_440, "icbt", 0x1F, 0x16, 0x00, 0x03E00001,
>> +               PPC_BOOKE, PPC2_BOOKE_FSL),
> 
> "icbt_440" is FSL-specific? :-)

No, it's available on both :).

> 
>> +static void spr_write_booke_pid (void *opaque, int sprn, int gprn)
>> +{
>> +    gen_store_spr(sprn, cpu_gpr[gprn]);
>> +    /* switching context, so need to flush tlb */
>> +    gen_helper_tlbia();
>> +}
> 
> Well, we want to flush the non-guest-visible TLB that doesn't understand
> PIDs -- but I'd expect "tlbia" to flush the architected TLB.  8xx, at
> least, has both tlbia and an architected TLB.
> 
> Plus, we need ppc_tlb_invalidate_all() to clear the architected TLB, unless
> we call something different on reset.

Yep, changed all that into its own op_helper call that simply does tlb_flush(). If this should ever be more than a toy we should probably investigate on how to add tags to qemu's tlb, so we don't constantly flush it.

> 
>> @@ -1578,45 +1614,38 @@ static void gen_spr_BookE_FSL (CPUPPCState *env, uint32_t mas_mask)
>>                  SPR_NOACCESS, SPR_NOACCESS,
>>                  &spr_read_generic, SPR_NOACCESS,
>>                  0x00000000); /* TOFIX */
>> -    /* XXX : not implemented */
>> -    spr_register(env, SPR_MMUCSR0, "MMUCSR0",
>> -                 SPR_NOACCESS, SPR_NOACCESS,
>> -                 &spr_read_generic, &spr_write_generic,
>> -                 0x00000000); /* TOFIX */
>>     switch (env->nb_ways) {
>>     case 4:
>> -        /* XXX : not implemented */
>>         spr_register(env, SPR_BOOKE_TLB3CFG, "TLB3CFG",
>>                      SPR_NOACCESS, SPR_NOACCESS,
>>                      &spr_read_generic, SPR_NOACCESS,
>> -                     0x00000000); /* TOFIX */
>> +                     tlbncfg[3]);
>>         /* Fallthru */
>>     case 3:
>> -        /* XXX : not implemented */
>>         spr_register(env, SPR_BOOKE_TLB2CFG, "TLB2CFG",
>>                      SPR_NOACCESS, SPR_NOACCESS,
>>                      &spr_read_generic, SPR_NOACCESS,
>> -                     0x00000000); /* TOFIX */
>> +                     tlbncfg[2]);
>>         /* Fallthru */
> 
> In some places you use nb_ways as the associativity of TLB0.  But here it
> seems to be the number of TLB arrays?

It's always the number of TLB arrays. It's gone in v4 though :).

> 
>> @@ -4334,8 +4373,23 @@ static void init_proc_e500 (CPUPPCState *env)
>>     /* Memory management */
>> #if !defined(CONFIG_USER_ONLY)
>>     env->nb_pids = 3;
>> +    env->nb_ways = 2;
>> +    env->id_tlbs = 0;
>> +    if ((env->spr[SPR_PVR] & 0x00010000)) {
>> +        /* e500v2 */
>> +        env->nb_tlbs[0] = 512;
>> +        tlbncfg[0] = gen_tlbncfg(4, 1, 1, 0, env->nb_tlbs[0]);
>> +        tlbncfg[1] = gen_tlbncfg(16, 1, 12, TLBnCFG_AVAIL | TLBnCFG_IPROT, 16);
>> +    } else {
>> +        /* e500v1 */
>> +        env->nb_tlbs[0] = 256;
>> +        tlbncfg[0] = gen_tlbncfg(2, 1, 1, 0, env->nb_tlbs[0]);
>> +        tlbncfg[1] = gen_tlbncfg(16, 1, 9, TLBnCFG_AVAIL | TLBnCFG_IPROT, 16);
>> +    }
> 
> It would be nice to be more precise with PVR testing, and complain if an
> unrecognized PVR is used (e.g. e500mc).

Alright, done.


Alex
Scott Wood May 6, 2011, 5:40 p.m. UTC | #3
On Fri, 6 May 2011 12:01:11 +0200
Alexander Graf <agraf@suse.de> wrote:

> >> +    for (i = env->nb_tlbs[0]; i < env->nb_tlb; i++) {
> >> +        tlb = &env->tlb[i].tlbe;
> >> +        ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, rw,
> >> +                                 access_type, i);
> >> +        if (!ret) {
> >> +            goto found_tlb;
> >>         }
> >>     }
> >> -    if (ret >= 0)
> >> +
> >> +    /* check possible TLB0 entries */
> >> +    for (i = 0; i < env->nb_ways; i++) {
> >> +        tlb = &env->tlb[ea | (i << 7)].tlbe;
> > 
> > Do we have to hardcode 7 (and 0x7f) here?
> 
> I'm not sure - the spec isn't exactly obvious on this part. How do I find out the mask from TLBnCFG?

I don't think the ISA specifies the hash at all.  The implementation manual
should specify it.  I think 7 bits is correct on all e500-family chips so
far, but it's really a function of the TLB geometry.  It may be desireable
in some situations for qemu to create a larger TLB than hardware actually
has, to reduce the TLB misses that the guest has to handle.

> > Better victim selection would check for empty ways, and perhaps keep
> > last_way on a per-set basis.
> 
> Does the hardware do this?

Hmm, I thought it did, but checking the docs it seems to be similar to
what you implemented.  I guess I was thinking of the I/D-cache victim
selection.

> >> +static const target_phys_addr_t e500_tlb_sizes[] = {
> >> +    0,
> >> +    4 * 1024,
> >> +    16 * 1024,
> >> +    64 * 1024,
> >> +    256 * 1024,
> >> +    1024 * 1024,
> >> +    4 * 1024 * 1024,
> >> +    16 * 1024 * 1024,
> >> +    64 * 1024 * 1024,
> >> +    256 * 1024 * 1024,
> >> +    1024 * 1024 * 1024,
> >> +    (target_phys_addr_t)(4ULL * 1024ULL * 1024ULL * 1024ULL),
> >> +};
> > 
> > FWIW, power-of-2 page sizes are architected, and may appear in future
> > e500-family chips.  The TSIZE field is extended by one bit on the right
> > (previously reserved and should-be-zero).
> 
> Yeah, I've seen that mentioned as MAV 2.0 or so change. This is the exact list of supported page sizes for an e500v2 though. Should we just support all of the possible one and ignore the chip's deficiencies?

I'd say just support all of them -- it's undefined behavior if a MAV 1.0
guest sets that low bit, so this is as valid an implementation as any. :-)

> >> +static inline target_phys_addr_t e500_tlb_to_page_size(int size)
> > 
> > unsigned int
> 
> Why?

So you don't have to check size < 0.

> >> +    } else {
> >> +        rpn = ((uint64_t)env->spr[SPR_BOOKE_MAS7] << 32) |
> >> +              (env->spr[SPR_BOOKE_MAS3] & 0xfffff000);
> >> +    }
> > 
> > Not sure why this is in an else-branch versus msr_gs.
> 
> Because that's what the spec says:
> 
> if category E.HV.LRAT supported & (MSRGS=1) & (MAS1V=1) then
>   rpn <- translate_logical_to_real(MAS7RPNU || MAS3RPNL, MAS8TLPID)
> else if MAS7 implemented then
>   rpn <- MAS7RPNU || MAS3RPNL
> else rpn <- 320 || MAS3RPNL

Sorry, I was thinking of MAS8[TGS], not MSR[GS].

> Yes, I need to think of a good way to generalize NV still. It isn't defined that there's only a single NV available in the architecture either...

Architecturally, the NV algorithm is implementation-defined.

> >> @@ -8433,7 +8505,7 @@ GEN_HANDLER2(icbt_40x, "icbt", 0x1F, 0x06, 0x08, 0x03E00001, PPC_40x_ICBT),
> >> GEN_HANDLER(iccci, 0x1F, 0x06, 0x1E, 0x00000001, PPC_4xx_COMMON),
> >> GEN_HANDLER(icread, 0x1F, 0x06, 0x1F, 0x03E00001, PPC_4xx_COMMON),
> >> GEN_HANDLER2(rfci_40x, "rfci", 0x13, 0x13, 0x01, 0x03FF8001, PPC_40x_EXCP),
> >> -GEN_HANDLER(rfci, 0x13, 0x13, 0x01, 0x03FF8001, PPC_BOOKE),
> >> +GEN_HANDLER_E(rfci, 0x13, 0x13, 0x01, 0x03FF8001, PPC_BOOKE, PPC2_BOOKE_FSL),
> > 
> > What is PPC2_BOOKE_FSL supposed to indicate?
> > 
> > rfci is basic booke.  It's in the 440.
> 
> It's also in e500, right?

Yes.

> The flags there are a mask. Basically it means "this opcode is available
> on PPC_BOOKE and PPC2_BOOKE_FSL capable CPUs".

OK, it looked like it was being limited to only FSL.  I missed that
PPC_BOOKE and PPC2_BOOKE_FSL are the same "kind" of flags despite being in
different words.  Does PPC_BOOKE not refer to things that are common to all
booke?  Why do the individual implementations of booke need to be listed?

-Scott
Alexander Graf May 6, 2011, 6:33 p.m. UTC | #4
On 06.05.2011, at 19:40, Scott Wood wrote:

> On Fri, 6 May 2011 12:01:11 +0200
> Alexander Graf <agraf@suse.de> wrote:
> 
>>>> +    for (i = env->nb_tlbs[0]; i < env->nb_tlb; i++) {
>>>> +        tlb = &env->tlb[i].tlbe;
>>>> +        ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, rw,
>>>> +                                 access_type, i);
>>>> +        if (!ret) {
>>>> +            goto found_tlb;
>>>>        }
>>>>    }
>>>> -    if (ret >= 0)
>>>> +
>>>> +    /* check possible TLB0 entries */
>>>> +    for (i = 0; i < env->nb_ways; i++) {
>>>> +        tlb = &env->tlb[ea | (i << 7)].tlbe;
>>> 
>>> Do we have to hardcode 7 (and 0x7f) here?
>> 
>> I'm not sure - the spec isn't exactly obvious on this part. How do I find out the mask from TLBnCFG?
> 
> I don't think the ISA specifies the hash at all.  The implementation manual
> should specify it.  I think 7 bits is correct on all e500-family chips so
> far, but it's really a function of the TLB geometry.  It may be desireable
> in some situations for qemu to create a larger TLB than hardware actually
> has, to reduce the TLB misses that the guest has to handle.

Yup, changed that one :).

> 
>>> Better victim selection would check for empty ways, and perhaps keep
>>> last_way on a per-set basis.
>> 
>> Does the hardware do this?
> 
> Hmm, I thought it did, but checking the docs it seems to be similar to
> what you implemented.  I guess I was thinking of the I/D-cache victim
> selection.
> 
>>>> +static const target_phys_addr_t e500_tlb_sizes[] = {
>>>> +    0,
>>>> +    4 * 1024,
>>>> +    16 * 1024,
>>>> +    64 * 1024,
>>>> +    256 * 1024,
>>>> +    1024 * 1024,
>>>> +    4 * 1024 * 1024,
>>>> +    16 * 1024 * 1024,
>>>> +    64 * 1024 * 1024,
>>>> +    256 * 1024 * 1024,
>>>> +    1024 * 1024 * 1024,
>>>> +    (target_phys_addr_t)(4ULL * 1024ULL * 1024ULL * 1024ULL),
>>>> +};
>>> 
>>> FWIW, power-of-2 page sizes are architected, and may appear in future
>>> e500-family chips.  The TSIZE field is extended by one bit on the right
>>> (previously reserved and should-be-zero).
>> 
>> Yeah, I've seen that mentioned as MAV 2.0 or so change. This is the exact list of supported page sizes for an e500v2 though. Should we just support all of the possible one and ignore the chip's deficiencies?
> 
> I'd say just support all of them -- it's undefined behavior if a MAV 1.0
> guest sets that low bit, so this is as valid an implementation as any. :-)

I have a nice algorithm for MAV 1.0 now - when 2.0 comes along we can check again and see what fits.

> 
>>>> +static inline target_phys_addr_t e500_tlb_to_page_size(int size)
>>> 
>>> unsigned int
>> 
>> Why?
> 
> So you don't have to check size < 0.

Ah, I see. That one's moot by now since we don't look up things in an array anymore.

> 
>>>> +    } else {
>>>> +        rpn = ((uint64_t)env->spr[SPR_BOOKE_MAS7] << 32) |
>>>> +              (env->spr[SPR_BOOKE_MAS3] & 0xfffff000);
>>>> +    }
>>> 
>>> Not sure why this is in an else-branch versus msr_gs.
>> 
>> Because that's what the spec says:
>> 
>> if category E.HV.LRAT supported & (MSRGS=1) & (MAS1V=1) then
>>  rpn <- translate_logical_to_real(MAS7RPNU || MAS3RPNL, MAS8TLPID)
>> else if MAS7 implemented then
>>  rpn <- MAS7RPNU || MAS3RPNL
>> else rpn <- 320 || MAS3RPNL
> 
> Sorry, I was thinking of MAS8[TGS], not MSR[GS].
> 
>> Yes, I need to think of a good way to generalize NV still. It isn't defined that there's only a single NV available in the architecture either...
> 
> Architecturally, the NV algorithm is implementation-defined.

Meh :).

> 
>>>> @@ -8433,7 +8505,7 @@ GEN_HANDLER2(icbt_40x, "icbt", 0x1F, 0x06, 0x08, 0x03E00001, PPC_40x_ICBT),
>>>> GEN_HANDLER(iccci, 0x1F, 0x06, 0x1E, 0x00000001, PPC_4xx_COMMON),
>>>> GEN_HANDLER(icread, 0x1F, 0x06, 0x1F, 0x03E00001, PPC_4xx_COMMON),
>>>> GEN_HANDLER2(rfci_40x, "rfci", 0x13, 0x13, 0x01, 0x03FF8001, PPC_40x_EXCP),
>>>> -GEN_HANDLER(rfci, 0x13, 0x13, 0x01, 0x03FF8001, PPC_BOOKE),
>>>> +GEN_HANDLER_E(rfci, 0x13, 0x13, 0x01, 0x03FF8001, PPC_BOOKE, PPC2_BOOKE_FSL),
>>> 
>>> What is PPC2_BOOKE_FSL supposed to indicate?
>>> 
>>> rfci is basic booke.  It's in the 440.
>> 
>> It's also in e500, right?
> 
> Yes.
> 
>> The flags there are a mask. Basically it means "this opcode is available
>> on PPC_BOOKE and PPC2_BOOKE_FSL capable CPUs".
> 
> OK, it looked like it was being limited to only FSL.  I missed that
> PPC_BOOKE and PPC2_BOOKE_FSL are the same "kind" of flags despite being in
> different words.  Does PPC_BOOKE not refer to things that are common to all
> booke?  Why do the individual implementations of booke need to be listed?

Well, the PPC_BOOKE flag was also used for the 440 tlb modification opcodes, which are different for 2.06. So I just introduced a new one :). We could of course also introduce yet another flag for 440s and define specific instructions individually, but I don't really see it buying us too much for now.


Alex
Scott Wood May 6, 2011, 6:40 p.m. UTC | #5
On Fri, 6 May 2011 20:33:24 +0200
Alexander Graf <agraf@suse.de> wrote:

> On 06.05.2011, at 19:40, Scott Wood wrote:
> 
> > OK, it looked like it was being limited to only FSL.  I missed that
> > PPC_BOOKE and PPC2_BOOKE_FSL are the same "kind" of flags despite being in
> > different words.  Does PPC_BOOKE not refer to things that are common to all
> > booke?  Why do the individual implementations of booke need to be listed?
> 
> Well, the PPC_BOOKE flag was also used for the 440 tlb modification opcodes, which are different for 2.06. So I just introduced a new one :). We could of course also introduce yet another flag for 440s and define specific instructions individually, but I don't really see it buying us too much for now.

It would help reduce confusion if PPC_BOOKE really meant booke, and not
440. :-)

-Scott
diff mbox

Patch

different TLB modification.

This patch adds support for the FSL style MMU.

Signed-off-by: Alexander Graf <agraf@suse.de>

---

v1 -> v2:

  - fix linux-user build
  - optimize tlb invalidate & search
---
 target-ppc/cpu.h            |  217 ++++++++++++++++++++++-
 target-ppc/helper.c         |  233 +++++++++++++++++++-----
 target-ppc/helper.h         |    5 +
 target-ppc/op_helper.c      |  418 +++++++++++++++++++++++++++++++++++++++++++
 target-ppc/translate.c      |   91 +++++++++-
 target-ppc/translate_init.c |  127 +++++++++-----
 6 files changed, 994 insertions(+), 97 deletions(-)

diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index b8d9049..785ae53 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -608,6 +608,215 @@  enum {
 #define vscr_sat	(((env->vscr) >> VSCR_SAT)	& 0x1)
 
 /*****************************************************************************/
+/* BookE e500 MMU registers */
+
+#define MAS0_NV_SHIFT      0
+#define MAS0_NV_MASK       (0xfff << MAS0_NV_SHIFT)
+
+#define MAS0_WQ_SHIFT      12
+#define MAS0_WQ_MASK       (3 << MAS0_WQ_SHIFT)
+/* Write TLB entry regardless of reservation */
+#define MAS0_WQ_ALWAYS     (0 << MAS0_WQ_SHIFT)
+/* Write TLB entry only already in use */
+#define MAS0_WQ_COND       (1 << MAS0_WQ_SHIFT)
+/* Clear TLB entry */
+#define MAS0_WQ_CLR_RSRV   (2 << MAS0_WQ_SHIFT)
+
+#define MAS0_HES_SHIFT     14
+#define MAS0_HES           (1 << MAS0_HES_SHIFT)
+
+#define MAS0_ESEL_SHIFT    16
+#define MAS0_ESEL_MASK     (0xfff << MAS0_ESEL_SHIFT)
+
+#define MAS0_TLBSEL_SHIFT  28
+#define MAS0_TLBSEL_MASK   (3 << MAS0_TLBSEL_SHIFT)
+#define MAS0_TLBSEL_TLB0   (0 << MAS0_TLBSEL_SHIFT)
+#define MAS0_TLBSEL_TLB1   (1 << MAS0_TLBSEL_SHIFT)
+#define MAS0_TLBSEL_TLB2   (2 << MAS0_TLBSEL_SHIFT)
+#define MAS0_TLBSEL_TLB3   (3 << MAS0_TLBSEL_SHIFT)
+
+#define MAS0_ATSEL_SHIFT   31
+#define MAS0_ATSEL         (1 << MAS0_ATSEL_SHIFT)
+#define MAS0_ATSEL_TLB     0
+#define MAS0_ATSEL_LRAT    MAS0_ATSEL
+
+#define MAS1_TSIZE_SHIFT   8
+#define MAS1_TSIZE_MASK    (0xf << MAS1_TSIZE_SHIFT)
+
+#define MAS1_TS_SHIFT      12
+#define MAS1_TS            (1 << MAS1_TS_SHIFT)
+
+#define MAS1_IND_SHIFT     13
+#define MAS1_IND           (1 << MAS1_IND_SHIFT)
+
+#define MAS1_TID_SHIFT     16
+#define MAS1_TID_MASK      (0x3fff << MAS1_TID_SHIFT)
+
+#define MAS1_IPROT_SHIFT   30
+#define MAS1_IPROT         (1 << MAS1_IPROT_SHIFT)
+
+#define MAS1_VALID_SHIFT   31
+#define MAS1_VALID         0x80000000
+
+#define MAS2_EPN_SHIFT     12
+#define MAS2_EPN_MASK      (0xfffff << MAS2_EPN_SHIFT)
+
+#define MAS2_ACM_SHIFT     6
+#define MAS2_ACM           (1 << MAS2_ACM_SHIFT)
+
+#define MAS2_VLE_SHIFT     5
+#define MAS2_VLE           (1 << MAS2_VLE_SHIFT)
+
+#define MAS2_W_SHIFT       4
+#define MAS2_W             (1 << MAS2_W_SHIFT)
+
+#define MAS2_I_SHIFT       3
+#define MAS2_I             (1 << MAS2_I_SHIFT)
+
+#define MAS2_M_SHIFT       2
+#define MAS2_M             (1 << MAS2_M_SHIFT)
+
+#define MAS2_G_SHIFT       1
+#define MAS2_G             (1 << MAS2_G_SHIFT)
+
+#define MAS2_E_SHIFT       0
+#define MAS2_E             (1 << MAS2_E_SHIFT)
+
+#define MAS3_RPN_SHIFT     12
+#define MAS3_RPN_MASK      (0xfffff << MAS3_RPN_SHIFT)
+
+#define MAS3_U0                 0x00000200
+#define MAS3_U1                 0x00000100
+#define MAS3_U2                 0x00000080
+#define MAS3_U3                 0x00000040
+#define MAS3_UX                 0x00000020
+#define MAS3_SX                 0x00000010
+#define MAS3_UW                 0x00000008
+#define MAS3_SW                 0x00000004
+#define MAS3_UR                 0x00000002
+#define MAS3_SR                 0x00000001
+#define MAS3_SPSIZE_SHIFT       1
+#define MAS3_SPSIZE_MASK        (0x3e << MAS3_SPSIZE_SHIFT)
+
+#define MAS4_TLBSELD_SHIFT      MAS0_TLBSEL_SHIFT
+#define MAS4_TLBSELD_MASK       MAS0_TLBSEL_MASK
+#define MAS4_TIDSELD_MASK       0x00030000
+#define MAS4_TIDSELD_PID0       0x00000000
+#define MAS4_TIDSELD_PID1       0x00010000
+#define MAS4_TIDSELD_PID2       0x00020000
+#define MAS4_TIDSELD_PIDZ       0x00030000
+#define MAS4_INDD               0x00008000      /* Default IND */
+#define MAS4_TSIZED_SHIFT       MAS1_TSIZE_SHIFT
+#define MAS4_TSIZED_MASK        MAS1_TSIZE_MASK
+#define MAS4_ACMD               0x00000040
+#define MAS4_VLED               0x00000020
+#define MAS4_WD                 0x00000010
+#define MAS4_ID                 0x00000008
+#define MAS4_MD                 0x00000004
+#define MAS4_GD                 0x00000002
+#define MAS4_ED                 0x00000001
+#define MAS4_WIMGED_MASK        0x0000001f      /* Default WIMGE */
+#define MAS4_WIMGED_SHIFT       0
+
+#define MAS5_SGS                0x80000000
+#define MAS5_SLPID_MASK         0x00000fff
+
+#define MAS6_SPID0              0x3fff0000
+#define MAS6_SPID1              0x00007ffe
+#define MAS6_ISIZE(x)           MAS1_TSIZE(x)
+#define MAS6_SAS                0x00000001
+#define MAS6_SPID               MAS6_SPID0
+#define MAS6_SIND               0x00000002      /* Indirect page */
+#define MAS6_SIND_SHIFT         1
+#define MAS6_SPID_MASK          0x3fff0000
+#define MAS6_SPID_SHIFT         16
+#define MAS6_ISIZE_MASK         0x00000f80
+#define MAS6_ISIZE_SHIFT        7
+
+#define MAS7_RPN                0xffffffff
+
+#define MAS8_TGS                0x80000000
+#define MAS8_VF                 0x40000000
+#define MAS8_TLBPID             0x00000fff
+
+/* Bit definitions for MMUCFG */
+#define MMUCFG_MAVN     0x00000003      /* MMU Architecture Version Number */
+#define MMUCFG_MAVN_V1  0x00000000      /* v1.0 */
+#define MMUCFG_MAVN_V2  0x00000001      /* v2.0 */
+#define MMUCFG_NTLBS    0x0000000c      /* Number of TLBs */
+#define MMUCFG_PIDSIZE  0x000007c0      /* PID Reg Size */
+#define MMUCFG_TWC      0x00008000      /* TLB Write Conditional (v2.0) */
+#define MMUCFG_LRAT     0x00010000      /* LRAT Supported (v2.0) */
+#define MMUCFG_RASIZE   0x00fe0000      /* Real Addr Size */
+#define MMUCFG_LPIDSIZE 0x0f000000      /* LPID Reg Size */
+
+/* Bit definitions for MMUCSR0 */
+#define MMUCSR0_TLB1FI  0x00000002      /* TLB1 Flash invalidate */
+#define MMUCSR0_TLB0FI  0x00000004      /* TLB0 Flash invalidate */
+#define MMUCSR0_TLB2FI  0x00000040      /* TLB2 Flash invalidate */
+#define MMUCSR0_TLB3FI  0x00000020      /* TLB3 Flash invalidate */
+#define MMUCSR0_TLBFI   (MMUCSR0_TLB0FI | MMUCSR0_TLB1FI | \
+                         MMUCSR0_TLB2FI | MMUCSR0_TLB3FI)
+#define MMUCSR0_TLB0PS  0x00000780      /* TLB0 Page Size */
+#define MMUCSR0_TLB1PS  0x00007800      /* TLB1 Page Size */
+#define MMUCSR0_TLB2PS  0x00078000      /* TLB2 Page Size */
+#define MMUCSR0_TLB3PS  0x00780000      /* TLB3 Page Size */
+
+/* TLBnCFG encoding */
+#define TLBnCFG_N_ENTRY         0x00000fff      /* number of entries */
+#define TLBnCFG_HES             0x00002000      /* HW select supported */
+#define TLBnCFG_AVAIL           0x00004000      /* variable page size */
+#define TLBnCFG_IPROT           0x00008000      /* IPROT supported */
+#define TLBnCFG_GTWE            0x00010000      /* Guest can write */
+#define TLBnCFG_IND             0x00020000      /* IND entries supported */
+#define TLBnCFG_PT              0x00040000      /* Can load from page table */
+#define TLBnCFG_MINSIZE         0x00f00000      /* Minimum Page Size (v1.0) */
+#define TLBnCFG_MINSIZE_SHIFT   20
+#define TLBnCFG_MAXSIZE         0x000f0000      /* Maximum Page Size (v1.0) */
+#define TLBnCFG_MAXSIZE_SHIFT   16
+#define TLBnCFG_ASSOC           0xff000000      /* Associativity */
+#define TLBnCFG_ASSOC_SHIFT     24
+
+/* TLBnPS encoding */
+#define TLBnPS_4K               0x00000004
+#define TLBnPS_8K               0x00000008
+#define TLBnPS_16K              0x00000010
+#define TLBnPS_32K              0x00000020
+#define TLBnPS_64K              0x00000040
+#define TLBnPS_128K             0x00000080
+#define TLBnPS_256K             0x00000100
+#define TLBnPS_512K             0x00000200
+#define TLBnPS_1M               0x00000400
+#define TLBnPS_2M               0x00000800
+#define TLBnPS_4M               0x00001000
+#define TLBnPS_8M               0x00002000
+#define TLBnPS_16M              0x00004000
+#define TLBnPS_32M              0x00008000
+#define TLBnPS_64M              0x00010000
+#define TLBnPS_128M             0x00020000
+#define TLBnPS_256M             0x00040000
+#define TLBnPS_512M             0x00080000
+#define TLBnPS_1G               0x00100000
+#define TLBnPS_2G               0x00200000
+#define TLBnPS_4G               0x00400000
+#define TLBnPS_8G               0x00800000
+#define TLBnPS_16G              0x01000000
+#define TLBnPS_32G              0x02000000
+#define TLBnPS_64G              0x04000000
+#define TLBnPS_128G             0x08000000
+#define TLBnPS_256G             0x10000000
+
+/* tlbilx action encoding */
+#define TLBILX_T_ALL                    0
+#define TLBILX_T_TID                    1
+#define TLBILX_T_FULLMATCH              3
+#define TLBILX_T_CLASS0                 4
+#define TLBILX_T_CLASS1                 5
+#define TLBILX_T_CLASS2                 6
+#define TLBILX_T_CLASS3                 7
+
+
+/*****************************************************************************/
 /* The whole PowerPC CPU context */
 #define NB_MMU_MODES 3
 
@@ -678,8 +887,9 @@  struct CPUPPCState {
     int nb_BATs;
     target_ulong DBAT[2][8];
     target_ulong IBAT[2][8];
-    /* PowerPC TLB registers (for 4xx and 60x software driven TLBs) */
+    /* PowerPC TLB registers (for 4xx, e500 and 60x software driven TLBs) */
     int nb_tlb;      /* Total number of TLB                                  */
+    int nb_tlbs[4];  /* Number of respective TLB entries (e500)              */
     int tlb_per_way; /* Speed-up helper: used to avoid divisions at run time */
     int nb_ways;     /* Number of ways in the TLB set                        */
     int last_way;    /* Last used way used to allocate TLB in a LRU way      */
@@ -1546,6 +1756,11 @@  enum {
     PPC_DCRUX          = 0x4000000000000000ULL,
     /* popcntw and popcntd instructions                                      */
     PPC_POPCNTWD       = 0x8000000000000000ULL,
+
+    /* extended type values */
+
+    /* BookE FSL (e500) PowerPC specification                                */
+    PPC2_BOOKE_FSL     = 0x0000000000000001ULL,
 };
 
 /*****************************************************************************/
diff --git a/target-ppc/helper.c b/target-ppc/helper.c
index 5e4030b..261c1a9 100644
--- a/target-ppc/helper.c
+++ b/target-ppc/helper.c
@@ -1153,48 +1153,144 @@  void store_40x_sler (CPUPPCState *env, uint32_t val)
     env->spr[SPR_405_SLER] = val;
 }
 
+static inline int mmubooke_check_tlb (CPUState *env, ppcemb_tlb_t *tlb,
+                                      target_phys_addr_t *raddr, int *prot,
+                                      target_ulong address, int rw,
+                                      int access_type, int i)
+{
+    int ret, _prot;
+
+    if (ppcemb_tlb_check(env, tlb, raddr, address,
+                         env->spr[SPR_BOOKE_PID],
+                         !env->nb_pids, i) >= 0) {
+        goto found_tlb;
+    }
+
+    if ((env->nb_pids > 1) && env->spr[SPR_BOOKE_PID1] &&
+        ppcemb_tlb_check(env, tlb, raddr, address,
+                         env->spr[SPR_BOOKE_PID1], 0, i) >= 0) {
+        goto found_tlb;
+    }
+
+    if ((env->nb_pids > 2) && env->spr[SPR_BOOKE_PID2] &&
+        ppcemb_tlb_check(env, tlb, raddr, address,
+                         env->spr[SPR_BOOKE_PID2], 0, i) >= 0) {
+        goto found_tlb;
+    }
+
+    return -1;
+
+found_tlb:
+
+    if (msr_pr != 0) {
+        _prot = tlb->prot & 0xF;
+    } else {
+        _prot = (tlb->prot >> 4) & 0xF;
+    }
+
+    /* Check the address space */
+    if (access_type == ACCESS_CODE) {
+        if (msr_ir != (tlb->attr & 1)) {
+            return -1;
+        }
+
+        *prot = _prot;
+        if (_prot & PAGE_EXEC) {
+            return 0;
+        }
+
+        ret = -3;
+    } else {
+        if (msr_dr != (tlb->attr & 1)) {
+            return -1;
+        }
+
+        *prot = _prot;
+        if ((!rw && _prot & PAGE_READ) || (rw && (_prot & PAGE_WRITE))) {
+            return 0;
+        }
+
+        ret = -2;
+    }
+
+    return ret;
+}
+
 static int mmubooke_get_physical_address (CPUState *env, mmu_ctx_t *ctx,
                                           target_ulong address, int rw,
                                           int access_type)
 {
     ppcemb_tlb_t *tlb;
     target_phys_addr_t raddr;
-    int i, prot, ret;
+    int i, ret;
 
     ret = -1;
     raddr = (target_phys_addr_t)-1ULL;
     for (i = 0; i < env->nb_tlb; i++) {
         tlb = &env->tlb[i].tlbe;
-        if (ppcemb_tlb_check(env, tlb, &raddr, address,
-                             env->spr[SPR_BOOKE_PID], 1, i) < 0)
-            continue;
-        if (msr_pr != 0)
-            prot = tlb->prot & 0xF;
-        else
-            prot = (tlb->prot >> 4) & 0xF;
-        /* Check the address space */
-        if (access_type == ACCESS_CODE) {
-            if (msr_ir != (tlb->attr & 1))
-                continue;
-            ctx->prot = prot;
-            if (prot & PAGE_EXEC) {
-                ret = 0;
-                break;
-            }
-            ret = -3;
-        } else {
-            if (msr_dr != (tlb->attr & 1))
-                continue;
-            ctx->prot = prot;
-            if ((!rw && prot & PAGE_READ) || (rw && (prot & PAGE_WRITE))) {
-                ret = 0;
-                break;
-            }
-            ret = -2;
+        ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, rw,
+                                 access_type, i);
+        if (!ret) {
+            break;
+        }
+    }
+
+    if (ret >= 0) {
+        ctx->raddr = raddr;
+        LOG_SWTLB("%s: access granted " TARGET_FMT_lx " => " TARGET_FMT_plx
+                  " %d %d\n", __func__, address, ctx->raddr, ctx->prot,
+                  ret);
+    } else {
+        LOG_SWTLB("%s: access refused " TARGET_FMT_lx " => " TARGET_FMT_plx
+                  " %d %d\n", __func__, address, raddr, ctx->prot, ret);
+    }
+
+    return ret;
+}
+
+static int mmue500_get_physical_address (CPUState *env, mmu_ctx_t *ctx,
+                                         target_ulong address, int rw,
+                                         int access_type)
+{
+    ppcemb_tlb_t *tlb;
+    target_phys_addr_t raddr;
+    int i, ret;
+    uint32_t ea = (address >> MAS2_EPN_SHIFT) & 0x7f;
+
+    ret = -1;
+    raddr = (target_phys_addr_t)-1ULL;
+
+    /* check TLB1 - hits often because the kernel is here */
+    for (i = env->nb_tlbs[0]; i < env->nb_tlb; i++) {
+        tlb = &env->tlb[i].tlbe;
+        ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, rw,
+                                 access_type, i);
+        if (!ret) {
+            goto found_tlb;
         }
     }
-    if (ret >= 0)
+
+    /* check possible TLB0 entries */
+    for (i = 0; i < env->nb_ways; i++) {
+        tlb = &env->tlb[ea | (i << 7)].tlbe;
+        ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, rw,
+                                 access_type, i);
+        if (!ret) {
+            goto found_tlb;
+        }
+    }
+
+found_tlb:
+
+    if (ret >= 0) {
         ctx->raddr = raddr;
+        LOG_SWTLB("%s: access granted " TARGET_FMT_lx " => " TARGET_FMT_plx
+                  " %d %d\n", __func__, address, ctx->raddr, ctx->prot,
+                  ret);
+    } else {
+        LOG_SWTLB("%s: access refused " TARGET_FMT_lx " => " TARGET_FMT_plx
+                  " %d %d\n", __func__, address, raddr, ctx->prot, ret);
+    }
 
     return ret;
 }
@@ -1255,8 +1351,7 @@  static inline int check_physical(CPUState *env, mmu_ctx_t *ctx,
         cpu_abort(env, "MPC8xx MMU model is not implemented\n");
         break;
     case POWERPC_MMU_BOOKE_FSL:
-        /* XXX: TODO */
-        cpu_abort(env, "BookE FSL MMU model not implemented\n");
+        cpu_abort(env, "BookE FSL MMU model doesn't have physical real mode\n");
         break;
     default:
         cpu_abort(env, "Unknown or invalid MMU model\n");
@@ -1281,6 +1376,9 @@  int get_physical_address (CPUState *env, mmu_ctx_t *ctx, target_ulong eaddr,
                IS and DS bits only affect the address space.  */
             ret = mmubooke_get_physical_address(env, ctx, eaddr,
                                                 rw, access_type);
+        } else if (env->mmu_model == POWERPC_MMU_BOOKE_FSL) {
+            ret = mmue500_get_physical_address(env, ctx, eaddr, rw,
+                                               access_type);
         } else {
             /* No address translation.  */
             ret = check_physical(env, ctx, eaddr, rw);
@@ -1314,14 +1412,14 @@  int get_physical_address (CPUState *env, mmu_ctx_t *ctx, target_ulong eaddr,
             ret = mmubooke_get_physical_address(env, ctx, eaddr,
                                                 rw, access_type);
             break;
+        case POWERPC_MMU_BOOKE_FSL:
+            ret = mmue500_get_physical_address(env, ctx, eaddr, rw,
+                                               access_type);
+            break;
         case POWERPC_MMU_MPC8xx:
             /* XXX: TODO */
             cpu_abort(env, "MPC8xx MMU model is not implemented\n");
             break;
-        case POWERPC_MMU_BOOKE_FSL:
-            /* XXX: TODO */
-            cpu_abort(env, "BookE FSL MMU model not implemented\n");
-            return -1;
         case POWERPC_MMU_REAL:
             cpu_abort(env, "PowerPC in real mode do not do any translation\n");
             return -1;
@@ -1348,6 +1446,44 @@  target_phys_addr_t cpu_get_phys_page_debug (CPUState *env, target_ulong addr)
     return ctx.raddr & TARGET_PAGE_MASK;
 }
 
+static void e500_update_mas_tlb_miss(CPUState *env, target_ulong address,
+                                     int rw)
+{
+    env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK;
+    env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK;
+    env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK;
+    env->spr[SPR_BOOKE_MAS6] = 0;
+
+    /* AS */
+    if (((rw == 2) && msr_ir) || ((rw != 2) && msr_dr)) {
+        env->spr[SPR_BOOKE_MAS1] |= MAS1_TS;
+        env->spr[SPR_BOOKE_MAS6] |= MAS6_SAS;
+    }
+
+    env->spr[SPR_BOOKE_MAS1] |= MAS1_VALID;
+    env->spr[SPR_BOOKE_MAS2] |= address & MAS2_EPN_MASK;
+
+    switch (env->spr[SPR_BOOKE_MAS4] & MAS4_TIDSELD_PIDZ) {
+    case MAS4_TIDSELD_PID0:
+        env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID] << MAS1_TID_SHIFT;
+        break;
+    case MAS4_TIDSELD_PID1:
+        env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID1] << MAS1_TID_SHIFT;
+        break;
+    case MAS4_TIDSELD_PID2:
+        env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID2] << MAS1_TID_SHIFT;
+        break;
+    }
+
+    env->spr[SPR_BOOKE_MAS6] |= env->spr[SPR_BOOKE_PID] << 16;
+
+    /* next victim logic */
+    env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT;
+    env->last_way++;
+    env->last_way &= 3;
+    env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT;
+}
+
 /* Perform address translation */
 int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
                               int mmu_idx, int is_softmmu)
@@ -1403,15 +1539,14 @@  int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
                     env->exception_index = POWERPC_EXCP_ISI;
                     env->error_code = 0x40000000;
                     break;
+                case POWERPC_MMU_BOOKE_FSL:
+                    e500_update_mas_tlb_miss(env, address, rw);
+                    /* fall through */
                 case POWERPC_MMU_BOOKE:
                     env->exception_index = POWERPC_EXCP_ITLB;
                     env->error_code = 0;
                     env->spr[SPR_BOOKE_DEAR] = address;
                     return -1;
-                case POWERPC_MMU_BOOKE_FSL:
-                    /* XXX: TODO */
-                    cpu_abort(env, "BookE FSL MMU model is not implemented\n");
-                    return -1;
                 case POWERPC_MMU_MPC8xx:
                     /* XXX: TODO */
                     cpu_abort(env, "MPC8xx MMU model is not implemented\n");
@@ -1432,7 +1567,8 @@  int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
                 break;
             case -3:
                 /* No execute protection violation */
-                if (env->mmu_model == POWERPC_MMU_BOOKE) {
+                if ((env->mmu_model == POWERPC_MMU_BOOKE) ||
+                    (env->mmu_model == POWERPC_MMU_BOOKE_FSL)) {
                     env->spr[SPR_BOOKE_ESR] = 0x00000000;
                 }
                 env->exception_index = POWERPC_EXCP_ISI;
@@ -1522,16 +1658,15 @@  int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
                     /* XXX: TODO */
                     cpu_abort(env, "MPC8xx MMU model is not implemented\n");
                     break;
+                case POWERPC_MMU_BOOKE_FSL:
+                    e500_update_mas_tlb_miss(env, address, rw);
+                    /* fall through */
                 case POWERPC_MMU_BOOKE:
                     env->exception_index = POWERPC_EXCP_DTLB;
                     env->error_code = 0;
                     env->spr[SPR_BOOKE_DEAR] = address;
                     env->spr[SPR_BOOKE_ESR] = rw ? 1 << ESR_ST : 0;
                     return -1;
-                case POWERPC_MMU_BOOKE_FSL:
-                    /* XXX: TODO */
-                    cpu_abort(env, "BookE FSL MMU model is not implemented\n");
-                    return -1;
                 case POWERPC_MMU_REAL:
                     cpu_abort(env, "PowerPC in real mode should never raise "
                               "any MMU exceptions\n");
@@ -1551,7 +1686,8 @@  int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
                     if (rw) {
                         env->spr[SPR_40x_ESR] |= 0x00800000;
                     }
-                } else if (env->mmu_model == POWERPC_MMU_BOOKE) {
+                } else if ((env->mmu_model == POWERPC_MMU_BOOKE) ||
+                           (env->mmu_model == POWERPC_MMU_BOOKE_FSL)) {
                     env->spr[SPR_BOOKE_DEAR] = address;
                     env->spr[SPR_BOOKE_ESR] = rw ? 1 << ESR_ST : 0;
                 } else {
@@ -1820,12 +1956,8 @@  void ppc_tlb_invalidate_all (CPUPPCState *env)
         cpu_abort(env, "MPC8xx MMU model is not implemented\n");
         break;
     case POWERPC_MMU_BOOKE:
-        tlb_flush(env, 1);
-        break;
     case POWERPC_MMU_BOOKE_FSL:
-        /* XXX: TODO */
-        if (!kvm_enabled())
-            cpu_abort(env, "BookE MMU model is not implemented\n");
+        tlb_flush(env, 1);
         break;
     case POWERPC_MMU_32B:
     case POWERPC_MMU_601:
@@ -2589,7 +2721,8 @@  static inline void powerpc_excp(CPUState *env, int excp_model, int excp)
     env->exception_index = POWERPC_EXCP_NONE;
     env->error_code = 0;
 
-    if (env->mmu_model == POWERPC_MMU_BOOKE) {
+    if ((env->mmu_model == POWERPC_MMU_BOOKE) ||
+        (env->mmu_model == POWERPC_MMU_BOOKE_FSL)) {
         /* XXX: The BookE changes address space when switching modes,
                 we should probably implement that as different MMU indexes,
                 but for the moment we do it the slow way and flush all.  */
diff --git a/target-ppc/helper.h b/target-ppc/helper.h
index 7c02be9..7839aad 100644
--- a/target-ppc/helper.h
+++ b/target-ppc/helper.h
@@ -334,6 +334,11 @@  DEF_HELPER_1(4xx_tlbsx, tl, tl)
 DEF_HELPER_2(440_tlbre, tl, i32, tl)
 DEF_HELPER_3(440_tlbwe, void, i32, tl, tl)
 DEF_HELPER_1(440_tlbsx, tl, tl)
+DEF_HELPER_0(e500_tlbre, void)
+DEF_HELPER_0(e500_tlbwe, void)
+DEF_HELPER_2(e500_tlbsx, void, tl, tl)
+DEF_HELPER_1(e500_tlbivax, void, tl)
+DEF_HELPER_1(e500_tlbflush, void, i32)
 DEF_HELPER_1(6xx_tlbd, void, tl)
 DEF_HELPER_1(6xx_tlbi, void, tl)
 DEF_HELPER_1(74xx_tlbd, void, tl)
diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c
index d5db484..3c0b061 100644
--- a/target-ppc/op_helper.c
+++ b/target-ppc/op_helper.c
@@ -4206,4 +4206,422 @@  target_ulong helper_440_tlbsx (target_ulong address)
     return ppcemb_tlb_search(env, address, env->spr[SPR_440_MMUCR] & 0xFF);
 }
 
+/* PowerPC e500 TLB management */
+
+static inline int e500_tlb_num(CPUState *env, ppcemb_tlb_t *tlb)
+{
+    ulong tlbel = (ulong)tlb;
+    ulong tlbl = (ulong)env->tlb;
+
+    return (tlbel - tlbl) / sizeof(env->tlb[0]);
+}
+
+static inline ppcemb_tlb_t *e500_cur_tlb(CPUState *env)
+{
+    uint32_t tlbncfg = 0;
+    int esel = (env->spr[SPR_BOOKE_MAS0] & MAS0_ESEL_MASK) >> MAS0_ESEL_SHIFT;
+    int ea = (env->spr[SPR_BOOKE_MAS2] & MAS2_EPN_MASK) >> MAS2_EPN_SHIFT;
+    int tlb;
+    int r;
+
+    tlb = (env->spr[SPR_BOOKE_MAS0] & MAS0_TLBSEL_MASK) >> MAS0_TLBSEL_SHIFT;
+    tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlb];
+
+    if ((tlbncfg & TLBnCFG_HES) && (env->spr[SPR_BOOKE_MAS0] & MAS0_HES)) {
+        cpu_abort(env, "we don't support HES yet\n");
+    }
+
+    if (tlb == 1) {
+        r = env->nb_tlbs[0] + (esel % env->nb_tlbs[1]);
+    } else {
+        esel &= env->nb_ways - 1;
+        esel <<= 7;
+        ea &= 0x7f;
+        r = (esel | ea) % env->nb_tlbs[0];
+    }
+
+    return &env->tlb[r].tlbe;
+}
+
+static const target_phys_addr_t e500_tlb_sizes[] = {
+    0,
+    4 * 1024,
+    16 * 1024,
+    64 * 1024,
+    256 * 1024,
+    1024 * 1024,
+    4 * 1024 * 1024,
+    16 * 1024 * 1024,
+    64 * 1024 * 1024,
+    256 * 1024 * 1024,
+    1024 * 1024 * 1024,
+    (target_phys_addr_t)(4ULL * 1024ULL * 1024ULL * 1024ULL),
+};
+
+static inline target_phys_addr_t e500_tlb_to_page_size(int size)
+{
+    target_phys_addr_t r;
+
+    if (size > 11) {
+        /* should not happen */
+        r = 1024 * 1024 * 1024;
+    } else {
+        r = e500_tlb_sizes[size];
+    }
+
+    return r;
+}
+
+static inline target_phys_addr_t e500_page_size_to_tlb(uint64_t size)
+{
+    int i;
+    target_phys_addr_t r = 0;
+
+    for (i = 0; i < ARRAY_SIZE(e500_tlb_sizes); i++) {
+        if (e500_tlb_sizes[i] == size) {
+            r = i;
+            break;
+        }
+    }
+
+    return r;
+}
+
+void helper_e500_tlbwe(void)
+{
+    uint32_t tlbncfg;
+    ppcemb_tlb_t *tlb;
+    target_phys_addr_t rpn;
+
+    switch (env->spr[SPR_BOOKE_MAS0] & MAS0_WQ_MASK) {
+    case MAS0_WQ_ALWAYS:
+        /* good to go, write that entry */
+        break;
+    case MAS0_WQ_COND:
+        /* XXX check if reserved */
+        if (0) {
+            return;
+        }
+        break;
+    case MAS0_WQ_CLR_RSRV:
+        /* XXX clear entry */
+        return;
+    default:
+        /* no idea what to do */
+        return;
+    }
+
+    if (((env->spr[SPR_BOOKE_MAS0] & MAS0_ATSEL) == MAS0_ATSEL_LRAT) &&
+         !msr_gs) {
+        /* XXX we don't support direct LRAT setting yet */
+        fprintf(stderr, "cpu: don't support LRAT setting yet\n");
+        return;
+    }
+
+    switch (env->spr[SPR_BOOKE_MAS0] & MAS0_TLBSEL_MASK) {
+    case MAS0_TLBSEL_TLB0:
+        tlbncfg = env->spr[SPR_BOOKE_TLB0CFG];
+        break;
+    case MAS0_TLBSEL_TLB1:
+        tlbncfg = env->spr[SPR_BOOKE_TLB1CFG];
+        break;
+    case MAS0_TLBSEL_TLB2:
+        tlbncfg = env->spr[SPR_BOOKE_TLB2CFG];
+        break;
+    case MAS0_TLBSEL_TLB3:
+        tlbncfg = env->spr[SPR_BOOKE_TLB3CFG];
+        break;
+    }
+
+    tlb = e500_cur_tlb(env);
+
+    /* XXX check for IPROT */
+
+    if (msr_gs) {
+        cpu_abort(env, "missing HV implementation\n");
+    } else {
+        rpn = ((uint64_t)env->spr[SPR_BOOKE_MAS7] << 32) |
+              (env->spr[SPR_BOOKE_MAS3] & 0xfffff000);
+    }
+    tlb->RPN = rpn;
+
+    tlb->PID = (env->spr[SPR_BOOKE_MAS1] & MAS1_TID_MASK) >> MAS1_TID_SHIFT;
+    tlb->size = e500_tlb_to_page_size((env->spr[SPR_BOOKE_MAS1]
+                      & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT);
+    tlb->EPN = (uint32_t)(env->spr[SPR_BOOKE_MAS2] & MAS2_EPN_MASK);
+    tlb->attr = env->spr[SPR_BOOKE_MAS2] & (MAS2_ACM | MAS2_VLE | MAS2_W |
+                                            MAS2_I | MAS2_M | MAS2_G | MAS2_E)
+                << 1;
+    tlb->attr |= env->spr[SPR_BOOKE_MAS1] & MAS1_IPROT;
+    tlb->attr |= (env->spr[SPR_BOOKE_MAS3] &
+                  ((MAS3_U0 | MAS3_U1 | MAS3_U2 | MAS3_U3)) << 8);
+    if (env->spr[SPR_BOOKE_MAS1] & MAS1_TS) {
+        tlb->attr |= 1;
+    }
+
+    tlb->prot = 0;
+
+    if (env->spr[SPR_BOOKE_MAS1] & MAS1_VALID) {
+        tlb->prot |= PAGE_VALID;
+    }
+    if (env->spr[SPR_BOOKE_MAS3] & MAS3_UX) {
+        tlb->prot |= PAGE_EXEC;
+    }
+    if (env->spr[SPR_BOOKE_MAS3] & MAS3_SX) {
+        tlb->prot |= PAGE_EXEC << 4;
+    }
+    if (env->spr[SPR_BOOKE_MAS3] & MAS3_UW) {
+        tlb->prot |= PAGE_WRITE;
+    }
+    if (env->spr[SPR_BOOKE_MAS3] & MAS3_SW) {
+        tlb->prot |= PAGE_WRITE << 4;
+    }
+    if (env->spr[SPR_BOOKE_MAS3] & MAS3_UR) {
+        tlb->prot |= PAGE_READ;
+    }
+    if (env->spr[SPR_BOOKE_MAS3] & MAS3_SR) {
+        tlb->prot |= PAGE_READ << 4;
+    }
+
+    if (tlb->size == TARGET_PAGE_SIZE) {
+        tlb_flush_page(env, tlb->EPN);
+    } else {
+        tlb_flush(env, 1);
+    }
+}
+
+static inline void e500_tlb_to_mas(CPUState *env, ppcemb_tlb_t *tlb, int i)
+{
+    env->spr[SPR_BOOKE_MAS0] = 0;
+    env->spr[SPR_BOOKE_MAS1] = MAS1_VALID;
+    env->spr[SPR_BOOKE_MAS2] = 0;
+
+    if (i >= env->nb_tlbs[0]) {
+        env->spr[SPR_BOOKE_MAS0] |= MAS0_TLBSEL_TLB1;
+        env->spr[SPR_BOOKE_MAS0] |= (i - env->nb_tlbs[0]) << MAS0_ESEL_SHIFT;
+    } else {
+        env->spr[SPR_BOOKE_MAS0] |= MAS0_TLBSEL_TLB0;
+        env->spr[SPR_BOOKE_MAS0] |= (i >> 7) << MAS0_ESEL_SHIFT;
+    }
+
+    env->spr[SPR_BOOKE_MAS7] = (uint64_t)tlb->RPN >> 32;
+    env->spr[SPR_BOOKE_MAS3] = tlb->RPN;
+    env->spr[SPR_BOOKE_MAS1] |= tlb->PID << MAS1_TID_SHIFT;
+    env->spr[SPR_BOOKE_MAS1] |= e500_page_size_to_tlb(tlb->size)
+                                << MAS1_TSIZE_SHIFT;
+    env->spr[SPR_BOOKE_MAS1] |= tlb->attr & MAS1_IPROT;
+    if (tlb->attr & 1) {
+        env->spr[SPR_BOOKE_MAS1] |= MAS1_TS;
+    }
+
+    env->spr[SPR_BOOKE_MAS2] = tlb->EPN;
+    env->spr[SPR_BOOKE_MAS2] |= (tlb->attr >> 1) &
+        (MAS2_ACM | MAS2_VLE | MAS2_W | MAS2_I | MAS2_M | MAS2_G | MAS2_E);
+
+    if (tlb->prot & PAGE_EXEC) {
+        env->spr[SPR_BOOKE_MAS3] |= MAS3_UX;
+    }
+    if (tlb->prot & (PAGE_EXEC << 4)) {
+        env->spr[SPR_BOOKE_MAS3] |= MAS3_SX;
+    }
+    if (tlb->prot & PAGE_WRITE) {
+        env->spr[SPR_BOOKE_MAS3] |= MAS3_UW;
+    }
+    if (tlb->prot & (PAGE_WRITE << 4)) {
+        env->spr[SPR_BOOKE_MAS3] |= MAS3_SW;
+    }
+    if (tlb->prot & PAGE_READ) {
+        env->spr[SPR_BOOKE_MAS3] |= MAS3_UR;
+    }
+    if (tlb->prot & (PAGE_READ << 4)) {
+        env->spr[SPR_BOOKE_MAS3] |= MAS3_SR;
+    }
+
+    env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT;
+}
+
+void helper_e500_tlbre(void)
+{
+    ppcemb_tlb_t *tlb = NULL;
+
+    tlb = e500_cur_tlb(env);
+    e500_tlb_to_mas(env, tlb, e500_tlb_num(env, tlb));
+}
+
+/* Generic TLB check function for embedded PowerPC implementations */
+static inline int ppcemb_tlb_check(CPUState *env, ppcemb_tlb_t *tlb,
+                                   target_phys_addr_t *raddrp,
+                                   target_ulong address, uint32_t pid, int ext,
+                                   int i)
+{
+    target_ulong mask;
+
+    /* Check valid flag */
+    if (!(tlb->prot & PAGE_VALID)) {
+        return -1;
+    }
+    mask = ~(tlb->size - 1);
+
+    /* Check PID */
+    if (tlb->PID != 0 && tlb->PID != pid) {
+        return -1;
+    }
+    /* Check effective address */
+    if ((address & mask) != tlb->EPN) {
+        return -1;
+    }
+    *raddrp = (tlb->RPN & mask) | (address & ~mask);
+#if (TARGET_PHYS_ADDR_BITS >= 36)
+    if (ext) {
+        /* Extend the physical address to 36 bits */
+        *raddrp |= (target_phys_addr_t)(tlb->RPN & 0xF) << 32;
+    }
+#endif
+
+    return 0;
+}
+
+void helper_e500_tlbsx(target_ulong address_hi, target_ulong address_lo)
+{
+    ppcemb_tlb_t *tlb = NULL;
+    int i;
+    target_phys_addr_t raddr;
+    uint32_t spid, sas;
+    uint32_t ea = (address_lo >> MAS2_EPN_SHIFT) & 0x7f;
+
+    spid = (env->spr[SPR_BOOKE_MAS6] & MAS6_SPID_MASK) >> MAS6_SPID_SHIFT;
+    sas = env->spr[SPR_BOOKE_MAS6] & MAS6_SAS;
+
+    /* check possible TLB0 entries */
+    for (i = 0; i < env->nb_ways; i++) {
+        tlb = &env->tlb[ea | (i << 7)].tlbe;
+
+        if (ppcemb_tlb_check(env, tlb, &raddr, address_lo, spid, 0, i) < 0) {
+            continue;
+        }
+
+        if (sas != (tlb->attr & MAS6_SAS)) {
+            continue;
+        }
+
+        e500_tlb_to_mas(env, tlb, ea | (i << 7));
+        return;
+    }
+
+    /* check TLB1 */
+    for (i = env->nb_tlbs[0]; i < (env->nb_tlbs[0] + env->nb_tlbs[1]); i++) {
+        tlb = &env->tlb[i].tlbe;
+
+        if (ppcemb_tlb_check(env, tlb, &raddr, address_lo, spid, 0, i) < 0) {
+            continue;
+        }
+
+        if (sas != (tlb->attr & MAS6_SAS)) {
+            continue;
+        }
+
+        e500_tlb_to_mas(env, tlb, i);
+        return;
+    }
+
+    /* no entry found, fill with defaults */
+    env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK;
+    env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK;
+    env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK;
+
+    if (env->spr[SPR_BOOKE_MAS6] & MAS6_SAS) {
+        env->spr[SPR_BOOKE_MAS1] |= MAS1_TS;
+    }
+
+    env->spr[SPR_BOOKE_MAS1] |= (env->spr[SPR_BOOKE_MAS6] >> 16)
+                                << MAS1_TID_SHIFT;
+
+    /* next victim logic */
+    env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT;
+    env->last_way++;
+    env->last_way &= 3;
+    env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT;
+}
+
+static inline void e500_invalidate_ea_tlb0(CPUState *env, uint32_t ea)
+{
+    int i;
+    ppcemb_tlb_t *tlb;
+
+    ea &= 0x7f;
+
+    /* check only possible TLB0 entries */
+    for (i = 0; i < env->nb_ways; i++) {
+        tlb = &env->tlb[ea | (i << 7)].tlbe;
+        if (tlb->EPN == ea) {
+            tlb->prot = 0;
+        }
+    }
+}
+
+static inline void e500_invalidate_ea_tlb1(CPUState *env, uint32_t ea)
+{
+    int i;
+
+    for (i = env->nb_tlbs[0]; i < (env->nb_tlbs[0] + env->nb_tlbs[1]); i++) {
+        ppcemb_tlb_t *tlb = &env->tlb[i].tlbe;
+        if ((tlb->EPN == (ea & ~(tlb->size - 1))) &&
+            !(tlb->attr & MAS1_IPROT)) {
+            tlb->prot = 0;
+        }
+    }
+}
+
+void helper_e500_tlbivax(target_ulong address)
+{
+    uint32_t ea = address >> MAS2_EPN_SHIFT;
+
+    if (address & 0x4) {
+        /* flush all entries */
+        if (address & 0x8) {
+            /* flush all of TLB1 */
+            helper_e500_tlbflush(2);
+        } else {
+            /* flush all of TLB0 */
+            helper_e500_tlbflush(4);
+        }
+        return;
+    }
+
+    if (address & 0x8) {
+        /* flush TLB1 entries */
+        e500_invalidate_ea_tlb1(env, ea);
+        tlb_flush(env, 1);
+    } else {
+        /* flush TLB0 entries */
+        e500_invalidate_ea_tlb0(env, ea);
+        tlb_flush_page(env, address & MAS2_EPN_MASK);
+    }
+}
+
+void helper_e500_tlbflush(uint32_t type)
+{
+    int i;
+
+    if (type & 4) {
+        /* flush tlb0 */
+        for (i = 0; i < env->nb_tlbs[0]; i++) {
+            ppcemb_tlb_t *tlb = &env->tlb[i].tlbe;
+            tlb->prot = 0;
+        }
+    }
+
+    if (type & 2) {
+        /* flush tlb1 */
+        for (i = env->nb_tlbs[0]; i < env->nb_tlb; i++) {
+            ppcemb_tlb_t *tlb = &env->tlb[i].tlbe;
+            if (!(tlb->attr & MAS1_IPROT)) {
+                tlb->prot = 0;
+            }
+        }
+    }
+
+    tlb_flush(env, 1);
+}
+
 #endif /* !CONFIG_USER_ONLY */
diff --git a/target-ppc/translate.c b/target-ppc/translate.c
index 9076df0..629d019 100644
--- a/target-ppc/translate.c
+++ b/target-ppc/translate.c
@@ -5987,6 +5987,78 @@  static void gen_tlbwe_440(DisasContext *ctx)
 #endif
 }
 
+/* TLB management - PowerPC e500 implementation */
+
+/* tlbre */
+static void gen_tlbre_e500(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+    if (unlikely(!ctx->mem_idx)) {
+        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+        return;
+    }
+
+    gen_helper_e500_tlbre();
+#endif
+}
+
+/* tlbsx - tlbsx. */
+static void gen_tlbsx_e500(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+    TCGv t0;
+    if (unlikely(!ctx->mem_idx)) {
+        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+        return;
+    }
+
+    if (rA(ctx->opcode)) {
+        t0 = cpu_gpr[rD(ctx->opcode)];
+    } else {
+        t0 = tcg_const_tl(0);
+    }
+
+    gen_helper_e500_tlbsx(t0, cpu_gpr[rB(ctx->opcode)]);
+#endif
+}
+
+/* tlbwe */
+static void gen_tlbwe_e500(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+    if (unlikely(!ctx->mem_idx)) {
+        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+        return;
+    }
+    gen_helper_e500_tlbwe();
+#endif
+}
+
+static void gen_tlbivax_e500(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+    TCGv t0;
+    if (unlikely(!ctx->mem_idx)) {
+        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+        return;
+    }
+
+    t0 = tcg_temp_new();
+    gen_addr_reg_index(ctx, t0);
+
+    gen_helper_e500_tlbivax(t0);
+#endif
+}
+
+
 /* wrtee */
 static void gen_wrtee(DisasContext *ctx)
 {
@@ -8433,7 +8505,7 @@  GEN_HANDLER2(icbt_40x, "icbt", 0x1F, 0x06, 0x08, 0x03E00001, PPC_40x_ICBT),
 GEN_HANDLER(iccci, 0x1F, 0x06, 0x1E, 0x00000001, PPC_4xx_COMMON),
 GEN_HANDLER(icread, 0x1F, 0x06, 0x1F, 0x03E00001, PPC_4xx_COMMON),
 GEN_HANDLER2(rfci_40x, "rfci", 0x13, 0x13, 0x01, 0x03FF8001, PPC_40x_EXCP),
-GEN_HANDLER(rfci, 0x13, 0x13, 0x01, 0x03FF8001, PPC_BOOKE),
+GEN_HANDLER_E(rfci, 0x13, 0x13, 0x01, 0x03FF8001, PPC_BOOKE, PPC2_BOOKE_FSL),
 GEN_HANDLER(rfdi, 0x13, 0x07, 0x01, 0x03FF8001, PPC_RFDI),
 GEN_HANDLER(rfmci, 0x13, 0x06, 0x01, 0x03FF8001, PPC_RFMCI),
 GEN_HANDLER2(tlbre_40x, "tlbre", 0x1F, 0x12, 0x1D, 0x00000001, PPC_40x_TLB),
@@ -8442,12 +8514,23 @@  GEN_HANDLER2(tlbwe_40x, "tlbwe", 0x1F, 0x12, 0x1E, 0x00000001, PPC_40x_TLB),
 GEN_HANDLER2(tlbre_440, "tlbre", 0x1F, 0x12, 0x1D, 0x00000001, PPC_BOOKE),
 GEN_HANDLER2(tlbsx_440, "tlbsx", 0x1F, 0x12, 0x1C, 0x00000000, PPC_BOOKE),
 GEN_HANDLER2(tlbwe_440, "tlbwe", 0x1F, 0x12, 0x1E, 0x00000001, PPC_BOOKE),
+GEN_HANDLER2_E(tlbre_e500, "tlbre", 0x1F, 0x12, 0x1D, 0x00000001,
+               PPC_NONE, PPC2_BOOKE_FSL),
+GEN_HANDLER2_E(tlbsx_e500, "tlbsx", 0x1F, 0x12, 0x1C, 0x00000000,
+               PPC_NONE, PPC2_BOOKE_FSL),
+GEN_HANDLER2_E(tlbwe_e500, "tlbwe", 0x1F, 0x12, 0x1E, 0x00000001,
+               PPC_NONE, PPC2_BOOKE_FSL),
+GEN_HANDLER2_E(tlbivax_e500, "tlbivax", 0x1F, 0x12, 0x18, 0x00000001,
+               PPC_NONE, PPC2_BOOKE_FSL),
 GEN_HANDLER(wrtee, 0x1F, 0x03, 0x04, 0x000FFC01, PPC_WRTEE),
 GEN_HANDLER(wrteei, 0x1F, 0x03, 0x05, 0x000E7C01, PPC_WRTEE),
 GEN_HANDLER(dlmzb, 0x1F, 0x0E, 0x02, 0x00000000, PPC_440_SPEC),
-GEN_HANDLER(mbar, 0x1F, 0x16, 0x1a, 0x001FF801, PPC_BOOKE),
-GEN_HANDLER(msync, 0x1F, 0x16, 0x12, 0x03FFF801, PPC_BOOKE),
-GEN_HANDLER2(icbt_440, "icbt", 0x1F, 0x16, 0x00, 0x03E00001, PPC_BOOKE),
+GEN_HANDLER_E(mbar, 0x1F, 0x16, 0x1a, 0x001FF801,
+              PPC_BOOKE, PPC2_BOOKE_FSL),
+GEN_HANDLER_E(msync, 0x1F, 0x16, 0x12, 0x03FFF801,
+              PPC_BOOKE, PPC2_BOOKE_FSL),
+GEN_HANDLER2_E(icbt_440, "icbt", 0x1F, 0x16, 0x00, 0x03E00001,
+               PPC_BOOKE, PPC2_BOOKE_FSL),
 GEN_HANDLER(lvsl, 0x1f, 0x06, 0x00, 0x00000001, PPC_ALTIVEC),
 GEN_HANDLER(lvsr, 0x1f, 0x06, 0x01, 0x00000001, PPC_ALTIVEC),
 GEN_HANDLER(mfvscr, 0x04, 0x2, 0x18, 0x001ff800, PPC_ALTIVEC),
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
index aec0e13..2d7afa2 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -1355,6 +1355,31 @@  static void gen_74xx_soft_tlb (CPUPPCState *env, int nb_tlbs, int nb_ways)
 #endif
 }
 
+#if !defined(CONFIG_USER_ONLY)
+static void spr_write_e500_l1csr0 (void *opaque, int sprn, int gprn)
+{
+    TCGv t0 = tcg_temp_new();
+
+    tcg_gen_andi_tl(t0, cpu_gpr[gprn], ~256);
+    gen_store_spr(sprn, t0);
+    tcg_temp_free(t0);
+}
+
+static void spr_write_e500_mmucsr0 (void *opaque, int sprn, int gprn)
+{
+    TCGv t0 = tcg_const_i32(sprn);
+    gen_helper_e500_tlbflush(t0);
+    tcg_temp_free(t0);
+}
+
+static void spr_write_booke_pid (void *opaque, int sprn, int gprn)
+{
+    gen_store_spr(sprn, cpu_gpr[gprn]);
+    /* switching context, so need to flush tlb */
+    gen_helper_tlbia();
+}
+#endif
+
 static void gen_spr_usprgh (CPUPPCState *env)
 {
     spr_register(env, SPR_USPRG4, "USPRG4",
@@ -1494,7 +1519,7 @@  static void gen_spr_BookE (CPUPPCState *env, uint64_t ivor_mask)
     }
     spr_register(env, SPR_BOOKE_PID, "PID",
                  SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
+                 &spr_read_generic, &spr_write_booke_pid,
                  0x00000000);
     spr_register(env, SPR_BOOKE_TCR, "TCR",
                  SPR_NOACCESS, SPR_NOACCESS,
@@ -1536,8 +1561,19 @@  static void gen_spr_BookE (CPUPPCState *env, uint64_t ivor_mask)
                  0x00000000);
 }
 
+static inline uint32_t gen_tlbncfg(uint32_t assoc, uint32_t minsize,
+                                   uint32_t maxsize, uint32_t flags,
+                                   uint32_t nentries)
+{
+    return (assoc << TLBnCFG_ASSOC_SHIFT) |
+           (minsize << TLBnCFG_MINSIZE_SHIFT) |
+           (maxsize << TLBnCFG_MAXSIZE_SHIFT) |
+           flags | nentries;
+}
+
 /* FSL storage control registers */
-static void gen_spr_BookE_FSL (CPUPPCState *env, uint32_t mas_mask)
+static void gen_spr_BookE_FSL(CPUPPCState *env, uint32_t mas_mask,
+                              uint32_t *tlbncfg)
 {
 #if !defined(CONFIG_USER_ONLY)
     const char *mas_names[8] = {
@@ -1563,14 +1599,14 @@  static void gen_spr_BookE_FSL (CPUPPCState *env, uint32_t mas_mask)
         /* XXX : not implemented */
         spr_register(env, SPR_BOOKE_PID1, "PID1",
                      SPR_NOACCESS, SPR_NOACCESS,
-                     &spr_read_generic, &spr_write_generic,
+                     &spr_read_generic, &spr_write_booke_pid,
                      0x00000000);
     }
     if (env->nb_pids > 2) {
         /* XXX : not implemented */
         spr_register(env, SPR_BOOKE_PID2, "PID2",
                      SPR_NOACCESS, SPR_NOACCESS,
-                     &spr_read_generic, &spr_write_generic,
+                     &spr_read_generic, &spr_write_booke_pid,
                      0x00000000);
     }
     /* XXX : not implemented */
@@ -1578,45 +1614,38 @@  static void gen_spr_BookE_FSL (CPUPPCState *env, uint32_t mas_mask)
                  SPR_NOACCESS, SPR_NOACCESS,
                  &spr_read_generic, SPR_NOACCESS,
                  0x00000000); /* TOFIX */
-    /* XXX : not implemented */
-    spr_register(env, SPR_MMUCSR0, "MMUCSR0",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000); /* TOFIX */
     switch (env->nb_ways) {
     case 4:
-        /* XXX : not implemented */
         spr_register(env, SPR_BOOKE_TLB3CFG, "TLB3CFG",
                      SPR_NOACCESS, SPR_NOACCESS,
                      &spr_read_generic, SPR_NOACCESS,
-                     0x00000000); /* TOFIX */
+                     tlbncfg[3]);
         /* Fallthru */
     case 3:
-        /* XXX : not implemented */
         spr_register(env, SPR_BOOKE_TLB2CFG, "TLB2CFG",
                      SPR_NOACCESS, SPR_NOACCESS,
                      &spr_read_generic, SPR_NOACCESS,
-                     0x00000000); /* TOFIX */
+                     tlbncfg[2]);
         /* Fallthru */
     case 2:
-        /* XXX : not implemented */
         spr_register(env, SPR_BOOKE_TLB1CFG, "TLB1CFG",
                      SPR_NOACCESS, SPR_NOACCESS,
                      &spr_read_generic, SPR_NOACCESS,
-                     0x00000000); /* TOFIX */
+                     tlbncfg[1]);
         /* Fallthru */
     case 1:
-        /* XXX : not implemented */
         spr_register(env, SPR_BOOKE_TLB0CFG, "TLB0CFG",
                      SPR_NOACCESS, SPR_NOACCESS,
                      &spr_read_generic, SPR_NOACCESS,
-                     0x00000000); /* TOFIX */
+                     tlbncfg[0]);
         /* Fallthru */
     case 0:
     default:
         break;
     }
 #endif
+
+    gen_spr_usprgh(env);
 }
 
 /* SPR specific to PowerPC 440 implementation */
@@ -4134,7 +4163,7 @@  static void init_proc_e200 (CPUPPCState *env)
                  &spr_read_spefscr, &spr_write_spefscr,
                  0x00000000);
     /* Memory management */
-    gen_spr_BookE_FSL(env, 0x0000005D);
+    gen_spr_BookE_FSL(env, 0x0000005D, NULL);
     /* XXX : not implemented */
     spr_register(env, SPR_HID0, "HID0",
                  SPR_NOACCESS, SPR_NOACCESS,
@@ -4205,6 +4234,11 @@  static void init_proc_e200 (CPUPPCState *env)
                  SPR_NOACCESS, SPR_NOACCESS,
                  &spr_read_generic, &spr_write_generic,
                  0x00000000);
+    /* XXX : not implemented */
+    spr_register(env, SPR_MMUCSR0, "MMUCSR0",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000); /* TOFIX */
     spr_register(env, SPR_BOOKE_DSRR0, "DSRR0",
                  SPR_NOACCESS, SPR_NOACCESS,
                  &spr_read_generic, &spr_write_generic,
@@ -4282,9 +4316,8 @@  static void init_proc_e300 (CPUPPCState *env)
                                 PPC_WRTEE | PPC_RFDI |                  \
                                 PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | \
                                 PPC_CACHE_DCBZ | PPC_CACHE_DCBA |       \
-                                PPC_MEM_TLBSYNC | PPC_TLBIVAX |         \
-                                PPC_BOOKE)
-#define POWERPC_INSNS2_e500v1  (PPC_NONE)
+                                PPC_MEM_TLBSYNC | PPC_TLBIVAX)
+#define POWERPC_INSNS2_e500v1  (PPC2_BOOKE_FSL)
 #define POWERPC_MSRM_e500v1    (0x000000000606FF30ULL)
 #define POWERPC_MMU_e500v1     (POWERPC_MMU_BOOKE_FSL)
 #define POWERPC_EXCP_e500v1    (POWERPC_EXCP_BOOKE)
@@ -4302,9 +4335,8 @@  static void init_proc_e300 (CPUPPCState *env)
                                 PPC_WRTEE | PPC_RFDI |                  \
                                 PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | \
                                 PPC_CACHE_DCBZ | PPC_CACHE_DCBA |       \
-                                PPC_MEM_TLBSYNC | PPC_TLBIVAX |         \
-                                PPC_BOOKE)
-#define POWERPC_INSNS2_e500v2  (PPC_NONE)
+                                PPC_MEM_TLBSYNC | PPC_TLBIVAX)
+#define POWERPC_INSNS2_e500v2  (PPC2_BOOKE_FSL)
 #define POWERPC_MSRM_e500v2    (0x000000000606FF30ULL)
 #define POWERPC_MMU_e500v2     (POWERPC_MMU_BOOKE_FSL)
 #define POWERPC_EXCP_e500v2    (POWERPC_EXCP_BOOKE)
@@ -4318,9 +4350,16 @@  static void init_proc_e300 (CPUPPCState *env)
 
 static void init_proc_e500 (CPUPPCState *env)
 {
+    uint32_t tlbncfg[2];
+
     /* Time base */
     gen_tbl(env);
-    gen_spr_BookE(env, 0x0000000F0000FD7FULL);
+    /*
+     * XXX The e500 doesn't implement IVOR7 and IVOR9, but doesn't
+     *     complain when accessing them.
+     * gen_spr_BookE(env, 0x0000000F0000FD7FULL);
+     */
+    gen_spr_BookE(env, 0x0000000F0000FFFFULL);
     /* Processor identification */
     spr_register(env, SPR_BOOKE_PIR, "PIR",
                  SPR_NOACCESS, SPR_NOACCESS,
@@ -4334,8 +4373,23 @@  static void init_proc_e500 (CPUPPCState *env)
     /* Memory management */
 #if !defined(CONFIG_USER_ONLY)
     env->nb_pids = 3;
+    env->nb_ways = 2;
+    env->id_tlbs = 0;
+    if ((env->spr[SPR_PVR] & 0x00010000)) {
+        /* e500v2 */
+        env->nb_tlbs[0] = 512;
+        tlbncfg[0] = gen_tlbncfg(4, 1, 1, 0, env->nb_tlbs[0]);
+        tlbncfg[1] = gen_tlbncfg(16, 1, 12, TLBnCFG_AVAIL | TLBnCFG_IPROT, 16);
+    } else {
+        /* e500v1 */
+        env->nb_tlbs[0] = 256;
+        tlbncfg[0] = gen_tlbncfg(2, 1, 1, 0, env->nb_tlbs[0]);
+        tlbncfg[1] = gen_tlbncfg(16, 1, 9, TLBnCFG_AVAIL | TLBnCFG_IPROT, 16);
+    }
+    env->nb_tlbs[1] = 16;
+    env->nb_tlb = env->nb_tlbs[0] + env->nb_tlbs[1];
 #endif
-    gen_spr_BookE_FSL(env, 0x0000005F);
+    gen_spr_BookE_FSL(env, 0x000000DF, tlbncfg);
     /* XXX : not implemented */
     spr_register(env, SPR_HID0, "HID0",
                  SPR_NOACCESS, SPR_NOACCESS,
@@ -4384,23 +4438,13 @@  static void init_proc_e500 (CPUPPCState *env)
     /* XXX : not implemented */
     spr_register(env, SPR_Exxx_L1CSR0, "L1CSR0",
                  SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
+                 &spr_read_generic, &spr_write_e500_l1csr0,
                  0x00000000);
     /* XXX : not implemented */
     spr_register(env, SPR_Exxx_L1CSR1, "L1CSR1",
                  SPR_NOACCESS, SPR_NOACCESS,
                  &spr_read_generic, &spr_write_generic,
                  0x00000000);
-    /* XXX : not implemented */
-    spr_register(env, SPR_BOOKE_TLB0CFG, "TLB0CFG",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-    /* XXX : not implemented */
-    spr_register(env, SPR_BOOKE_TLB1CFG, "TLB1CFG",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
     spr_register(env, SPR_BOOKE_MCSRR0, "MCSRR0",
                  SPR_NOACCESS, SPR_NOACCESS,
                  &spr_read_generic, &spr_write_generic,
@@ -4409,11 +4453,10 @@  static void init_proc_e500 (CPUPPCState *env)
                  SPR_NOACCESS, SPR_NOACCESS,
                  &spr_read_generic, &spr_write_generic,
                  0x00000000);
-#if !defined(CONFIG_USER_ONLY)
-    env->nb_tlb = 64;
-    env->nb_ways = 1;
-    env->id_tlbs = 0;
-#endif
+    spr_register(env, SPR_MMUCSR0, "MMUCSR0",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_e500_mmucsr0,
+                 0x00000000);
     init_excp_e200(env);
     env->dcache_line_size = 32;
     env->icache_line_size = 32;