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

login
register
mail settings
Submitter Alexander Graf
Date May 6, 2011, noon
Message ID <1304683237-26177-6-git-send-email-agraf@suse.de>
Download mbox | patch
Permalink /patch/94372/
State New
Headers show

Comments

Alexander Graf - May 6, 2011, noon
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
Scott Wood - May 6, 2011, 10:25 p.m.
On Fri, 6 May 2011 14:00:35 +0200
Alexander Graf <agraf@suse.de> wrote:

> +static inline void booke206_flush_tlb_one(ppc_tlb_t *tlb, int num)
> +{
> +    int i;
> +
> +    for (i = 0; i < num; i++) {
> +        if ((tlb[i].tlbe.prot & PAGE_VALID) &&
> +           !(tlb[i].tlbe.attr & MAS1_IPROT)) {
> +            tlb[i].tlbe.prot = 0;
> +        }
> +    }
> +}

When this is called on reset, we want to ignore IPROT.

The valid check seems unnecessary.

> +void helper_booke206_tlbsx(target_ulong address_hi, target_ulong address_lo)

What is address_hi?

From gen_tlbsx_booke206() it looks like these two arguments correspond to
the two operands, so shouldn't they be added together?  I only see
address_lo used.

-Scott
Alexander Graf - May 7, 2011, 9:36 p.m.
On 07.05.2011, at 00:25, Scott Wood wrote:

> On Fri, 6 May 2011 14:00:35 +0200
> Alexander Graf <agraf@suse.de> wrote:
> 
>> +static inline void booke206_flush_tlb_one(ppc_tlb_t *tlb, int num)
>> +{
>> +    int i;
>> +
>> +    for (i = 0; i < num; i++) {
>> +        if ((tlb[i].tlbe.prot & PAGE_VALID) &&
>> +           !(tlb[i].tlbe.attr & MAS1_IPROT)) {
>> +            tlb[i].tlbe.prot = 0;
>> +        }
>> +    }
>> +}
> 
> When this is called on reset, we want to ignore IPROT.

Ugh. Yes. This functions needs a constant parameter to know if it should check for IPROT - and not do so on reset :).

> The valid check seems unnecessary.

Yup, but it really helps to reduce debug output when putting in some in that line which I did :). It shouldn't really hurt, right?

>> +void helper_booke206_tlbsx(target_ulong address_hi, target_ulong address_lo)
> 
> What is address_hi?
> 
> From gen_tlbsx_booke206() it looks like these two arguments correspond to
> the two operands, so shouldn't they be added together?  I only see
> address_lo used.

Yup. According to the e500 spec:

  Note that rA = 0 is the preferred form for tlbsx and that some Freescale implementations, such as the e500, take an illegal instruction exception program interrupt if rA!=0.

So I figured that we're architecturally close enough if we just ignore it for now :).


Alex
Scott Wood - May 9, 2011, 7:27 p.m.
On Sat, 7 May 2011 23:36:29 +0200
Alexander Graf <agraf@suse.de> wrote:

> On 07.05.2011, at 00:25, Scott Wood wrote:
> >> +void helper_booke206_tlbsx(target_ulong address_hi, target_ulong address_lo)
> > 
> > What is address_hi?
> > 
> > From gen_tlbsx_booke206() it looks like these two arguments correspond to
> > the two operands, so shouldn't they be added together?  I only see
> > address_lo used.
> 
> Yup. According to the e500 spec:
> 
>   Note that rA = 0 is the preferred form for tlbsx and that some Freescale implementations, such as the e500, take an illegal instruction exception program interrupt if rA!=0.
> 
> So I figured that we're architecturally close enough if we just ignore it for now :).

Architecturally, ignoring it and taking a trap are significantly
different. :-)

In practice it won't matter much, but it seems simple to handle it (why
handle it in tlbivax but not here?), especially if this is to be general
book3e code rather than e500.  I'm still confused about the "address_hi/lo"
naming.

-Scott
Alexander Graf - May 9, 2011, 7:36 p.m.
On 09.05.2011, at 21:27, Scott Wood wrote:

> On Sat, 7 May 2011 23:36:29 +0200
> Alexander Graf <agraf@suse.de> wrote:
> 
>> On 07.05.2011, at 00:25, Scott Wood wrote:
>>>> +void helper_booke206_tlbsx(target_ulong address_hi, target_ulong address_lo)
>>> 
>>> What is address_hi?
>>> 
>>> From gen_tlbsx_booke206() it looks like these two arguments correspond to
>>> the two operands, so shouldn't they be added together?  I only see
>>> address_lo used.
>> 
>> Yup. According to the e500 spec:
>> 
>>  Note that rA = 0 is the preferred form for tlbsx and that some Freescale implementations, such as the e500, take an illegal instruction exception program interrupt if rA!=0.
>> 
>> So I figured that we're architecturally close enough if we just ignore it for now :).
> 
> Architecturally, ignoring it and taking a trap are significantly
> different. :-)
> 
> In practice it won't matter much, but it seems simple to handle it (why
> handle it in tlbivax but not here?), especially if this is to be general
> book3e code rather than e500.  I'm still confused about the "address_hi/lo"
> naming.

So what would you prefer? Just do whatever the 2.06 spec says and ignore e500 specifics? :) Or always do what the e500 spec says?


Alex
Scott Wood - May 9, 2011, 7:42 p.m.
On Mon, 9 May 2011 21:36:12 +0200
Alexander Graf <agraf@suse.de> wrote:

> 
> On 09.05.2011, at 21:27, Scott Wood wrote:
> 
> > On Sat, 7 May 2011 23:36:29 +0200
> > Alexander Graf <agraf@suse.de> wrote:
> > 
> >> On 07.05.2011, at 00:25, Scott Wood wrote:
> >>>> +void helper_booke206_tlbsx(target_ulong address_hi, target_ulong address_lo)
> >>> 
> >>> What is address_hi?
> >>> 
> >>> From gen_tlbsx_booke206() it looks like these two arguments correspond to
> >>> the two operands, so shouldn't they be added together?  I only see
> >>> address_lo used.
> >> 
> >> Yup. According to the e500 spec:
> >> 
> >>  Note that rA = 0 is the preferred form for tlbsx and that some Freescale implementations, such as the e500, take an illegal instruction exception program interrupt if rA!=0.
> >> 
> >> So I figured that we're architecturally close enough if we just ignore it for now :).
> > 
> > Architecturally, ignoring it and taking a trap are significantly
> > different. :-)
> > 
> > In practice it won't matter much, but it seems simple to handle it (why
> > handle it in tlbivax but not here?), especially if this is to be general
> > book3e code rather than e500.  I'm still confused about the "address_hi/lo"
> > naming.
> 
> So what would you prefer? Just do whatever the 2.06 spec says and ignore e500 specifics? :) Or always do what the e500 spec says?

Do what the architecture says.  I doubt any software is going to be worse
off if this instruction form succeeds rather than traps -- if such a
situation actually turns up, we can deal with it then.

-Scott

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

v3 -> v4:

  - remove tlb_nbs
  - rename BOOKE_FSL to BOOKE206
  - generalize implementation to be extensible for future MMUs
  - check if tsize should be set
  - check if iprot should be set
  - set RPN on tlb miss
  - implement tlb_invalidate_all
  - use specific helper to set PIDs
  - create generic booke206 tlb flushing function
  - make e500 check more explicit
  - rename functions to booke206
  - rework page size finder

  Special thanks go to Scott here for pointing out all the issues :)
---
 target-ppc/cpu.h            |  304 ++++++++++++++++++++++++++++++++++++++++++-
 target-ppc/helper.c         |  277 +++++++++++++++++++++++++++++++--------
 target-ppc/helper.h         |    6 +
 target-ppc/op_helper.c      |  296 +++++++++++++++++++++++++++++++++++++++++
 target-ppc/translate.c      |   91 ++++++++++++-
 target-ppc/translate_init.c |  163 ++++++++++++++++-------
 6 files changed, 1024 insertions(+), 113 deletions(-)

diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index 2a7431c..20d995a 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -108,8 +108,8 @@  enum powerpc_mmu_t {
     POWERPC_MMU_MPC8xx     = 0x00000007,
     /* BookE MMU model                                         */
     POWERPC_MMU_BOOKE      = 0x00000008,
-    /* BookE FSL MMU model                                     */
-    POWERPC_MMU_BOOKE_FSL  = 0x00000009,
+    /* BookE 2.06 MMU model                                    */
+    POWERPC_MMU_BOOKE206   = 0x00000009,
     /* PowerPC 601 MMU model (specific BATs format)            */
     POWERPC_MMU_601        = 0x0000000A,
 #if defined(TARGET_PPC64)
@@ -608,6 +608,224 @@  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
+
+/* BookE 2.06 helper defines */
+
+#define BOOKE206_FLUSH_TLB0    (1 << 0)
+#define BOOKE206_FLUSH_TLB1    (1 << 1)
+#define BOOKE206_FLUSH_TLB2    (1 << 2)
+#define BOOKE206_FLUSH_TLB3    (1 << 3)
+
+/* number of possible TLBs */
+#define BOOKE206_MAX_TLBN      4
+
+/*****************************************************************************/
 /* The whole PowerPC CPU context */
 #define NB_MMU_MODES 3
 
@@ -678,7 +896,7 @@  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 tlb_per_way; /* Speed-up helper: used to avoid divisions at run time */
     int nb_ways;     /* Number of ways in the TLB set                        */
@@ -856,6 +1074,10 @@  void store_40x_dbcr0 (CPUPPCState *env, uint32_t val);
 void store_40x_sler (CPUPPCState *env, uint32_t val);
 void store_booke_tcr (CPUPPCState *env, target_ulong val);
 void store_booke_tsr (CPUPPCState *env, target_ulong val);
+void booke206_flush_tlb(CPUState *env, int flags);
+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);
 void ppc_tlb_invalidate_all (CPUPPCState *env);
 void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr);
 #if defined(TARGET_PPC64)
@@ -1547,6 +1769,11 @@  enum {
     PPC_DCRUX          = 0x4000000000000000ULL,
     /* popcntw and popcntd instructions                                      */
     PPC_POPCNTWD       = 0x8000000000000000ULL,
+
+    /* extended type values */
+
+    /* BookE 2.06 PowerPC specification                                      */
+    PPC2_BOOKE206      = 0x0000000000000001ULL,
 };
 
 /*****************************************************************************/
@@ -1699,6 +1926,77 @@  static inline void cpu_set_tls(CPUState *env, target_ulong newtls)
 #endif
 }
 
+#if !defined(CONFIG_USER_ONLY)
+static inline int booke206_tlbe_id(CPUState *env, ppcemb_tlb_t *tlbe)
+{
+    ulong tlbel = (ulong)tlbe;
+    ulong tlbl = (ulong)env->tlb;
+
+    return (tlbel - tlbl) / sizeof(env->tlb[0]);
+}
+
+static inline int booke206_tlb_size(CPUState *env, int tlbn)
+{
+    uint32_t tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlbn];
+    int r = tlbncfg & TLBnCFG_N_ENTRY;
+    return r;
+}
+
+static inline int booke206_tlb_ways(CPUState *env, int tlbn)
+{
+    uint32_t tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlbn];
+    int r = tlbncfg >> TLBnCFG_ASSOC_SHIFT;
+    return r;
+}
+
+static inline int booke206_tlbe_to_tlbn(CPUState *env, ppcemb_tlb_t *tlbe)
+{
+    int id = booke206_tlbe_id(env, tlbe);
+    int end = 0;
+    int i;
+
+    for (i = 0; i < BOOKE206_MAX_TLBN; i++) {
+        end += booke206_tlb_size(env, i);
+        if (id < end) {
+            return i;
+        }
+    }
+
+    cpu_abort(env, "Unknown TLBe: %d\n", id);
+    return 0;
+}
+
+static inline int booke206_tlbe_to_way(CPUState *env, ppcemb_tlb_t *tlb)
+{
+    int tlbn = booke206_tlbe_to_tlbn(env, tlb);
+    int tlbid = booke206_tlbe_id(env, tlb);
+    return tlbid & (booke206_tlb_ways(env, tlbn) - 1);
+}
+
+static inline ppcemb_tlb_t *booke206_get_tlbe(CPUState *env, const int tlbn,
+                                              target_ulong ea, int way)
+{
+    int r;
+    uint32_t ways = booke206_tlb_ways(env, tlbn);
+    int ways_bits = ffs(ways) - 1;
+    int tlb_bits = ffs(booke206_tlb_size(env, tlbn)) - 1;
+    int i;
+
+    way &= ways - 1;
+    ea >>= MAS2_EPN_SHIFT;
+    ea &= (1 << (tlb_bits - ways_bits)) - 1;
+    r = (ea << ways_bits) | way;
+
+    /* bump up to tlbn index */
+    for (i = 0; i < tlbn; i++) {
+        r += booke206_tlb_size(env, i);
+    }
+
+    return &env->tlb[r].tlbe;
+}
+
+#endif
+
 extern void (*cpu_ppc_hypercall)(CPUState *);
 
 #endif /* !defined (__CPU_PPC_H__) */
diff --git a/target-ppc/helper.c b/target-ppc/helper.c
index 5e4030b..2632376 100644
--- a/target-ppc/helper.c
+++ b/target-ppc/helper.c
@@ -993,10 +993,10 @@  static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
 }
 
 /* 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)
+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;
 
@@ -1006,8 +1006,8 @@  static inline int ppcemb_tlb_check(CPUState *env, ppcemb_tlb_t *tlb,
     }
     mask = ~(tlb->size - 1);
     LOG_SWTLB("%s: TLB %d address " TARGET_FMT_lx " PID %u <=> " TARGET_FMT_lx
-              " " TARGET_FMT_lx " %u\n", __func__, i, address, pid, tlb->EPN,
-              mask, (uint32_t)tlb->PID);
+              " " TARGET_FMT_lx " %u %x\n", __func__, i, address, pid, tlb->EPN,
+              mask, (uint32_t)tlb->PID, tlb->prot);
     /* Check PID */
     if (tlb->PID != 0 && tlb->PID != pid)
         return -1;
@@ -1153,48 +1153,170 @@  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->spr[SPR_BOOKE_PID1] &&
+        ppcemb_tlb_check(env, tlb, raddr, address,
+                         env->spr[SPR_BOOKE_PID1], 0, i) >= 0) {
+        goto found_tlb;
+    }
+
+    if (env->spr[SPR_BOOKE_PID2] &&
+        ppcemb_tlb_check(env, tlb, raddr, address,
+                         env->spr[SPR_BOOKE_PID2], 0, i) >= 0) {
+        goto found_tlb;
+    }
+
+    LOG_SWTLB("%s: TLB entry not found\n", __func__);
+    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)) {
+            LOG_SWTLB("%s: AS doesn't match\n", __func__);
+            return -1;
+        }
+
+        *prot = _prot;
+        if (_prot & PAGE_EXEC) {
+            LOG_SWTLB("%s: good TLB!\n", __func__);
+            return 0;
+        }
+
+        LOG_SWTLB("%s: no PAGE_EXEC: %x\n", __func__, _prot);
+        ret = -3;
+    } else {
+        if (msr_dr != (tlb->attr & 1)) {
+            LOG_SWTLB("%s: AS doesn't match\n", __func__);
+            return -1;
+        }
+
+        *prot = _prot;
+        if ((!rw && _prot & PAGE_READ) || (rw && (_prot & PAGE_WRITE))) {
+            LOG_SWTLB("%s: found TLB!\n", __func__);
+            return 0;
+        }
+
+        LOG_SWTLB("%s: PAGE_READ/WRITE doesn't match: %x\n", __func__, _prot);
+        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 = 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 inline void booke206_flush_tlb_one(ppc_tlb_t *tlb, int num)
+{
+    int i;
+
+    for (i = 0; i < num; i++) {
+        if ((tlb[i].tlbe.prot & PAGE_VALID) &&
+           !(tlb[i].tlbe.attr & MAS1_IPROT)) {
+            tlb[i].tlbe.prot = 0;
+        }
+    }
+}
+
+void booke206_flush_tlb(CPUState *env, int flags)
+{
+    int start = 0;
+    int i;
+
+    for (i = 0; i < BOOKE206_MAX_TLBN; i++) {
+        if (flags & (1 << i)) {
+            booke206_flush_tlb_one(&env->tlb[start], booke206_tlb_size(env, i));
+        }
+        start += booke206_tlb_size(env, i);
+    }
+
+    tlb_flush(env, 1);
+}
+
+static int mmubooke206_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, j, ret;
+
+    ret = -1;
+    raddr = (target_phys_addr_t)-1ULL;
+
+    for (i = 0; i < BOOKE206_MAX_TLBN; i++) {
+        int ways = booke206_tlb_ways(env, i);
+
+        for (j = 0; j < ways; j++) {
+            tlb = booke206_get_tlbe(env, i, address, j);
+            ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, rw,
+                                     access_type, j);
+            if (ret != -1) {
+                goto found_tlb;
             }
-            ret = -2;
         }
     }
-    if (ret >= 0)
+
+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;
 }
@@ -1254,9 +1376,8 @@  static inline int check_physical(CPUState *env, mmu_ctx_t *ctx,
         /* 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");
+    case POWERPC_MMU_BOOKE206:
+        cpu_abort(env, "BookE 2.06 MMU doesn't have physical real mode\n");
         break;
     default:
         cpu_abort(env, "Unknown or invalid MMU model\n");
@@ -1281,6 +1402,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_BOOKE206) {
+            ret = mmubooke206_get_physical_address(env, ctx, eaddr, rw,
+                                               access_type);
         } else {
             /* No address translation.  */
             ret = check_physical(env, ctx, eaddr, rw);
@@ -1314,14 +1438,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_BOOKE206:
+            ret = mmubooke206_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 +1472,46 @@  target_phys_addr_t cpu_get_phys_page_debug (CPUState *env, target_ulong addr)
     return ctx.raddr & TARGET_PAGE_MASK;
 }
 
+static void booke206_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_MAS3] = 0;
+    env->spr[SPR_BOOKE_MAS6] = 0;
+    env->spr[SPR_BOOKE_MAS7] = 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 &= booke206_tlb_ways(env, 0) - 1;
+    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 +1567,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_BOOKE206:
+                    booke206_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 +1595,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_BOOKE206)) {
                     env->spr[SPR_BOOKE_ESR] = 0x00000000;
                 }
                 env->exception_index = POWERPC_EXCP_ISI;
@@ -1522,16 +1686,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_BOOKE206:
+                    booke206_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 +1714,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_BOOKE206)) {
                     env->spr[SPR_BOOKE_DEAR] = address;
                     env->spr[SPR_BOOKE_ESR] = rw ? 1 << ESR_ST : 0;
                 } else {
@@ -1822,10 +1986,8 @@  void ppc_tlb_invalidate_all (CPUPPCState *env)
     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");
+    case POWERPC_MMU_BOOKE206:
+        booke206_flush_tlb(env, -1);
         break;
     case POWERPC_MMU_32B:
     case POWERPC_MMU_601:
@@ -1869,9 +2031,9 @@  void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr)
         /* XXX: TODO */
         cpu_abort(env, "BookE MMU model is not implemented\n");
         break;
-    case POWERPC_MMU_BOOKE_FSL:
+    case POWERPC_MMU_BOOKE206:
         /* XXX: TODO */
-        cpu_abort(env, "BookE FSL MMU model is not implemented\n");
+        cpu_abort(env, "BookE 2.06 MMU model is not implemented\n");
         break;
     case POWERPC_MMU_32B:
     case POWERPC_MMU_601:
@@ -2589,7 +2751,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_BOOKE206)) {
         /* 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..a528128 100644
--- a/target-ppc/helper.h
+++ b/target-ppc/helper.h
@@ -334,6 +334,12 @@  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(booke206_tlbre, void)
+DEF_HELPER_0(booke206_tlbwe, void)
+DEF_HELPER_2(booke206_tlbsx, void, tl, tl)
+DEF_HELPER_1(booke206_tlbivax, void, tl)
+DEF_HELPER_1(booke206_tlbflush, void, i32)
+DEF_HELPER_2(booke_setpid, void, i32, tl)
 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..b34364a 100644
--- a/target-ppc/op_helper.c
+++ b/target-ppc/op_helper.c
@@ -4206,4 +4206,300 @@  target_ulong helper_440_tlbsx (target_ulong address)
     return ppcemb_tlb_search(env, address, env->spr[SPR_440_MMUCR] & 0xFF);
 }
 
+/* PowerPC BookE 2.06 TLB management */
+
+static ppcemb_tlb_t *booke206_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);
+    int tlb;
+
+    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");
+    }
+
+    return booke206_get_tlbe(env, tlb, ea, esel);
+}
+
+static inline target_phys_addr_t booke206_tlb_to_page_size(int size)
+{
+    return (1 << (size << 1)) << 10;
+}
+
+static inline target_phys_addr_t booke206_page_size_to_tlb(uint64_t size)
+{
+    return (ffs(size >> 10) - 1) >> 1;
+}
+
+void helper_booke_setpid(uint32_t pidn, target_ulong pid)
+{
+    env->spr[pidn] = pid;
+    /* changing PIDs mean we're in a different address space now */
+    tlb_flush(env, 1);
+}
+
+void helper_booke206_tlbwe(void)
+{
+    uint32_t tlbncfg, tlbn;
+    ppcemb_tlb_t *tlb;
+    target_phys_addr_t rpn;
+    int tlbe_size;
+
+    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;
+    }
+
+    tlbn = (env->spr[SPR_BOOKE_MAS0] & MAS0_TLBSEL_MASK) >> MAS0_TLBSEL_SHIFT;
+    tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlbn];
+
+    tlb = booke206_cur_tlb(env);
+
+    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;
+    if (tlbncfg & TLBnCFG_AVAIL) {
+        tlbe_size = (env->spr[SPR_BOOKE_MAS1] & MAS1_TSIZE_MASK)
+                    >> MAS1_TSIZE_SHIFT;
+    } else {
+        tlbe_size = (tlbncfg & TLBnCFG_MINSIZE) >> TLBnCFG_MINSIZE_SHIFT;
+    }
+
+    tlb->size = booke206_tlb_to_page_size(tlbe_size);
+    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;
+
+    if (tlbncfg & TLBnCFG_IPROT) {
+        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 booke206_tlb_to_mas(CPUState *env, ppcemb_tlb_t *tlb)
+{
+    int tlbn = booke206_tlbe_to_tlbn(env, tlb);
+    int way = booke206_tlbe_to_way(env, tlb);
+
+    env->spr[SPR_BOOKE_MAS0] = tlbn << MAS0_TLBSEL_SHIFT;
+    env->spr[SPR_BOOKE_MAS0] |= way << MAS0_ESEL_SHIFT;
+
+    env->spr[SPR_BOOKE_MAS1] = MAS1_VALID;
+    env->spr[SPR_BOOKE_MAS2] = 0;
+
+    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] |= booke206_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_booke206_tlbre(void)
+{
+    ppcemb_tlb_t *tlb = NULL;
+
+    tlb = booke206_cur_tlb(env);
+    booke206_tlb_to_mas(env, tlb);
+}
+
+void helper_booke206_tlbsx(target_ulong address_hi, target_ulong address_lo)
+{
+    ppcemb_tlb_t *tlb = NULL;
+    int i, j;
+    target_phys_addr_t raddr;
+    uint32_t spid, sas;
+
+    spid = (env->spr[SPR_BOOKE_MAS6] & MAS6_SPID_MASK) >> MAS6_SPID_SHIFT;
+    sas = env->spr[SPR_BOOKE_MAS6] & MAS6_SAS;
+
+    for (i = 0; i < BOOKE206_MAX_TLBN; i++) {
+        int ways = booke206_tlb_ways(env, i);
+
+        for (j = 0; j < ways; j++) {
+            tlb = booke206_get_tlbe(env, i, address_lo, j);
+
+            if (ppcemb_tlb_check(env, tlb, &raddr, address_lo, spid, 0, j)) {
+                continue;
+            }
+
+            if (sas != (tlb->attr & MAS6_SAS)) {
+                continue;
+            }
+
+            booke206_tlb_to_mas(env, tlb);
+            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;
+    env->spr[SPR_BOOKE_MAS3] = 0;
+    env->spr[SPR_BOOKE_MAS7] = 0;
+
+    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 &= booke206_tlb_ways(env, 0) - 1;
+    env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT;
+}
+
+static inline void booke206_invalidate_ea_tlb(CPUState *env, int tlbn,
+                                              uint32_t ea)
+{
+    int i;
+    int ways = booke206_tlb_ways(env, tlbn);
+
+    for (i = 0; i < ways; i++) {
+        ppcemb_tlb_t *tlb = booke206_get_tlbe(env, tlbn, ea, i);
+        target_phys_addr_t masked_ea = ea & ~(tlb->size - 1);
+        if ((tlb->EPN == (masked_ea >> MAS2_EPN_SHIFT)) &&
+            !(tlb->attr & MAS1_IPROT)) {
+            tlb->prot = 0;
+        }
+    }
+}
+
+void helper_booke206_tlbivax(target_ulong address)
+{
+    if (address & 0x4) {
+        /* flush all entries */
+        if (address & 0x8) {
+            /* flush all of TLB1 */
+            booke206_flush_tlb(env, BOOKE206_FLUSH_TLB1);
+        } else {
+            /* flush all of TLB0 */
+            booke206_flush_tlb(env, BOOKE206_FLUSH_TLB0);
+        }
+        return;
+    }
+
+    if (address & 0x8) {
+        /* flush TLB1 entries */
+        booke206_invalidate_ea_tlb(env, 1, address);
+        tlb_flush(env, 1);
+    } else {
+        /* flush TLB0 entries */
+        booke206_invalidate_ea_tlb(env, 0, address);
+        tlb_flush_page(env, address & MAS2_EPN_MASK);
+    }
+}
+
+void helper_booke206_tlbflush(uint32_t type)
+{
+    int flags = 0;
+
+    if (type & 2) {
+        flags |= BOOKE206_FLUSH_TLB1;
+    }
+
+    if (type & 4) {
+        flags |= BOOKE206_FLUSH_TLB0;
+    }
+
+    booke206_flush_tlb(env, flags);
+}
+
 #endif /* !CONFIG_USER_ONLY */
diff --git a/target-ppc/translate.c b/target-ppc/translate.c
index 95813f2..b6bbc63 100644
--- a/target-ppc/translate.c
+++ b/target-ppc/translate.c
@@ -5988,6 +5988,78 @@  static void gen_tlbwe_440(DisasContext *ctx)
 #endif
 }
 
+/* TLB management - PowerPC BookE 2.06 implementation */
+
+/* tlbre */
+static void gen_tlbre_booke206(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_booke206_tlbre();
+#endif
+}
+
+/* tlbsx - tlbsx. */
+static void gen_tlbsx_booke206(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_booke206_tlbsx(t0, cpu_gpr[rB(ctx->opcode)]);
+#endif
+}
+
+/* tlbwe */
+static void gen_tlbwe_booke206(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_booke206_tlbwe();
+#endif
+}
+
+static void gen_tlbivax_booke206(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_booke206_tlbivax(t0);
+#endif
+}
+
+
 /* wrtee */
 static void gen_wrtee(DisasContext *ctx)
 {
@@ -8434,7 +8506,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_BOOKE206),
 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),
@@ -8443,12 +8515,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_booke206, "tlbre", 0x1F, 0x12, 0x1D, 0x00000001,
+               PPC_NONE, PPC2_BOOKE206),
+GEN_HANDLER2_E(tlbsx_booke206, "tlbsx", 0x1F, 0x12, 0x1C, 0x00000000,
+               PPC_NONE, PPC2_BOOKE206),
+GEN_HANDLER2_E(tlbwe_booke206, "tlbwe", 0x1F, 0x12, 0x1E, 0x00000001,
+               PPC_NONE, PPC2_BOOKE206),
+GEN_HANDLER2_E(tlbivax_booke206, "tlbivax", 0x1F, 0x12, 0x18, 0x00000001,
+               PPC_NONE, PPC2_BOOKE206),
 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_BOOKE206),
+GEN_HANDLER_E(msync, 0x1F, 0x16, 0x12, 0x03FFF801,
+              PPC_BOOKE, PPC2_BOOKE206),
+GEN_HANDLER2_E(icbt_440, "icbt", 0x1F, 0x16, 0x00, 0x03E00001,
+               PPC_BOOKE, PPC2_BOOKE206),
 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..40092b3 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_booke206_mmucsr0 (void *opaque, int sprn, int gprn)
+{
+    TCGv t0 = tcg_const_i32(sprn);
+    gen_helper_booke206_tlbflush(t0);
+    tcg_temp_free(t0);
+}
+
+static void spr_write_booke_pid (void *opaque, int sprn, int gprn)
+{
+    TCGv t0 = tcg_const_i32(sprn);
+    gen_helper_booke_setpid(t0, cpu_gpr[gprn]);
+    tcg_temp_free(t0);
+}
+#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);
 }
 
-/* FSL storage control registers */
-static void gen_spr_BookE_FSL (CPUPPCState *env, uint32_t mas_mask)
+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;
+}
+
+/* BookE 2.06 storage control registers */
+static void gen_spr_BookE206(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 */
@@ -4113,7 +4142,7 @@  static void init_proc_G2LE (CPUPPCState *env)
                               PPC_BOOKE)
 #define POWERPC_INSNS2_e200  (PPC_NONE)
 #define POWERPC_MSRM_e200    (0x000000000606FF30ULL)
-#define POWERPC_MMU_e200     (POWERPC_MMU_BOOKE_FSL)
+#define POWERPC_MMU_e200     (POWERPC_MMU_BOOKE206)
 #define POWERPC_EXCP_e200    (POWERPC_EXCP_BOOKE)
 #define POWERPC_INPUT_e200   (PPC_FLAGS_INPUT_BookE)
 #define POWERPC_BFDM_e200    (bfd_mach_ppc_860)
@@ -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_BookE206(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,11 +4316,10 @@  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_BOOKE206)
 #define POWERPC_MSRM_e500v1    (0x000000000606FF30ULL)
-#define POWERPC_MMU_e500v1     (POWERPC_MMU_BOOKE_FSL)
+#define POWERPC_MMU_e500v1     (POWERPC_MMU_BOOKE206)
 #define POWERPC_EXCP_e500v1    (POWERPC_EXCP_BOOKE)
 #define POWERPC_INPUT_e500v1   (PPC_FLAGS_INPUT_BookE)
 #define POWERPC_BFDM_e500v1    (bfd_mach_ppc_860)
@@ -4294,7 +4327,7 @@  static void init_proc_e300 (CPUPPCState *env)
                                 POWERPC_FLAG_UBLE | POWERPC_FLAG_DE |   \
                                 POWERPC_FLAG_BUS_CLK)
 #define check_pow_e500v1       check_pow_hid0
-#define init_proc_e500v1       init_proc_e500
+#define init_proc_e500v1       init_proc_e500v1
 
 /* e500v2 core                                                               */
 #define POWERPC_INSNS_e500v2   (PPC_INSNS_BASE | PPC_ISEL |             \
@@ -4302,11 +4335,10 @@  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_BOOKE206)
 #define POWERPC_MSRM_e500v2    (0x000000000606FF30ULL)
-#define POWERPC_MMU_e500v2     (POWERPC_MMU_BOOKE_FSL)
+#define POWERPC_MMU_e500v2     (POWERPC_MMU_BOOKE206)
 #define POWERPC_EXCP_e500v2    (POWERPC_EXCP_BOOKE)
 #define POWERPC_INPUT_e500v2   (PPC_FLAGS_INPUT_BookE)
 #define POWERPC_BFDM_e500v2    (bfd_mach_ppc_860)
@@ -4314,13 +4346,23 @@  static void init_proc_e300 (CPUPPCState *env)
                                 POWERPC_FLAG_UBLE | POWERPC_FLAG_DE |   \
                                 POWERPC_FLAG_BUS_CLK)
 #define check_pow_e500v2       check_pow_hid0
-#define init_proc_e500v2       init_proc_e500
+#define init_proc_e500v2       init_proc_e500v2
 
-static void init_proc_e500 (CPUPPCState *env)
+static void init_proc_e500 (CPUPPCState *env, int version)
 {
+    uint32_t tlbncfg[2];
+#if !defined(CONFIG_USER_ONLY)
+    int i;
+#endif
+
     /* 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 +4376,24 @@  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;
+    switch (version) {
+    case 1:
+        /* e500v1 */
+        tlbncfg[0] = gen_tlbncfg(2, 1, 1, 0, 256);
+        tlbncfg[1] = gen_tlbncfg(16, 1, 9, TLBnCFG_AVAIL | TLBnCFG_IPROT, 16);
+        break;
+    case 2:
+        /* e500v2 */
+        tlbncfg[0] = gen_tlbncfg(4, 1, 1, 0, 512);
+        tlbncfg[1] = gen_tlbncfg(16, 1, 12, TLBnCFG_AVAIL | TLBnCFG_IPROT, 16);
+        break;
+    default:
+        cpu_abort(env, "Unknown CPU: " TARGET_FMT_lx "\n", env->spr[SPR_PVR]);
+    }
 #endif
-    gen_spr_BookE_FSL(env, 0x0000005F);
+    gen_spr_BookE206(env, 0x000000DF, tlbncfg);
     /* XXX : not implemented */
     spr_register(env, SPR_HID0, "HID0",
                  SPR_NOACCESS, SPR_NOACCESS,
@@ -4384,23 +4442,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 +4457,18 @@  static void init_proc_e500 (CPUPPCState *env)
                  SPR_NOACCESS, SPR_NOACCESS,
                  &spr_read_generic, &spr_write_generic,
                  0x00000000);
+    spr_register(env, SPR_MMUCSR0, "MMUCSR0",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_booke206_mmucsr0,
+                 0x00000000);
+
 #if !defined(CONFIG_USER_ONLY)
-    env->nb_tlb = 64;
-    env->nb_ways = 1;
-    env->id_tlbs = 0;
+    env->nb_tlb = 0;
+    for (i = 0; i < BOOKE206_MAX_TLBN; i++) {
+        env->nb_tlb += booke206_tlb_size(env, i);
+    }
 #endif
+
     init_excp_e200(env);
     env->dcache_line_size = 32;
     env->icache_line_size = 32;
@@ -4421,6 +4476,16 @@  static void init_proc_e500 (CPUPPCState *env)
     ppce500_irq_init(env);
 }
 
+static void init_proc_e500v1(CPUPPCState *env)
+{
+    init_proc_e500(env, 1);
+}
+
+static void init_proc_e500v2(CPUPPCState *env)
+{
+    init_proc_e500(env, 2);
+}
+
 /* Non-embedded PowerPC                                                      */
 
 /* POWER : same as 601, without mfmsr, mfsr                                  */
@@ -9756,8 +9821,8 @@  int cpu_ppc_register_internal (CPUPPCState *env, const ppc_def_t *def)
         case POWERPC_MMU_BOOKE:
             mmu_model = "PowerPC BookE";
             break;
-        case POWERPC_MMU_BOOKE_FSL:
-            mmu_model = "PowerPC BookE FSL";
+        case POWERPC_MMU_BOOKE206:
+            mmu_model = "PowerPC BookE 2.06";
             break;
         case POWERPC_MMU_601:
             mmu_model = "PowerPC 601";