summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--gdbstub.c3
-rw-r--r--hw/ppc/spapr.c16
-rw-r--r--hw/ppc/spapr_hcall.c102
-rw-r--r--hw/ppc/xics.c57
-rw-r--r--hw/spapr_pci.c30
-rw-r--r--hw/spapr_pci.h4
-rw-r--r--hw/xics.h3
-rw-r--r--monitor.c4
-rw-r--r--target-ppc/Makefile.objs7
-rw-r--r--target-ppc/cpu-models.c2
-rw-r--r--target-ppc/cpu-qom.h4
-rw-r--r--target-ppc/cpu.h91
-rw-r--r--target-ppc/fpu_helper.c5
-rw-r--r--target-ppc/helper.h1
-rw-r--r--target-ppc/kvm.c3
-rw-r--r--target-ppc/machine.c4
-rw-r--r--target-ppc/mem_helper.c38
-rw-r--r--target-ppc/misc_helper.c6
-rw-r--r--target-ppc/mmu-hash32.c560
-rw-r--r--target-ppc/mmu-hash32.h102
-rw-r--r--target-ppc/mmu-hash64.c546
-rw-r--r--target-ppc/mmu-hash64.h124
-rw-r--r--target-ppc/mmu_helper.c835
-rw-r--r--target-ppc/translate.c226
-rw-r--r--target-ppc/translate_init.c360
-rw-r--r--target-ppc/user_only_helper.c44
26 files changed, 1873 insertions, 1304 deletions
diff --git a/gdbstub.c b/gdbstub.c
index decb505600..a666cb5bb0 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -781,7 +781,8 @@ static int cpu_gdb_write_register(CPUPPCState *env, uint8_t *mem_buf, int n)
             /* fpscr */
             if (gdb_has_xml)
                 return 0;
-            return 4;
+            store_fpscr(env, ldtul_p(mem_buf), 0xffffffff);
+            return sizeof(target_ulong);
         }
     }
     return 0;
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index f355a9bb84..7b2a11fbe4 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -629,7 +629,7 @@ static void ppc_spapr_reset(void)
                        spapr->rtas_size);
 
     /* Set up the entry state */
-    first_cpu_cpu = CPU(first_cpu);
+    first_cpu_cpu = ENV_GET_CPU(first_cpu);
     first_cpu->gpr[3] = spapr->fdt_addr;
     first_cpu->gpr[5] = 0;
     first_cpu_cpu->halted = 0;
@@ -779,6 +779,11 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
         spapr->htab_shift++;
     }
 
+    /* Set up Interrupt Controller before we create the VCPUs */
+    spapr->icp = xics_system_init(smp_cpus * kvmppc_smt_threads() / smp_threads,
+                                  XICS_IRQS);
+    spapr->next_irq = XICS_IRQ_BASE;
+
     /* init CPUs */
     if (cpu_model == NULL) {
         cpu_model = kvm_enabled() ? "host" : "POWER7";
@@ -791,6 +796,8 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
         }
         env = &cpu->env;
 
+        xics_cpu_setup(spapr->icp, cpu);
+
         /* Set time-base frequency to 512 MHz */
         cpu_ppc_tb_init(env, TIMEBASE_FREQ);
 
@@ -830,11 +837,6 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
     }
     g_free(filename);
 
-
-    /* Set up Interrupt Controller */
-    spapr->icp = xics_system_init(XICS_IRQS);
-    spapr->next_irq = XICS_IRQ_BASE;
-
     /* Set up EPOW events infrastructure */
     spapr_events_init(spapr);
 
@@ -856,7 +858,7 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
     /* Set up PCI */
     spapr_pci_rtas_init();
 
-    phb = spapr_create_phb(spapr, 0, "pci");
+    phb = spapr_create_phb(spapr, 0);
 
     for (i = 0; i < nb_nics; i++) {
         NICInfo *nd = &nd_table[i];
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index dd72743b52..22cfb7e674 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -3,39 +3,7 @@
 #include "sysemu/sysemu.h"
 #include "helper_regs.h"
 #include "hw/spapr.h"
-
-#define HPTES_PER_GROUP 8
-
-#define HPTE_V_SSIZE_SHIFT      62
-#define HPTE_V_AVPN_SHIFT       7
-#define HPTE_V_AVPN             0x3fffffffffffff80ULL
-#define HPTE_V_AVPN_VAL(x)      (((x) & HPTE_V_AVPN) >> HPTE_V_AVPN_SHIFT)
-#define HPTE_V_COMPARE(x, y)    (!(((x) ^ (y)) & 0xffffffffffffff80UL))
-#define HPTE_V_BOLTED           0x0000000000000010ULL
-#define HPTE_V_LOCK             0x0000000000000008ULL
-#define HPTE_V_LARGE            0x0000000000000004ULL
-#define HPTE_V_SECONDARY        0x0000000000000002ULL
-#define HPTE_V_VALID            0x0000000000000001ULL
-
-#define HPTE_R_PP0              0x8000000000000000ULL
-#define HPTE_R_TS               0x4000000000000000ULL
-#define HPTE_R_KEY_HI           0x3000000000000000ULL
-#define HPTE_R_RPN_SHIFT        12
-#define HPTE_R_RPN              0x3ffffffffffff000ULL
-#define HPTE_R_FLAGS            0x00000000000003ffULL
-#define HPTE_R_PP               0x0000000000000003ULL
-#define HPTE_R_N                0x0000000000000004ULL
-#define HPTE_R_G                0x0000000000000008ULL
-#define HPTE_R_M                0x0000000000000010ULL
-#define HPTE_R_I                0x0000000000000020ULL
-#define HPTE_R_W                0x0000000000000040ULL
-#define HPTE_R_WIMG             0x0000000000000078ULL
-#define HPTE_R_C                0x0000000000000080ULL
-#define HPTE_R_R                0x0000000000000100ULL
-#define HPTE_R_KEY_LO           0x0000000000000e00ULL
-
-#define HPTE_V_1TB_SEG          0x4000000000000000ULL
-#define HPTE_V_VRMA_MASK        0x4001ffffff000000ULL
+#include "mmu-hash64.h"
 
 static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r,
                                      target_ulong pte_index)
@@ -44,17 +12,17 @@ static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r,
 
     rb = (v & ~0x7fULL) << 16; /* AVA field */
     va_low = pte_index >> 3;
-    if (v & HPTE_V_SECONDARY) {
+    if (v & HPTE64_V_SECONDARY) {
         va_low = ~va_low;
     }
     /* xor vsid from AVA */
-    if (!(v & HPTE_V_1TB_SEG)) {
+    if (!(v & HPTE64_V_1TB_SEG)) {
         va_low ^= v >> 12;
     } else {
         va_low ^= v >> 24;
     }
     va_low &= 0x7ff;
-    if (v & HPTE_V_LARGE) {
+    if (v & HPTE64_V_LARGE) {
         rb |= 1;                         /* L field */
 #if 0 /* Disable that P7 specific bit for now */
         if (r & 0xff000) {
@@ -84,10 +52,10 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr,
     target_ulong page_shift = 12;
     target_ulong raddr;
     target_ulong i;
-    uint8_t *hpte;
+    hwaddr hpte;
 
     /* only handle 4k and 16M pages for now */
-    if (pteh & HPTE_V_LARGE) {
+    if (pteh & HPTE64_V_LARGE) {
 #if 0 /* We don't support 64k pages yet */
         if ((ptel & 0xf000) == 0x1000) {
             /* 64k page */
@@ -105,11 +73,11 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr,
         }
     }
 
-    raddr = (ptel & HPTE_R_RPN) & ~((1ULL << page_shift) - 1);
+    raddr = (ptel & HPTE64_R_RPN) & ~((1ULL << page_shift) - 1);
 
     if (raddr < spapr->ram_limit) {
         /* Regular RAM - should have WIMG=0010 */
-        if ((ptel & HPTE_R_WIMG) != HPTE_R_M) {
+        if ((ptel & HPTE64_R_WIMG) != HPTE64_R_M) {
             return H_PARAMETER;
         }
     } else {
@@ -117,7 +85,7 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr,
         /* FIXME: What WIMG combinations could be sensible for IO?
          * For now we allow WIMG=010x, but are there others? */
         /* FIXME: Should we check against registered IO addresses? */
-        if ((ptel & (HPTE_R_W | HPTE_R_I | HPTE_R_M)) != HPTE_R_I) {
+        if ((ptel & (HPTE64_R_W | HPTE64_R_I | HPTE64_R_M)) != HPTE64_R_I) {
             return H_PARAMETER;
         }
     }
@@ -129,26 +97,26 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr,
     }
     if (likely((flags & H_EXACT) == 0)) {
         pte_index &= ~7ULL;
-        hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64);
+        hpte = pte_index * HASH_PTE_SIZE_64;
         for (i = 0; ; ++i) {
             if (i == 8) {
                 return H_PTEG_FULL;
             }
-            if ((ldq_p(hpte) & HPTE_V_VALID) == 0) {
+            if ((ppc_hash64_load_hpte0(env, hpte) & HPTE64_V_VALID) == 0) {
                 break;
             }
             hpte += HASH_PTE_SIZE_64;
         }
     } else {
         i = 0;
-        hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64);
-        if (ldq_p(hpte) & HPTE_V_VALID) {
+        hpte = pte_index * HASH_PTE_SIZE_64;
+        if (ppc_hash64_load_hpte0(env, hpte) & HPTE64_V_VALID) {
             return H_PTEG_FULL;
         }
     }
-    stq_p(hpte + (HASH_PTE_SIZE_64/2), ptel);
+    ppc_hash64_store_hpte1(env, hpte, ptel);
     /* eieio();  FIXME: need some sort of barrier for smp? */
-    stq_p(hpte, pteh);
+    ppc_hash64_store_hpte0(env, hpte, pteh);
 
     args[0] = pte_index + i;
     return H_SUCCESS;
@@ -166,26 +134,26 @@ static target_ulong remove_hpte(CPUPPCState *env, target_ulong ptex,
                                 target_ulong flags,
                                 target_ulong *vp, target_ulong *rp)
 {
-    uint8_t *hpte;
+    hwaddr hpte;
     target_ulong v, r, rb;
 
     if ((ptex * HASH_PTE_SIZE_64) & ~env->htab_mask) {
         return REMOVE_PARM;
     }
 
-    hpte = env->external_htab + (ptex * HASH_PTE_SIZE_64);
+    hpte = ptex * HASH_PTE_SIZE_64;
 
-    v = ldq_p(hpte);
-    r = ldq_p(hpte + (HASH_PTE_SIZE_64/2));
+    v = ppc_hash64_load_hpte0(env, hpte);
+    r = ppc_hash64_load_hpte1(env, hpte);
 
-    if ((v & HPTE_V_VALID) == 0 ||
+    if ((v & HPTE64_V_VALID) == 0 ||
         ((flags & H_AVPN) && (v & ~0x7fULL) != avpn) ||
         ((flags & H_ANDCOND) && (v & avpn) != 0)) {
         return REMOVE_NOT_FOUND;
     }
     *vp = v;
     *rp = r;
-    stq_p(hpte, 0);
+    ppc_hash64_store_hpte0(env, hpte, 0);
     rb = compute_tlbie_rb(v, r, ptex);
     ppc_tlb_invalidate_one(env, rb);
     return REMOVE_SUCCESS;
@@ -271,7 +239,7 @@ static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPREnvironment *spapr,
 
         switch (ret) {
         case REMOVE_SUCCESS:
-            *tsh |= (r & (HPTE_R_C | HPTE_R_R)) << 43;
+            *tsh |= (r & (HPTE64_R_C | HPTE64_R_R)) << 43;
             break;
 
         case REMOVE_PARM:
@@ -292,34 +260,34 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPREnvironment *spapr,
     target_ulong flags = args[0];
     target_ulong pte_index = args[1];
     target_ulong avpn = args[2];
-    uint8_t *hpte;
+    hwaddr hpte;
     target_ulong v, r, rb;
 
     if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) {
         return H_PARAMETER;
     }
 
-    hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64);
+    hpte = pte_index * HASH_PTE_SIZE_64;
 
-    v = ldq_p(hpte);
-    r = ldq_p(hpte + (HASH_PTE_SIZE_64/2));
+    v = ppc_hash64_load_hpte0(env, hpte);
+    r = ppc_hash64_load_hpte1(env, hpte);
 
-    if ((v & HPTE_V_VALID) == 0 ||
+    if ((v & HPTE64_V_VALID) == 0 ||
         ((flags & H_AVPN) && (v & ~0x7fULL) != avpn)) {
         return H_NOT_FOUND;
     }
 
-    r &= ~(HPTE_R_PP0 | HPTE_R_PP | HPTE_R_N |
-           HPTE_R_KEY_HI | HPTE_R_KEY_LO);
-    r |= (flags << 55) & HPTE_R_PP0;
-    r |= (flags << 48) & HPTE_R_KEY_HI;
-    r |= flags & (HPTE_R_PP | HPTE_R_N | HPTE_R_KEY_LO);
+    r &= ~(HPTE64_R_PP0 | HPTE64_R_PP | HPTE64_R_N |
+           HPTE64_R_KEY_HI | HPTE64_R_KEY_LO);
+    r |= (flags << 55) & HPTE64_R_PP0;
+    r |= (flags << 48) & HPTE64_R_KEY_HI;
+    r |= flags & (HPTE64_R_PP | HPTE64_R_N | HPTE64_R_KEY_LO);
     rb = compute_tlbie_rb(v, r, pte_index);
-    stq_p(hpte, v & ~HPTE_V_VALID);
+    ppc_hash64_store_hpte0(env, hpte, v & ~HPTE64_V_VALID);
     ppc_tlb_invalidate_one(env, rb);
-    stq_p(hpte + (HASH_PTE_SIZE_64/2), r);
+    ppc_hash64_store_hpte1(env, hpte, r);
     /* Don't need a memory barrier, due to qemu's global lock */
-    stq_p(hpte, v);
+    ppc_hash64_store_hpte0(env, hpte, v);
     return H_SUCCESS;
 }
 
diff --git a/hw/ppc/xics.c b/hw/ppc/xics.c
index c3ef12fff4..374da5bbfd 100644
--- a/hw/ppc/xics.c
+++ b/hw/ppc/xics.c
@@ -521,45 +521,38 @@ static void xics_reset(void *opaque)
     }
 }
 
-struct icp_state *xics_system_init(int nr_irqs)
+void xics_cpu_setup(struct icp_state *icp, PowerPCCPU *cpu)
 {
-    CPUPPCState *env;
-    CPUState *cpu;
-    int max_server_num;
-    struct icp_state *icp;
-    struct ics_state *ics;
+    CPUState *cs = CPU(cpu);
+    CPUPPCState *env = &cpu->env;
+    struct icp_server_state *ss = &icp->ss[cs->cpu_index];
 
-    max_server_num = -1;
-    for (env = first_cpu; env != NULL; env = env->next_cpu) {
-        cpu = CPU(ppc_env_get_cpu(env));
-        if (cpu->cpu_index > max_server_num) {
-            max_server_num = cpu->cpu_index;
-        }
-    }
+    assert(cs->cpu_index < icp->nr_servers);
 
-    icp = g_malloc0(sizeof(*icp));
-    icp->nr_servers = max_server_num + 1;
-    icp->ss = g_malloc0(icp->nr_servers*sizeof(struct icp_server_state));
+    switch (PPC_INPUT(env)) {
+    case PPC_FLAGS_INPUT_POWER7:
+        ss->output = env->irq_inputs[POWER7_INPUT_INT];
+        break;
 
-    for (env = first_cpu; env != NULL; env = env->next_cpu) {
-        cpu = CPU(ppc_env_get_cpu(env));
-        struct icp_server_state *ss = &icp->ss[cpu->cpu_index];
+    case PPC_FLAGS_INPUT_970:
+        ss->output = env->irq_inputs[PPC970_INPUT_INT];
+        break;
 
-        switch (PPC_INPUT(env)) {
-        case PPC_FLAGS_INPUT_POWER7:
-            ss->output = env->irq_inputs[POWER7_INPUT_INT];
-            break;
+    default:
+        fprintf(stderr, "XICS interrupt controller does not support this CPU "
+                "bus model\n");
+        abort();
+    }
+}
 
-        case PPC_FLAGS_INPUT_970:
-            ss->output = env->irq_inputs[PPC970_INPUT_INT];
-            break;
+struct icp_state *xics_system_init(int nr_servers, int nr_irqs)
+{
+    struct icp_state *icp;
+    struct ics_state *ics;
 
-        default:
-            hw_error("XICS interrupt model does not support this CPU bus "
-                     "model\n");
-            exit(1);
-        }
-    }
+    icp = g_malloc0(sizeof(*icp));
+    icp->nr_servers = nr_servers;
+    icp->ss = g_malloc0(icp->nr_servers*sizeof(struct icp_server_state));
 
     ics = g_malloc0(sizeof(*ics));
     ics->nr_irqs = nr_irqs;
diff --git a/hw/spapr_pci.c b/hw/spapr_pci.c
index 36adbc5592..42c8b61c74 100644
--- a/hw/spapr_pci.c
+++ b/hw/spapr_pci.c
@@ -518,6 +518,7 @@ static int spapr_phb_init(SysBusDevice *s)
 {
     sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(s);
     PCIHostState *phb = PCI_HOST_BRIDGE(s);
+    const char *busname;
     char *namebuf;
     int i;
     PCIBus *bus;
@@ -575,9 +576,6 @@ static int spapr_phb_init(SysBusDevice *s)
     }
 
     sphb->dtbusname = g_strdup_printf("pci@%" PRIx64, sphb->buid);
-    if (!sphb->busname) {
-        sphb->busname = sphb->dtbusname;
-    }
 
     namebuf = alloca(strlen(sphb->dtbusname) + 32);
 
@@ -621,7 +619,26 @@ static int spapr_phb_init(SysBusDevice *s)
                                     &sphb->msiwindow);
     }
 
-    bus = pci_register_bus(DEVICE(s), sphb->busname,
+    /*
+     * Selecting a busname is more complex than you'd think, due to
+     * interacting constraints.  If the user has specified an id
+     * explicitly for the phb , then we want to use the qdev default
+     * of naming the bus based on the bridge device (so the user can
+     * then assign devices to it in the way they expect).  For the
+     * first / default PCI bus (index=0) we want to use just "pci"
+     * because libvirt expects there to be a bus called, simply,
+     * "pci".  Otherwise, we use the same name as in the device tree,
+     * since it's unique by construction, and makes the guest visible
+     * BUID clear.
+     */
+    if (s->qdev.id) {
+        busname = NULL;
+    } else if (sphb->index == 0) {
+        busname = "pci";
+    } else {
+        busname = sphb->dtbusname;
+    }
+    bus = pci_register_bus(DEVICE(s), busname,
                            pci_spapr_set_irq, pci_spapr_map_irq, sphb,
                            &sphb->memspace, &sphb->iospace,
                            PCI_DEVFN(0, 0), PCI_NUM_PINS);
@@ -663,7 +680,6 @@ static void spapr_phb_reset(DeviceState *qdev)
 }
 
 static Property spapr_phb_properties[] = {
-    DEFINE_PROP_STRING("busname", sPAPRPHBState, busname),
     DEFINE_PROP_INT32("index", sPAPRPHBState, index, -1),
     DEFINE_PROP_HEX64("buid", sPAPRPHBState, buid, -1),
     DEFINE_PROP_HEX32("liobn", sPAPRPHBState, dma_liobn, -1),
@@ -694,14 +710,12 @@ static const TypeInfo spapr_phb_info = {
     .class_init    = spapr_phb_class_init,
 };
 
-PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index,
-                               const char *busname)
+PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index)
 {
     DeviceState *dev;
 
     dev = qdev_create(NULL, TYPE_SPAPR_PCI_HOST_BRIDGE);
     qdev_prop_set_uint32(dev, "index", index);
-    qdev_prop_set_string(dev, "busname", busname);
     qdev_init_nofail(dev);
 
     return PCI_HOST_BRIDGE(dev);
diff --git a/hw/spapr_pci.h b/hw/spapr_pci.h
index 8bb3c62c3d..8bd8a663c5 100644
--- a/hw/spapr_pci.h
+++ b/hw/spapr_pci.h
@@ -39,7 +39,6 @@ typedef struct sPAPRPHBState {
 
     int32_t index;
     uint64_t buid;
-    char *busname;
     char *dtbusname;
 
     MemoryRegion memspace, iospace;
@@ -82,8 +81,7 @@ static inline qemu_irq spapr_phb_lsi_qirq(struct sPAPRPHBState *phb, int pin)
     return xics_get_qirq(spapr->icp, phb->lsi_table[pin].irq);
 }
 
-PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index,
-                               const char *busname);
+PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index);
 
 int spapr_populate_pci_dt(sPAPRPHBState *phb,
                           uint32_t xics_phandle,
diff --git a/hw/xics.h b/hw/xics.h
index c3bf0083e2..6bce0424df 100644
--- a/hw/xics.h
+++ b/hw/xics.h
@@ -35,6 +35,7 @@ struct icp_state;
 qemu_irq xics_get_qirq(struct icp_state *icp, int irq);
 void xics_set_irq_type(struct icp_state *icp, int irq, bool lsi);
 
-struct icp_state *xics_system_init(int nr_irqs);
+struct icp_state *xics_system_init(int nr_servers, int nr_irqs);
+void xics_cpu_setup(struct icp_state *icp, PowerPCCPU *cpu);
 
 #endif /* __XICS_H__ */
diff --git a/monitor.c b/monitor.c
index cfb5d64054..2d9e8878eb 100644
--- a/monitor.c
+++ b/monitor.c
@@ -2960,10 +2960,6 @@ static const MonitorDef monitor_defs[] = {
     { "xer", 0, &monitor_get_xer, },
     { "tbu", 0, &monitor_get_tbu, },
     { "tbl", 0, &monitor_get_tbl, },
-#if defined(TARGET_PPC64)
-    /* Address space register */
-    { "asr", offsetof(CPUPPCState, asr) },
-#endif
     /* Segment registers */
     { "sdr1", offsetof(CPUPPCState, spr[SPR_SDR1]) },
     { "sr0", offsetof(CPUPPCState, sr[0]) },
diff --git a/target-ppc/Makefile.objs b/target-ppc/Makefile.objs
index 00ac4adc51..2c43c34a5c 100644
--- a/target-ppc/Makefile.objs
+++ b/target-ppc/Makefile.objs
@@ -1,11 +1,14 @@
 obj-y += cpu-models.o
 obj-y += translate.o
-obj-$(CONFIG_SOFTMMU) += machine.o
+ifeq ($(CONFIG_SOFTMMU),y)
+obj-y += machine.o mmu_helper.o mmu-hash32.o
+obj-$(TARGET_PPC64) += mmu-hash64.o
+endif
 obj-$(CONFIG_KVM) += kvm.o kvm_ppc.o
 obj-y += excp_helper.o
 obj-y += fpu_helper.o
 obj-y += int_helper.o
-obj-y += mmu_helper.o
 obj-y += timebase_helper.o
 obj-y += misc_helper.o
 obj-y += mem_helper.o
+obj-$(CONFIG_USER_ONLY) += user_only_helper.o
diff --git a/target-ppc/cpu-models.c b/target-ppc/cpu-models.c
index 20ca84e614..17f56b7504 100644
--- a/target-ppc/cpu-models.c
+++ b/target-ppc/cpu-models.c
@@ -1101,9 +1101,9 @@
                 "PowerPC 7457A v1.2 (G4)")
     /* 64 bits PowerPC                                                       */
 #if defined (TARGET_PPC64)
+#if defined(TODO)
     POWERPC_DEF("620",           CPU_POWERPC_620,                    620,
                 "PowerPC 620")
-#if defined(TODO)
     POWERPC_DEF("630",           CPU_POWERPC_630,                    630,
                 "PowerPC 630 (POWER3)")
 #endif
diff --git a/target-ppc/cpu-qom.h b/target-ppc/cpu-qom.h
index 09bfae3d54..c27cef7e32 100644
--- a/target-ppc/cpu-qom.h
+++ b/target-ppc/cpu-qom.h
@@ -68,6 +68,10 @@ typedef struct PowerPCCPUClass {
 #endif
     void (*init_proc)(CPUPPCState *env);
     int  (*check_pow)(CPUPPCState *env);
+#if defined(CONFIG_SOFTMMU)
+    int (*handle_mmu_fault)(CPUPPCState *env, target_ulong eaddr, int rwx,
+                            int mmu_idx);
+#endif
 } PowerPCCPUClass;
 
 /**
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index 6886666d6e..42c36e2829 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -113,13 +113,13 @@ enum powerpc_mmu_t {
 #if defined(TARGET_PPC64)
 #define POWERPC_MMU_64       0x00010000
 #define POWERPC_MMU_1TSEG    0x00020000
+#define POWERPC_MMU_AMR      0x00040000
     /* 64 bits PowerPC MMU                                     */
     POWERPC_MMU_64B        = POWERPC_MMU_64 | 0x00000001,
-    /* 620 variant (no segment exceptions)                     */
-    POWERPC_MMU_620        = POWERPC_MMU_64 | 0x00000002,
     /* Architecture 2.06 variant                               */
-    POWERPC_MMU_2_06       = POWERPC_MMU_64 | POWERPC_MMU_1TSEG | 0x00000003,
-    /* Architecture 2.06 "degraded" (no 1T segments)           */
+    POWERPC_MMU_2_06       = POWERPC_MMU_64 | POWERPC_MMU_1TSEG
+                             | POWERPC_MMU_AMR | 0x00000003,
+    /* Architecture 2.06 "degraded" (no 1T segments or AMR)    */
     POWERPC_MMU_2_06d      = POWERPC_MMU_64 | 0x00000003,
 #endif /* defined(TARGET_PPC64) */
 };
@@ -396,36 +396,12 @@ union ppc_tlb_t {
 #define SDR_64_HTABSIZE        0x000000000000001FULL
 #endif /* defined(TARGET_PPC64 */
 
-#define HASH_PTE_SIZE_32       8
-#define HASH_PTE_SIZE_64       16
-
 typedef struct ppc_slb_t ppc_slb_t;
 struct ppc_slb_t {
     uint64_t esid;
     uint64_t vsid;
 };
 
-/* Bits in the SLB ESID word */
-#define SLB_ESID_ESID           0xFFFFFFFFF0000000ULL
-#define SLB_ESID_V              0x0000000008000000ULL /* valid */
-
-/* Bits in the SLB VSID word */
-#define SLB_VSID_SHIFT          12
-#define SLB_VSID_SHIFT_1T       24
-#define SLB_VSID_SSIZE_SHIFT    62
-#define SLB_VSID_B              0xc000000000000000ULL
-#define SLB_VSID_B_256M         0x0000000000000000ULL
-#define SLB_VSID_B_1T           0x4000000000000000ULL
-#define SLB_VSID_VSID           0x3FFFFFFFFFFFF000ULL
-#define SLB_VSID_PTEM           (SLB_VSID_B | SLB_VSID_VSID)
-#define SLB_VSID_KS             0x0000000000000800ULL
-#define SLB_VSID_KP             0x0000000000000400ULL
-#define SLB_VSID_N              0x0000000000000200ULL /* no-execute */
-#define SLB_VSID_L              0x0000000000000100ULL
-#define SLB_VSID_C              0x0000000000000080ULL /* class */
-#define SLB_VSID_LP             0x0000000000000030ULL
-#define SLB_VSID_ATTR           0x0000000000000FFFULL
-
 #define SEGMENT_SHIFT_256M      28
 #define SEGMENT_MASK_256M       (~((1ULL << SEGMENT_SHIFT_256M) - 1))
 
@@ -965,8 +941,6 @@ struct CPUPPCState {
     /* MMU context - only relevant for full system emulation */
 #if !defined(CONFIG_USER_ONLY)
 #if defined(TARGET_PPC64)
-    /* Address space register */
-    target_ulong asr;
     /* PowerPC 64 SLB area */
     ppc_slb_t slb[64];
     int slb_nr;
@@ -1105,20 +1079,6 @@ do {                                            \
     env->wdt_period[3] = (d_);                  \
  } while (0)
 
-#if !defined(CONFIG_USER_ONLY)
-/* Context used internally during MMU translations */
-typedef struct mmu_ctx_t mmu_ctx_t;
-struct mmu_ctx_t {
-    hwaddr raddr;      /* Real address              */
-    hwaddr eaddr;      /* Effective address         */
-    int prot;                      /* Protection bits           */
-    hwaddr hash[2];    /* Pagetable hash values     */
-    target_ulong ptem;             /* Virtual segment ID | API  */
-    int key;                       /* Access key                */
-    int nx;                        /* Non-execute area          */
-};
-#endif
-
 #include "cpu-qom.h"
 
 /*****************************************************************************/
@@ -1130,17 +1090,14 @@ int cpu_ppc_exec (CPUPPCState *s);
    is returned if the signal was handled by the virtual CPU.  */
 int cpu_ppc_signal_handler (int host_signum, void *pinfo,
                             void *puc);
-int cpu_ppc_handle_mmu_fault (CPUPPCState *env, target_ulong address, int rw,
-                              int mmu_idx);
-#define cpu_handle_mmu_fault cpu_ppc_handle_mmu_fault
 void ppc_hw_interrupt (CPUPPCState *env);
+#if defined(CONFIG_USER_ONLY)
+int cpu_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw,
+                         int mmu_idx);
+#endif
 
 #if !defined(CONFIG_USER_ONLY)
 void ppc_store_sdr1 (CPUPPCState *env, target_ulong value);
-#if defined(TARGET_PPC64)
-void ppc_store_asr (CPUPPCState *env, target_ulong value);
-int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs);
-#endif /* defined(TARGET_PPC64) */
 #endif /* !defined(CONFIG_USER_ONLY) */
 void ppc_store_msr (CPUPPCState *env, target_ulong value);
 
@@ -1172,14 +1129,13 @@ 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);
-int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb,
-                     hwaddr *raddrp, target_ulong address,
-                     uint32_t pid);
 void ppc_tlb_invalidate_all (CPUPPCState *env);
 void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr);
 #endif
 #endif
 
+void store_fpscr(CPUPPCState *env, uint64_t arg, uint32_t mask);
+
 static inline uint64_t ppc_dump_gpr(CPUPPCState *env, int gprn)
 {
     uint64_t gprv;
@@ -1270,6 +1226,7 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp)
 #define SPR_601_UDECR         (0x006)
 #define SPR_LR                (0x008)
 #define SPR_CTR               (0x009)
+#define SPR_UAMR              (0x00C)
 #define SPR_DSCR              (0x011)
 #define SPR_DSISR             (0x012)
 #define SPR_DAR               (0x013) /* DAE for PowerPC 601 */
@@ -1307,6 +1264,7 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp)
 #define SPR_MPC_CMPH          (0x09B)
 #define SPR_MPC_LCTRL1        (0x09C)
 #define SPR_MPC_LCTRL2        (0x09D)
+#define SPR_UAMOR             (0x09D)
 #define SPR_MPC_ICTRL         (0x09E)
 #define SPR_MPC_BAR           (0x09F)
 #define SPR_VRSAVE            (0x100)
@@ -1489,11 +1447,9 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp)
 #define SPR_RCPU_MI_RBA2      (0x302)
 #define SPR_MPC_MI_AP         (0x302)
 #define SPR_PERF3             (0x303)
-#define SPR_620_PMC1R         (0x303)
 #define SPR_RCPU_MI_RBA3      (0x303)
 #define SPR_MPC_MI_EPN        (0x303)
 #define SPR_PERF4             (0x304)
-#define SPR_620_PMC2R         (0x304)
 #define SPR_PERF5             (0x305)
 #define SPR_MPC_MI_TWC        (0x305)
 #define SPR_PERF6             (0x306)
@@ -1509,7 +1465,6 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp)
 #define SPR_RCPU_L2U_RBA2     (0x30A)
 #define SPR_MPC_MD_AP         (0x30A)
 #define SPR_PERFB             (0x30B)
-#define SPR_620_MMCR0R        (0x30B)
 #define SPR_RCPU_L2U_RBA3     (0x30B)
 #define SPR_MPC_MD_EPN        (0x30B)
 #define SPR_PERFC             (0x30C)
@@ -1524,9 +1479,7 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp)
 #define SPR_UPERF1            (0x311)
 #define SPR_UPERF2            (0x312)
 #define SPR_UPERF3            (0x313)
-#define SPR_620_PMC1W         (0x313)
 #define SPR_UPERF4            (0x314)
-#define SPR_620_PMC2W         (0x314)
 #define SPR_UPERF5            (0x315)
 #define SPR_UPERF6            (0x316)
 #define SPR_UPERF7            (0x317)
@@ -1534,7 +1487,6 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp)
 #define SPR_UPERF9            (0x319)
 #define SPR_UPERFA            (0x31A)
 #define SPR_UPERFB            (0x31B)
-#define SPR_620_MMCR0W        (0x31B)
 #define SPR_UPERFC            (0x31C)
 #define SPR_UPERFD            (0x31D)
 #define SPR_UPERFE            (0x31E)
@@ -1606,49 +1558,33 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp)
 #define SPR_USDA              (0x3AF)
 #define SPR_40x_ZPR           (0x3B0)
 #define SPR_BOOKE_MAS7        (0x3B0)
-#define SPR_620_PMR0          (0x3B0)
 #define SPR_MMCR2             (0x3B0)
 #define SPR_PMC5              (0x3B1)
 #define SPR_40x_PID           (0x3B1)
-#define SPR_620_PMR1          (0x3B1)
 #define SPR_PMC6              (0x3B2)
 #define SPR_440_MMUCR         (0x3B2)
-#define SPR_620_PMR2          (0x3B2)
 #define SPR_4xx_CCR0          (0x3B3)
 #define SPR_BOOKE_EPLC        (0x3B3)
-#define SPR_620_PMR3          (0x3B3)
 #define SPR_405_IAC3          (0x3B4)
 #define SPR_BOOKE_EPSC        (0x3B4)
-#define SPR_620_PMR4          (0x3B4)
 #define SPR_405_IAC4          (0x3B5)
-#define SPR_620_PMR5          (0x3B5)
 #define SPR_405_DVC1          (0x3B6)
-#define SPR_620_PMR6          (0x3B6)
 #define SPR_405_DVC2          (0x3B7)
-#define SPR_620_PMR7          (0x3B7)
 #define SPR_BAMR              (0x3B7)
 #define SPR_MMCR0             (0x3B8)
-#define SPR_620_PMR8          (0x3B8)
 #define SPR_PMC1              (0x3B9)
 #define SPR_40x_SGR           (0x3B9)
-#define SPR_620_PMR9          (0x3B9)
 #define SPR_PMC2              (0x3BA)
 #define SPR_40x_DCWR          (0x3BA)
-#define SPR_620_PMRA          (0x3BA)
 #define SPR_SIAR              (0x3BB)
 #define SPR_405_SLER          (0x3BB)
-#define SPR_620_PMRB          (0x3BB)
 #define SPR_MMCR1             (0x3BC)
 #define SPR_405_SU0R          (0x3BC)
-#define SPR_620_PMRC          (0x3BC)
 #define SPR_401_SKR           (0x3BC)
 #define SPR_PMC3              (0x3BD)
 #define SPR_405_DBCR1         (0x3BD)
-#define SPR_620_PMRD          (0x3BD)
 #define SPR_PMC4              (0x3BE)
-#define SPR_620_PMRE          (0x3BE)
 #define SPR_SDA               (0x3BF)
-#define SPR_620_PMRF          (0x3BF)
 #define SPR_403_VTBL          (0x3CC)
 #define SPR_403_VTBU          (0x3CD)
 #define SPR_DMISS             (0x3D0)
@@ -1716,15 +1652,12 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp)
 #define SPR_LDSTCR            (0x3F8)
 #define SPR_L2PMCR            (0x3F8)
 #define SPR_750FX_HID2        (0x3F8)
-#define SPR_620_BUSCSR        (0x3F8)
 #define SPR_Exxx_L1FINV0      (0x3F8)
 #define SPR_L2CR              (0x3F9)
-#define SPR_620_L2CR          (0x3F9)
 #define SPR_L3CR              (0x3FA)
 #define SPR_750_TDCH          (0x3FA)
 #define SPR_IABR2             (0x3FA)
 #define SPR_40x_DCCR          (0x3FA)
-#define SPR_620_L2SR          (0x3FA)
 #define SPR_ICTC              (0x3FB)
 #define SPR_40x_ICCR          (0x3FB)
 #define SPR_THRM1             (0x3FC)
diff --git a/target-ppc/fpu_helper.c b/target-ppc/fpu_helper.c
index 9d67926209..9e779eace6 100644
--- a/target-ppc/fpu_helper.c
+++ b/target-ppc/fpu_helper.c
@@ -463,6 +463,11 @@ void helper_store_fpscr(CPUPPCState *env, uint64_t arg, uint32_t mask)
     fpscr_set_rounding_mode(env);
 }
 
+void store_fpscr(CPUPPCState *env, uint64_t arg, uint32_t mask)
+{
+    helper_store_fpscr(env, arg, mask);
+}
+
 void helper_float_check_status(CPUPPCState *env)
 {
     if (env->exception_index == POWERPC_EXCP_PROGRAM &&
diff --git a/target-ppc/helper.h b/target-ppc/helper.h
index fcf372ab45..d33ee66b53 100644
--- a/target-ppc/helper.h
+++ b/target-ppc/helper.h
@@ -382,7 +382,6 @@ DEF_HELPER_1(load_601_rtcl, tl, env)
 DEF_HELPER_1(load_601_rtcu, tl, env)
 #if !defined(CONFIG_USER_ONLY)
 #if defined(TARGET_PPC64)
-DEF_HELPER_2(store_asr, void, env, tl)
 DEF_HELPER_1(load_purr, tl, env)
 #endif
 DEF_HELPER_2(store_sdr1, void, env, tl)
diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c
index e663ff0acb..597066f5a0 100644
--- a/target-ppc/kvm.c
+++ b/target-ppc/kvm.c
@@ -32,6 +32,7 @@
 #include "sysemu/device_tree.h"
 #include "hw/sysbus.h"
 #include "hw/spapr.h"
+#include "mmu-hash64.h"
 
 #include "hw/sysbus.h"
 #include "hw/spapr.h"
@@ -1077,7 +1078,7 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
         dprintf("handle halt\n");
         ret = kvmppc_handle_halt(cpu);
         break;
-#ifdef CONFIG_PSERIES
+#if defined(TARGET_PPC64)
     case KVM_EXIT_PAPR_HCALL:
         dprintf("handle PAPR hypercall\n");
         run->papr_hcall.ret = spapr_hypercall(cpu,
diff --git a/target-ppc/machine.c b/target-ppc/machine.c
index 708a840da7..235b0d5f49 100644
--- a/target-ppc/machine.c
+++ b/target-ppc/machine.c
@@ -37,7 +37,7 @@ void cpu_save(QEMUFile *f, void *opaque)
     qemu_put_be32s(f, &fpscr);
     qemu_put_sbe32s(f, &env->access_type);
 #if defined(TARGET_PPC64)
-    qemu_put_betls(f, &env->asr);
+    qemu_put_betls(f, &env->spr[SPR_ASR]);
     qemu_put_sbe32s(f, &env->slb_nr);
 #endif
     qemu_put_betls(f, &env->spr[SPR_SDR1]);
@@ -125,7 +125,7 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
     env->fpscr = fpscr;
     qemu_get_sbe32s(f, &env->access_type);
 #if defined(TARGET_PPC64)
-    qemu_get_betls(f, &env->asr);
+    qemu_get_betls(f, &env->spr[SPR_ASR]);
     qemu_get_sbe32s(f, &env->slb_nr);
 #endif
     qemu_get_betls(f, &sdr1);
diff --git a/target-ppc/mem_helper.c b/target-ppc/mem_helper.c
index ba383c8f11..9783e52b0c 100644
--- a/target-ppc/mem_helper.c
+++ b/target-ppc/mem_helper.c
@@ -252,41 +252,3 @@ STVE(stvewx, cpu_stl_data, bswap32, u32)
 
 #undef HI_IDX
 #undef LO_IDX
-
-/*****************************************************************************/
-/* Softmmu support */
-#if !defined(CONFIG_USER_ONLY)
-
-#define MMUSUFFIX _mmu
-
-#define SHIFT 0
-#include "exec/softmmu_template.h"
-
-#define SHIFT 1
-#include "exec/softmmu_template.h"
-
-#define SHIFT 2
-#include "exec/softmmu_template.h"
-
-#define SHIFT 3
-#include "exec/softmmu_template.h"
-
-/* try to fill the TLB and return an exception if error. If retaddr is
-   NULL, it means that the function was called in C code (i.e. not
-   from generated code or from helper.c) */
-/* XXX: fix it to restore all registers */
-void tlb_fill(CPUPPCState *env, target_ulong addr, int is_write, int mmu_idx,
-              uintptr_t retaddr)
-{
-    int ret;
-
-    ret = cpu_ppc_handle_mmu_fault(env, addr, is_write, mmu_idx);
-    if (unlikely(ret != 0)) {
-        if (likely(retaddr)) {
-            /* now we have a real cpu fault */
-            cpu_restore_state(env, retaddr);
-        }
-        helper_raise_exception_err(env, env->exception_index, env->error_code);
-    }
-}
-#endif /* !CONFIG_USER_ONLY */
diff --git a/target-ppc/misc_helper.c b/target-ppc/misc_helper.c
index 26edcca2df..616aab6fb6 100644
--- a/target-ppc/misc_helper.c
+++ b/target-ppc/misc_helper.c
@@ -35,12 +35,6 @@ void helper_store_dump_spr(CPUPPCState *env, uint32_t sprn)
              env->spr[sprn]);
 }
 #if !defined(CONFIG_USER_ONLY)
-#if defined(TARGET_PPC64)
-void helper_store_asr(CPUPPCState *env, target_ulong val)
-{
-    ppc_store_asr(env, val);
-}
-#endif
 
 void helper_store_sdr1(CPUPPCState *env, target_ulong val)
 {
diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c
new file mode 100644
index 0000000000..f6adf2245e
--- /dev/null
+++ b/target-ppc/mmu-hash32.c
@@ -0,0 +1,560 @@
+/*
+ *  PowerPC MMU, TLB and BAT emulation helpers for QEMU.
+ *
+ *  Copyright (c) 2003-2007 Jocelyn Mayer
+ *  Copyright (c) 2013 David Gibson, IBM Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cpu.h"
+#include "helper.h"
+#include "sysemu/kvm.h"
+#include "kvm_ppc.h"
+#include "mmu-hash32.h"
+
+//#define DEBUG_MMU
+//#define DEBUG_BAT
+
+#ifdef DEBUG_MMU
+#  define LOG_MMU(...) qemu_log(__VA_ARGS__)
+#  define LOG_MMU_STATE(env) log_cpu_state((env), 0)
+#else
+#  define LOG_MMU(...) do { } while (0)
+#  define LOG_MMU_STATE(...) do { } while (0)
+#endif
+
+#ifdef DEBUG_BATS
+#  define LOG_BATS(...) qemu_log(__VA_ARGS__)
+#else
+#  define LOG_BATS(...) do { } while (0)
+#endif
+
+struct mmu_ctx_hash32 {
+    hwaddr raddr;      /* Real address              */
+    int prot;                      /* Protection bits           */
+    int key;                       /* Access key                */
+};
+
+static int ppc_hash32_pp_prot(int key, int pp, int nx)
+{
+    int prot;
+
+    if (key == 0) {
+        switch (pp) {
+        case 0x0:
+        case 0x1:
+        case 0x2:
+            prot = PAGE_READ | PAGE_WRITE;
+            break;
+
+        case 0x3:
+            prot = PAGE_READ;
+            break;
+
+        default:
+            abort();
+        }
+    } else {
+        switch (pp) {
+        case 0x0:
+            prot = 0;
+            break;
+
+        case 0x1:
+        case 0x3:
+            prot = PAGE_READ;
+            break;
+
+        case 0x2:
+            prot = PAGE_READ | PAGE_WRITE;
+            break;
+
+        default:
+            abort();
+        }
+    }
+    if (nx == 0) {
+        prot |= PAGE_EXEC;
+    }
+
+    return prot;
+}
+
+static int ppc_hash32_pte_prot(CPUPPCState *env,
+                               target_ulong sr, ppc_hash_pte32_t pte)
+{
+    unsigned pp, key;
+
+    key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS));
+    pp = pte.pte1 & HPTE32_R_PP;
+
+    return ppc_hash32_pp_prot(key, pp, !!(sr & SR32_NX));
+}
+
+static target_ulong hash32_bat_size(CPUPPCState *env,
+                                    target_ulong batu, target_ulong batl)
+{
+    if ((msr_pr && !(batu & BATU32_VP))
+        || (!msr_pr && !(batu & BATU32_VS))) {
+        return 0;
+    }
+
+    return BATU32_BEPI & ~((batu & BATU32_BL) << 15);
+}
+
+static int hash32_bat_prot(CPUPPCState *env,
+                           target_ulong batu, target_ulong batl)
+{
+    int pp, prot;
+
+    prot = 0;
+    pp = batl & BATL32_PP;
+    if (pp != 0) {
+        prot = PAGE_READ | PAGE_EXEC;
+        if (pp == 0x2) {
+            prot |= PAGE_WRITE;
+        }
+    }
+    return prot;
+}
+
+static target_ulong hash32_bat_601_size(CPUPPCState *env,
+                                target_ulong batu, target_ulong batl)
+{
+    if (!(batl & BATL32_601_V)) {
+        return 0;
+    }
+
+    return BATU32_BEPI & ~((batl & BATL32_601_BL) << 17);
+}
+
+static int hash32_bat_601_prot(CPUPPCState *env,
+                               target_ulong batu, target_ulong batl)
+{
+    int key, pp;
+
+    pp = batu & BATU32_601_PP;
+    if (msr_pr == 0) {
+        key = !!(batu & BATU32_601_KS);
+    } else {
+        key = !!(batu & BATU32_601_KP);
+    }
+    return ppc_hash32_pp_prot(key, pp, 0);
+}
+
+static hwaddr ppc_hash32_bat_lookup(CPUPPCState *env, target_ulong ea, int rwx,
+                                    int *prot)
+{
+    target_ulong *BATlt, *BATut;
+    int i;
+
+    LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__,
+             rwx == 2 ? 'I' : 'D', ea);
+    if (rwx == 2) {
+        BATlt = env->IBAT[1];
+        BATut = env->IBAT[0];
+    } else {
+        BATlt = env->DBAT[1];
+        BATut = env->DBAT[0];
+    }
+    for (i = 0; i < env->nb_BATs; i++) {
+        target_ulong batu = BATut[i];
+        target_ulong batl = BATlt[i];
+        target_ulong mask;
+
+        if (unlikely(env->mmu_model == POWERPC_MMU_601)) {
+            mask = hash32_bat_601_size(env, batu, batl);
+        } else {
+            mask = hash32_bat_size(env, batu, batl);
+        }
+        LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx
+                 " BATl " TARGET_FMT_lx "\n", __func__,
+                 type == ACCESS_CODE ? 'I' : 'D', i, ea, batu, batl);
+
+        if (mask && ((ea & mask) == (batu & BATU32_BEPI))) {
+            hwaddr raddr = (batl & mask) | (ea & ~mask);
+
+            if (unlikely(env->mmu_model == POWERPC_MMU_601)) {
+                *prot = hash32_bat_601_prot(env, batu, batl);
+            } else {
+                *prot = hash32_bat_prot(env, batu, batl);
+            }
+
+            return raddr & TARGET_PAGE_MASK;
+        }
+    }
+
+    /* No hit */
+#if defined(DEBUG_BATS)
+    if (qemu_log_enabled()) {
+        LOG_BATS("no BAT match for " TARGET_FMT_lx ":\n", ea);
+        for (i = 0; i < 4; i++) {
+            BATu = &BATut[i];
+            BATl = &BATlt[i];
+            BEPIu = *BATu & BATU32_BEPIU;
+            BEPIl = *BATu & BATU32_BEPIL;
+            bl = (*BATu & 0x00001FFC) << 15;
+            LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx
+                     " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " "
+                     TARGET_FMT_lx " " TARGET_FMT_lx "\n",
+                     __func__, type == ACCESS_CODE ? 'I' : 'D', i, ea,
+                     *BATu, *BATl, BEPIu, BEPIl, bl);
+        }
+    }
+#endif
+
+    return -1;
+}
+
+static int ppc_hash32_direct_store(CPUPPCState *env, target_ulong sr,
+                                   target_ulong eaddr, int rwx,
+                                   hwaddr *raddr, int *prot)
+{
+    int key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS));
+
+    LOG_MMU("direct store...\n");
+
+    if ((sr & 0x1FF00000) >> 20 == 0x07f) {
+        /* Memory-forced I/O controller interface access */
+        /* If T=1 and BUID=x'07F', the 601 performs a memory access
+         * to SR[28-31] LA[4-31], bypassing all protection mechanisms.
+         */
+        *raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF);
+        *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+        return 0;
+    }
+
+    if (rwx == 2) {
+        /* No code fetch is allowed in direct-store areas */
+        env->exception_index = POWERPC_EXCP_ISI;
+        env->error_code = 0x10000000;
+        return 1;
+    }
+
+    switch (env->access_type) {
+    case ACCESS_INT:
+        /* Integer load/store : only access allowed */
+        break;
+    case ACCESS_FLOAT:
+        /* Floating point load/store */
+        env->exception_index = POWERPC_EXCP_ALIGN;
+        env->error_code = POWERPC_EXCP_ALIGN_FP;
+        env->spr[SPR_DAR] = eaddr;
+        return 1;
+    case ACCESS_RES:
+        /* lwarx, ldarx or srwcx. */
+        env->error_code = 0;
+        env->spr[SPR_DAR] = eaddr;
+        if (rwx == 1) {
+            env->spr[SPR_DSISR] = 0x06000000;
+        } else {
+            env->spr[SPR_DSISR] = 0x04000000;
+        }
+        return 1;
+    case ACCESS_CACHE:
+        /* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */
+        /* Should make the instruction do no-op.
+         * As it already do no-op, it's quite easy :-)
+         */
+        *raddr = eaddr;
+        return 0;
+    case ACCESS_EXT:
+        /* eciwx or ecowx */
+        env->exception_index = POWERPC_EXCP_DSI;
+        env->error_code = 0;
+        env->spr[SPR_DAR] = eaddr;
+        if (rwx == 1) {
+            env->spr[SPR_DSISR] = 0x06100000;
+        } else {
+            env->spr[SPR_DSISR] = 0x04100000;
+        }
+        return 1;
+    default:
+        qemu_log("ERROR: instruction should not need "
+                 "address translation\n");
+        abort();
+    }
+    if ((rwx == 1 || key != 1) && (rwx == 0 || key != 0)) {
+        *raddr = eaddr;
+        return 0;
+    } else {
+        env->exception_index = POWERPC_EXCP_DSI;
+        env->error_code = 0;
+        env->spr[SPR_DAR] = eaddr;
+        if (rwx == 1) {
+            env->spr[SPR_DSISR] = 0x0a000000;
+        } else {
+            env->spr[SPR_DSISR] = 0x08000000;
+        }
+        return 1;
+    }
+}
+
+hwaddr get_pteg_offset32(CPUPPCState *env, hwaddr hash)
+{
+    return (hash * HASH_PTEG_SIZE_32) & env->htab_mask;
+}
+
+static hwaddr ppc_hash32_pteg_search(CPUPPCState *env, hwaddr pteg_off,
+                                     bool secondary, target_ulong ptem,
+                                     ppc_hash_pte32_t *pte)
+{
+    hwaddr pte_offset = pteg_off;
+    target_ulong pte0, pte1;
+    int i;
+
+    for (i = 0; i < HPTES_PER_GROUP; i++) {
+        pte0 = ppc_hash32_load_hpte0(env, pte_offset);
+        pte1 = ppc_hash32_load_hpte1(env, pte_offset);
+
+        if ((pte0 & HPTE32_V_VALID)
+            && (secondary == !!(pte0 & HPTE32_V_SECONDARY))
+            && HPTE32_V_COMPARE(pte0, ptem)) {
+            pte->pte0 = pte0;
+            pte->pte1 = pte1;
+            return pte_offset;
+        }
+
+        pte_offset += HASH_PTE_SIZE_32;
+    }
+
+    return -1;
+}
+
+static hwaddr ppc_hash32_htab_lookup(CPUPPCState *env,
+                                     target_ulong sr, target_ulong eaddr,
+                                     ppc_hash_pte32_t *pte)
+{
+    hwaddr pteg_off, pte_offset;
+    hwaddr hash;
+    uint32_t vsid, pgidx, ptem;
+
+    vsid = sr & SR32_VSID;
+    pgidx = (eaddr & ~SEGMENT_MASK_256M) >> TARGET_PAGE_BITS;
+    hash = vsid ^ pgidx;
+    ptem = (vsid << 7) | (pgidx >> 10);
+
+    /* Page address translation */
+    LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx
+            " hash " TARGET_FMT_plx "\n",
+            env->htab_base, env->htab_mask, hash);
+
+    /* Primary PTEG lookup */
+    LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
+            " vsid=%" PRIx32 " ptem=%" PRIx32
+            " hash=" TARGET_FMT_plx "\n",
+            env->htab_base, env->htab_mask, vsid, ptem, hash);
+    pteg_off = get_pteg_offset32(env, hash);
+    pte_offset = ppc_hash32_pteg_search(env, pteg_off, 0, ptem, pte);
+    if (pte_offset == -1) {
+        /* Secondary PTEG lookup */
+        LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
+                " vsid=%" PRIx32 " api=%" PRIx32
+                " hash=" TARGET_FMT_plx "\n", env->htab_base,
+                env->htab_mask, vsid, ptem, ~hash);
+        pteg_off = get_pteg_offset32(env, ~hash);
+        pte_offset = ppc_hash32_pteg_search(env, pteg_off, 1, ptem, pte);
+    }
+
+    return pte_offset;
+}
+
+static hwaddr ppc_hash32_pte_raddr(target_ulong sr, ppc_hash_pte32_t pte,
+                                   target_ulong eaddr)
+{
+    hwaddr rpn = pte.pte1 & HPTE32_R_RPN;
+    hwaddr mask = ~TARGET_PAGE_MASK;
+
+    return (rpn & ~mask) | (eaddr & mask);
+}
+
+int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong eaddr, int rwx,
+                                int mmu_idx)
+{
+    target_ulong sr;
+    hwaddr pte_offset;
+    ppc_hash_pte32_t pte;
+    int prot;
+    uint32_t new_pte1;
+    const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC};
+    hwaddr raddr;
+
+    assert((rwx == 0) || (rwx == 1) || (rwx == 2));
+
+    /* 1. Handle real mode accesses */
+    if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) {
+        /* Translation is off */
+        raddr = eaddr;
+        tlb_set_page(env, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
+                     PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx,
+                     TARGET_PAGE_SIZE);
+        return 0;
+    }
+
+    /* 2. Check Block Address Translation entries (BATs) */
+    if (env->nb_BATs != 0) {
+        raddr = ppc_hash32_bat_lookup(env, eaddr, rwx, &prot);
+        if (raddr != -1) {
+            if (need_prot[rwx] & ~prot) {
+                if (rwx == 2) {
+                    env->exception_index = POWERPC_EXCP_ISI;
+                    env->error_code = 0x08000000;
+                } else {
+                    env->exception_index = POWERPC_EXCP_DSI;
+                    env->error_code = 0;
+                    env->spr[SPR_DAR] = eaddr;
+                    if (rwx == 1) {
+                        env->spr[SPR_DSISR] = 0x0a000000;
+                    } else {
+                        env->spr[SPR_DSISR] = 0x08000000;
+                    }
+                }
+                return 1;
+            }
+
+            tlb_set_page(env, eaddr & TARGET_PAGE_MASK,
+                         raddr & TARGET_PAGE_MASK, prot, mmu_idx,
+                         TARGET_PAGE_SIZE);
+            return 0;
+        }
+    }
+
+    /* 3. Look up the Segment Register */
+    sr = env->sr[eaddr >> 28];
+
+    /* 4. Handle direct store segments */
+    if (sr & SR32_T) {
+        if (ppc_hash32_direct_store(env, sr, eaddr, rwx,
+                                    &raddr, &prot) == 0) {
+            tlb_set_page(env, eaddr & TARGET_PAGE_MASK,
+                         raddr & TARGET_PAGE_MASK, prot, mmu_idx,
+                         TARGET_PAGE_SIZE);
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
+    /* 5. Check for segment level no-execute violation */
+    if ((rwx == 2) && (sr & SR32_NX)) {
+        env->exception_index = POWERPC_EXCP_ISI;
+        env->error_code = 0x10000000;
+        return 1;
+    }
+
+    /* 6. Locate the PTE in the hash table */
+    pte_offset = ppc_hash32_htab_lookup(env, sr, eaddr, &pte);
+    if (pte_offset == -1) {
+        if (rwx == 2) {
+            env->exception_index = POWERPC_EXCP_ISI;
+            env->error_code = 0x40000000;
+        } else {
+            env->exception_index = POWERPC_EXCP_DSI;
+            env->error_code = 0;
+            env->spr[SPR_DAR] = eaddr;
+            if (rwx == 1) {
+                env->spr[SPR_DSISR] = 0x42000000;
+            } else {
+                env->spr[SPR_DSISR] = 0x40000000;
+            }
+        }
+
+        return 1;
+    }
+    LOG_MMU("found PTE at offset %08" HWADDR_PRIx "\n", pte_offset);
+
+    /* 7. Check access permissions */
+
+    prot = ppc_hash32_pte_prot(env, sr, pte);
+
+    if (need_prot[rwx] & ~prot) {
+        /* Access right violation */
+        LOG_MMU("PTE access rejected\n");
+        if (rwx == 2) {
+            env->exception_index = POWERPC_EXCP_ISI;
+            env->error_code = 0x08000000;
+        } else {
+            env->exception_index = POWERPC_EXCP_DSI;
+            env->error_code = 0;
+            env->spr[SPR_DAR] = eaddr;
+            if (rwx == 1) {
+                env->spr[SPR_DSISR] = 0x0a000000;
+            } else {
+                env->spr[SPR_DSISR] = 0x08000000;
+            }
+        }
+        return 1;
+    }
+
+    LOG_MMU("PTE access granted !\n");
+
+    /* 8. Update PTE referenced and changed bits if necessary */
+
+    new_pte1 = pte.pte1 | HPTE32_R_R; /* set referenced bit */
+    if (rwx == 1) {
+        new_pte1 |= HPTE32_R_C; /* set changed (dirty) bit */
+    } else {
+        /* Treat the page as read-only for now, so that a later write
+         * will pass through this function again to set the C bit */
+        prot &= ~PAGE_WRITE;
+    }
+
+    if (new_pte1 != pte.pte1) {
+        ppc_hash32_store_hpte1(env, pte_offset, new_pte1);
+    }
+
+    /* 9. Determine the real address from the PTE */
+
+    raddr = ppc_hash32_pte_raddr(sr, pte, eaddr);
+
+    tlb_set_page(env, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
+                 prot, mmu_idx, TARGET_PAGE_SIZE);
+
+    return 0;
+}
+
+hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong eaddr)
+{
+    target_ulong sr;
+    hwaddr pte_offset;
+    ppc_hash_pte32_t pte;
+    int prot;
+
+    if (msr_dr == 0) {
+        /* Translation is off */
+        return eaddr;
+    }
+
+    if (env->nb_BATs != 0) {
+        hwaddr raddr = ppc_hash32_bat_lookup(env, eaddr, 0, &prot);
+        if (raddr != -1) {
+            return raddr;
+        }
+    }
+
+    sr = env->sr[eaddr >> 28];
+
+    if (sr & SR32_T) {
+        /* FIXME: Add suitable debug support for Direct Store segments */
+        return -1;
+    }
+
+    pte_offset = ppc_hash32_htab_lookup(env, sr, eaddr, &pte);
+    if (pte_offset == -1) {
+        return -1;
+    }
+
+    return ppc_hash32_pte_raddr(sr, pte, eaddr) & TARGET_PAGE_MASK;
+}
diff --git a/target-ppc/mmu-hash32.h b/target-ppc/mmu-hash32.h
new file mode 100644
index 0000000000..884786b97a
--- /dev/null
+++ b/target-ppc/mmu-hash32.h
@@ -0,0 +1,102 @@
+#if !defined (__MMU_HASH32_H__)
+#define __MMU_HASH32_H__
+
+#ifndef CONFIG_USER_ONLY
+
+hwaddr get_pteg_offset32(CPUPPCState *env, hwaddr hash);
+hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong addr);
+int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw,
+                                int mmu_idx);
+
+/*
+ * Segment register definitions
+ */
+
+#define SR32_T                  0x80000000
+#define SR32_KS                 0x40000000
+#define SR32_KP                 0x20000000
+#define SR32_NX                 0x10000000
+#define SR32_VSID               0x00ffffff
+
+/*
+ * Block Address Translation (BAT) definitions
+ */
+
+#define BATU32_BEPI             0xfffe0000
+#define BATU32_BL               0x00001ffc
+#define BATU32_VS               0x00000002
+#define BATU32_VP               0x00000001
+
+
+#define BATL32_BRPN             0xfffe0000
+#define BATL32_WIMG             0x00000078
+#define BATL32_PP               0x00000003
+
+/* PowerPC 601 has slightly different BAT registers */
+
+#define BATU32_601_KS           0x00000008
+#define BATU32_601_KP           0x00000004
+#define BATU32_601_PP           0x00000003
+
+#define BATL32_601_V            0x00000040
+#define BATL32_601_BL           0x0000003f
+
+/*
+ * Hash page table definitions
+ */
+
+#define HPTES_PER_GROUP         8
+#define HASH_PTE_SIZE_32        8
+#define HASH_PTEG_SIZE_32       (HASH_PTE_SIZE_32 * HPTES_PER_GROUP)
+
+#define HPTE32_V_VALID          0x80000000
+#define HPTE32_V_VSID           0x7fffff80
+#define HPTE32_V_SECONDARY      0x00000040
+#define HPTE32_V_API            0x0000003f
+#define HPTE32_V_COMPARE(x, y)  (!(((x) ^ (y)) & 0x7fffffbf))
+
+#define HPTE32_R_RPN            0xfffff000
+#define HPTE32_R_R              0x00000100
+#define HPTE32_R_C              0x00000080
+#define HPTE32_R_W              0x00000040
+#define HPTE32_R_I              0x00000020
+#define HPTE32_R_M              0x00000010
+#define HPTE32_R_G              0x00000008
+#define HPTE32_R_WIMG           0x00000078
+#define HPTE32_R_PP             0x00000003
+
+static inline target_ulong ppc_hash32_load_hpte0(CPUPPCState *env,
+                                                 hwaddr pte_offset)
+{
+    assert(!env->external_htab); /* Not supported on 32-bit for now */
+    return ldl_phys(env->htab_base + pte_offset);
+}
+
+static inline target_ulong ppc_hash32_load_hpte1(CPUPPCState *env,
+                                                 hwaddr pte_offset)
+{
+    assert(!env->external_htab); /* Not supported on 32-bit for now */
+    return ldl_phys(env->htab_base + pte_offset + HASH_PTE_SIZE_32/2);
+}
+
+static inline void ppc_hash32_store_hpte0(CPUPPCState *env,
+                                          hwaddr pte_offset, target_ulong pte0)
+{
+    assert(!env->external_htab); /* Not supported on 32-bit for now */
+    stl_phys(env->htab_base + pte_offset, pte0);
+}
+
+static inline void ppc_hash32_store_hpte1(CPUPPCState *env,
+                                          hwaddr pte_offset, target_ulong pte1)
+{
+    assert(!env->external_htab); /* Not supported on 32-bit for now */
+    stl_phys(env->htab_base + pte_offset + HASH_PTE_SIZE_32/2, pte1);
+}
+
+typedef struct {
+    uint32_t pte0, pte1;
+} ppc_hash_pte32_t;
+
+#endif /* CONFIG_USER_ONLY */
+
+#endif /* __MMU_HASH32_H__ */
diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c
new file mode 100644
index 0000000000..43ccf456e3
--- /dev/null
+++ b/target-ppc/mmu-hash64.c
@@ -0,0 +1,546 @@
+/*
+ *  PowerPC MMU, TLB, SLB and BAT emulation helpers for QEMU.
+ *
+ *  Copyright (c) 2003-2007 Jocelyn Mayer
+ *  Copyright (c) 2013 David Gibson, IBM Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "cpu.h"
+#include "helper.h"
+#include "sysemu/kvm.h"
+#include "kvm_ppc.h"
+#include "mmu-hash64.h"
+
+//#define DEBUG_MMU
+//#define DEBUG_SLB
+
+#ifdef DEBUG_MMU
+#  define LOG_MMU(...) qemu_log(__VA_ARGS__)
+#  define LOG_MMU_STATE(env) log_cpu_state((env), 0)
+#else
+#  define LOG_MMU(...) do { } while (0)
+#  define LOG_MMU_STATE(...) do { } while (0)
+#endif
+
+#ifdef DEBUG_SLB
+#  define LOG_SLB(...) qemu_log(__VA_ARGS__)
+#else
+#  define LOG_SLB(...) do { } while (0)
+#endif
+
+/*
+ * SLB handling
+ */
+
+static ppc_slb_t *slb_lookup(CPUPPCState *env, target_ulong eaddr)
+{
+    uint64_t esid_256M, esid_1T;
+    int n;
+
+    LOG_SLB("%s: eaddr " TARGET_FMT_lx "\n", __func__, eaddr);
+
+    esid_256M = (eaddr & SEGMENT_MASK_256M) | SLB_ESID_V;
+    esid_1T = (eaddr & SEGMENT_MASK_1T) | SLB_ESID_V;
+
+    for (n = 0; n < env->slb_nr; n++) {
+        ppc_slb_t *slb = &env->slb[n];
+
+        LOG_SLB("%s: slot %d %016" PRIx64 " %016"
+                    PRIx64 "\n", __func__, n, slb->esid, slb->vsid);
+        /* We check for 1T matches on all MMUs here - if the MMU
+         * doesn't have 1T segment support, we will have prevented 1T
+         * entries from being inserted in the slbmte code. */
+        if (((slb->esid == esid_256M) &&
+             ((slb->vsid & SLB_VSID_B) == SLB_VSID_B_256M))
+            || ((slb->esid == esid_1T) &&
+                ((slb->vsid & SLB_VSID_B) == SLB_VSID_B_1T))) {
+            return slb;
+        }
+    }
+
+    return NULL;
+}
+
+void dump_slb(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env)
+{
+    int i;
+    uint64_t slbe, slbv;
+
+    cpu_synchronize_state(env);
+
+    cpu_fprintf(f, "SLB\tESID\t\t\tVSID\n");
+    for (i = 0; i < env->slb_nr; i++) {
+        slbe = env->slb[i].esid;
+        slbv = env->slb[i].vsid;
+        if (slbe == 0 && slbv == 0) {
+            continue;
+        }
+        cpu_fprintf(f, "%d\t0x%016" PRIx64 "\t0x%016" PRIx64 "\n",
+                    i, slbe, slbv);
+    }
+}
+
+void helper_slbia(CPUPPCState *env)
+{
+    int n, do_invalidate;
+
+    do_invalidate = 0;
+    /* XXX: Warning: slbia never invalidates the first segment */
+    for (n = 1; n < env->slb_nr; n++) {
+        ppc_slb_t *slb = &env->slb[n];
+
+        if (slb->esid & SLB_ESID_V) {
+            slb->esid &= ~SLB_ESID_V;
+            /* XXX: given the fact that segment size is 256 MB or 1TB,
+             *      and we still don't have a tlb_flush_mask(env, n, mask)
+             *      in QEMU, we just invalidate all TLBs
+             */
+            do_invalidate = 1;
+        }
+    }
+    if (do_invalidate) {
+        tlb_flush(env, 1);
+    }
+}
+
+void helper_slbie(CPUPPCState *env, target_ulong addr)
+{
+    ppc_slb_t *slb;
+
+    slb = slb_lookup(env, addr);
+    if (!slb) {
+        return;
+    }
+
+    if (slb->esid & SLB_ESID_V) {
+        slb->esid &= ~SLB_ESID_V;
+
+        /* XXX: given the fact that segment size is 256 MB or 1TB,
+         *      and we still don't have a tlb_flush_mask(env, n, mask)
+         *      in QEMU, we just invalidate all TLBs
+         */
+        tlb_flush(env, 1);
+    }
+}
+
+int ppc_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs)
+{
+    int slot = rb & 0xfff;
+    ppc_slb_t *slb = &env->slb[slot];
+
+    if (rb & (0x1000 - env->slb_nr)) {
+        return -1; /* Reserved bits set or slot too high */
+    }
+    if (rs & (SLB_VSID_B & ~SLB_VSID_B_1T)) {
+        return -1; /* Bad segment size */
+    }
+    if ((rs & SLB_VSID_B) && !(env->mmu_model & POWERPC_MMU_1TSEG)) {
+        return -1; /* 1T segment on MMU that doesn't support it */
+    }
+
+    /* Mask out the slot number as we store the entry */
+    slb->esid = rb & (SLB_ESID_ESID | SLB_ESID_V);
+    slb->vsid = rs;
+
+    LOG_SLB("%s: %d " TARGET_FMT_lx " - " TARGET_FMT_lx " => %016" PRIx64
+            " %016" PRIx64 "\n", __func__, slot, rb, rs,
+            slb->esid, slb->vsid);
+
+    return 0;
+}
+
+static int ppc_load_slb_esid(CPUPPCState *env, target_ulong rb,
+                             target_ulong *rt)
+{
+    int slot = rb & 0xfff;
+    ppc_slb_t *slb = &env->slb[slot];
+
+    if (slot >= env->slb_nr) {
+        return -1;
+    }
+
+    *rt = slb->esid;
+    return 0;
+}
+
+static int ppc_load_slb_vsid(CPUPPCState *env, target_ulong rb,
+                             target_ulong *rt)
+{
+    int slot = rb & 0xfff;
+    ppc_slb_t *slb = &env->slb[slot];
+
+    if (slot >= env->slb_nr) {
+        return -1;
+    }
+
+    *rt = slb->vsid;
+    return 0;
+}
+
+void helper_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs)
+{
+    if (ppc_store_slb(env, rb, rs) < 0) {
+        helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM,
+                                   POWERPC_EXCP_INVAL);
+    }
+}
+
+target_ulong helper_load_slb_esid(CPUPPCState *env, target_ulong rb)
+{
+    target_ulong rt = 0;
+
+    if (ppc_load_slb_esid(env, rb, &rt) < 0) {
+        helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM,
+                                   POWERPC_EXCP_INVAL);
+    }
+    return rt;
+}
+
+target_ulong helper_load_slb_vsid(CPUPPCState *env, target_ulong rb)
+{
+    target_ulong rt = 0;
+
+    if (ppc_load_slb_vsid(env, rb, &rt) < 0) {
+        helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM,
+                                   POWERPC_EXCP_INVAL);
+    }
+    return rt;
+}
+
+/*
+ * 64-bit hash table MMU handling
+ */
+
+static int ppc_hash64_pte_prot(CPUPPCState *env,
+                               ppc_slb_t *slb, ppc_hash_pte64_t pte)
+{
+    unsigned pp, key;
+    /* Some pp bit combinations have undefined behaviour, so default
+     * to no access in those cases */
+    int prot = 0;
+
+    key = !!(msr_pr ? (slb->vsid & SLB_VSID_KP)
+             : (slb->vsid & SLB_VSID_KS));
+    pp = (pte.pte1 & HPTE64_R_PP) | ((pte.pte1 & HPTE64_R_PP0) >> 61);
+
+    if (key == 0) {
+        switch (pp) {
+        case 0x0:
+        case 0x1:
+        case 0x2:
+            prot = PAGE_READ | PAGE_WRITE;
+            break;
+
+        case 0x3:
+        case 0x6:
+            prot = PAGE_READ;
+            break;
+        }
+    } else {
+        switch (pp) {
+        case 0x0:
+        case 0x6:
+            prot = 0;
+            break;
+
+        case 0x1:
+        case 0x3:
+            prot = PAGE_READ;
+            break;
+
+        case 0x2:
+            prot = PAGE_READ | PAGE_WRITE;
+            break;
+        }
+    }
+
+    /* No execute if either noexec or guarded bits set */
+    if (!(pte.pte1 & HPTE64_R_N) || (pte.pte1 & HPTE64_R_G)
+        || (slb->vsid & SLB_VSID_N)) {
+        prot |= PAGE_EXEC;
+    }
+
+    return prot;
+}
+
+static int ppc_hash64_amr_prot(CPUPPCState *env, ppc_hash_pte64_t pte)
+{
+    int key, amrbits;
+    int prot = PAGE_EXEC;
+
+
+    /* Only recent MMUs implement Virtual Page Class Key Protection */
+    if (!(env->mmu_model & POWERPC_MMU_AMR)) {
+        return PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+    }
+
+    key = HPTE64_R_KEY(pte.pte1);
+    amrbits = (env->spr[SPR_AMR] >> 2*(31 - key)) & 0x3;
+
+    /* fprintf(stderr, "AMR protection: key=%d AMR=0x%" PRIx64 "\n", key, */
+    /*         env->spr[SPR_AMR]); */
+
+    if (amrbits & 0x2) {
+        prot |= PAGE_WRITE;
+    }
+    if (amrbits & 0x1) {
+        prot |= PAGE_READ;
+    }
+
+    return prot;
+}
+
+static hwaddr ppc_hash64_pteg_search(CPUPPCState *env, hwaddr pteg_off,
+                                     bool secondary, target_ulong ptem,
+                                     ppc_hash_pte64_t *pte)
+{
+    hwaddr pte_offset = pteg_off;
+    target_ulong pte0, pte1;
+    int i;
+
+    for (i = 0; i < HPTES_PER_GROUP; i++) {
+        pte0 = ppc_hash64_load_hpte0(env, pte_offset);
+        pte1 = ppc_hash64_load_hpte1(env, pte_offset);
+
+        if ((pte0 & HPTE64_V_VALID)
+            && (secondary == !!(pte0 & HPTE64_V_SECONDARY))
+            && HPTE64_V_COMPARE(pte0, ptem)) {
+            pte->pte0 = pte0;
+            pte->pte1 = pte1;
+            return pte_offset;
+        }
+
+        pte_offset += HASH_PTE_SIZE_64;
+    }
+
+    return -1;
+}
+
+static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env,
+                                     ppc_slb_t *slb, target_ulong eaddr,
+                                     ppc_hash_pte64_t *pte)
+{
+    hwaddr pteg_off, pte_offset;
+    hwaddr hash;
+    uint64_t vsid, epnshift, epnmask, epn, ptem;
+
+    /* Page size according to the SLB, which we use to generate the
+     * EPN for hash table lookup..  When we implement more recent MMU
+     * extensions this might be different from the actual page size
+     * encoded in the PTE */
+    epnshift = (slb->vsid & SLB_VSID_L)
+        ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS;
+    epnmask = ~((1ULL << epnshift) - 1);
+
+    if (slb->vsid & SLB_VSID_B) {
+        /* 1TB segment */
+        vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT_1T;
+        epn = (eaddr & ~SEGMENT_MASK_1T) & epnmask;
+        hash = vsid ^ (vsid << 25) ^ (epn >> epnshift);
+    } else {
+        /* 256M segment */
+        vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT;
+        epn = (eaddr & ~SEGMENT_MASK_256M) & epnmask;
+        hash = vsid ^ (epn >> epnshift);
+    }
+    ptem = (slb->vsid & SLB_VSID_PTEM) | ((epn >> 16) & HPTE64_V_AVPN);
+
+    /* Page address translation */
+    LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx
+            " hash " TARGET_FMT_plx "\n",
+            env->htab_base, env->htab_mask, hash);
+
+    /* Primary PTEG lookup */
+    LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
+            " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx
+            " hash=" TARGET_FMT_plx "\n",
+            env->htab_base, env->htab_mask, vsid, ptem,  hash);
+    pteg_off = (hash * HASH_PTEG_SIZE_64) & env->htab_mask;
+    pte_offset = ppc_hash64_pteg_search(env, pteg_off, 0, ptem, pte);
+
+    if (pte_offset == -1) {
+        /* Secondary PTEG lookup */
+        LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
+                " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx
+                " hash=" TARGET_FMT_plx "\n", env->htab_base,
+                env->htab_mask, vsid, ptem, ~hash);
+
+        pteg_off = (~hash * HASH_PTEG_SIZE_64) & env->htab_mask;
+        pte_offset = ppc_hash64_pteg_search(env, pteg_off, 1, ptem, pte);
+    }
+
+    return pte_offset;
+}
+
+static hwaddr ppc_hash64_pte_raddr(ppc_slb_t *slb, ppc_hash_pte64_t pte,
+                                   target_ulong eaddr)
+{
+    hwaddr rpn = pte.pte1 & HPTE64_R_RPN;
+    /* FIXME: Add support for SLLP extended page sizes */
+    int target_page_bits = (slb->vsid & SLB_VSID_L)
+        ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS;
+    hwaddr mask = (1ULL << target_page_bits) - 1;
+
+    return (rpn & ~mask) | (eaddr & mask);
+}
+
+int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong eaddr,
+                                int rwx, int mmu_idx)
+{
+    ppc_slb_t *slb;
+    hwaddr pte_offset;
+    ppc_hash_pte64_t pte;
+    int pp_prot, amr_prot, prot;
+    uint64_t new_pte1;
+    const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC};
+    hwaddr raddr;
+
+    assert((rwx == 0) || (rwx == 1) || (rwx == 2));
+
+    /* 1. Handle real mode accesses */
+    if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) {
+        /* Translation is off */
+        /* In real mode the top 4 effective address bits are ignored */
+        raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL;
+        tlb_set_page(env, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
+                     PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx,
+                     TARGET_PAGE_SIZE);
+        return 0;
+    }
+
+    /* 2. Translation is on, so look up the SLB */
+    slb = slb_lookup(env, eaddr);
+
+    if (!slb) {
+        if (rwx == 2) {
+            env->exception_index = POWERPC_EXCP_ISEG;
+            env->error_code = 0;
+        } else {
+            env->exception_index = POWERPC_EXCP_DSEG;
+            env->error_code = 0;
+            env->spr[SPR_DAR] = eaddr;
+        }
+        return 1;
+    }
+
+    /* 3. Check for segment level no-execute violation */
+    if ((rwx == 2) && (slb->vsid & SLB_VSID_N)) {
+        env->exception_index = POWERPC_EXCP_ISI;
+        env->error_code = 0x10000000;
+        return 1;
+    }
+
+    /* 4. Locate the PTE in the hash table */
+    pte_offset = ppc_hash64_htab_lookup(env, slb, eaddr, &pte);
+    if (pte_offset == -1) {
+        if (rwx == 2) {
+            env->exception_index = POWERPC_EXCP_ISI;
+            env->error_code = 0x40000000;
+        } else {
+            env->exception_index = POWERPC_EXCP_DSI;
+            env->error_code = 0;
+            env->spr[SPR_DAR] = eaddr;
+            if (rwx == 1) {
+                env->spr[SPR_DSISR] = 0x42000000;
+            } else {
+                env->spr[SPR_DSISR] = 0x40000000;
+            }
+        }
+        return 1;
+    }
+    LOG_MMU("found PTE at offset %08" HWADDR_PRIx "\n", pte_offset);
+
+    /* 5. Check access permissions */
+
+    pp_prot = ppc_hash64_pte_prot(env, slb, pte);
+    amr_prot = ppc_hash64_amr_prot(env, pte);
+    prot = pp_prot & amr_prot;
+
+    if ((need_prot[rwx] & ~prot) != 0) {
+        /* Access right violation */
+        LOG_MMU("PTE access rejected\n");
+        if (rwx == 2) {
+            env->exception_index = POWERPC_EXCP_ISI;
+            env->error_code = 0x08000000;
+        } else {
+            target_ulong dsisr = 0;
+
+            env->exception_index = POWERPC_EXCP_DSI;
+            env->error_code = 0;
+            env->spr[SPR_DAR] = eaddr;
+            if (need_prot[rwx] & ~pp_prot) {
+                dsisr |= 0x08000000;
+            }
+            if (rwx == 1) {
+                dsisr |= 0x02000000;
+            }
+            if (need_prot[rwx] & ~amr_prot) {
+                dsisr |= 0x00200000;
+            }
+            env->spr[SPR_DSISR] = dsisr;
+        }
+        return 1;
+    }
+
+    LOG_MMU("PTE access granted !\n");
+
+    /* 6. Update PTE referenced and changed bits if necessary */
+
+    new_pte1 = pte.pte1 | HPTE64_R_R; /* set referenced bit */
+    if (rwx == 1) {
+        new_pte1 |= HPTE64_R_C; /* set changed (dirty) bit */
+    } else {
+        /* Treat the page as read-only for now, so that a later write
+         * will pass through this function again to set the C bit */
+        prot &= ~PAGE_WRITE;
+    }
+
+    if (new_pte1 != pte.pte1) {
+        ppc_hash64_store_hpte1(env, pte_offset, new_pte1);
+    }
+
+    /* 7. Determine the real address from the PTE */
+
+    raddr = ppc_hash64_pte_raddr(slb, pte, eaddr);
+
+    tlb_set_page(env, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
+                 prot, mmu_idx, TARGET_PAGE_SIZE);
+
+    return 0;
+}
+
+hwaddr ppc_hash64_get_phys_page_debug(CPUPPCState *env, target_ulong addr)
+{
+    ppc_slb_t *slb;
+    hwaddr pte_offset;
+    ppc_hash_pte64_t pte;
+
+    if (msr_dr == 0) {
+        /* In real mode the top 4 effective address bits are ignored */
+        return addr & 0x0FFFFFFFFFFFFFFFULL;
+    }
+
+    slb = slb_lookup(env, addr);
+    if (!slb) {
+        return -1;
+    }
+
+    pte_offset = ppc_hash64_htab_lookup(env, slb, addr, &pte);
+    if (pte_offset == -1) {
+        return -1;
+    }
+
+    return ppc_hash64_pte_raddr(slb, pte, addr) & TARGET_PAGE_MASK;
+}
diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h
new file mode 100644
index 0000000000..55f5a230fd
--- /dev/null
+++ b/target-ppc/mmu-hash64.h
@@ -0,0 +1,124 @@
+#if !defined (__MMU_HASH64_H__)
+#define __MMU_HASH64_H__
+
+#ifndef CONFIG_USER_ONLY
+
+#ifdef TARGET_PPC64
+void dump_slb(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env);
+int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs);
+hwaddr ppc_hash64_get_phys_page_debug(CPUPPCState *env, target_ulong addr);
+int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw,
+                                int mmu_idx);
+#endif
+
+/*
+ * SLB definitions
+ */
+
+/* Bits in the SLB ESID word */
+#define SLB_ESID_ESID           0xFFFFFFFFF0000000ULL
+#define SLB_ESID_V              0x0000000008000000ULL /* valid */
+
+/* Bits in the SLB VSID word */
+#define SLB_VSID_SHIFT          12
+#define SLB_VSID_SHIFT_1T       24
+#define SLB_VSID_SSIZE_SHIFT    62
+#define SLB_VSID_B              0xc000000000000000ULL
+#define SLB_VSID_B_256M         0x0000000000000000ULL
+#define SLB_VSID_B_1T           0x4000000000000000ULL
+#define SLB_VSID_VSID           0x3FFFFFFFFFFFF000ULL
+#define SLB_VSID_PTEM           (SLB_VSID_B | SLB_VSID_VSID)
+#define SLB_VSID_KS             0x0000000000000800ULL
+#define SLB_VSID_KP             0x0000000000000400ULL
+#define SLB_VSID_N              0x0000000000000200ULL /* no-execute */
+#define SLB_VSID_L              0x0000000000000100ULL
+#define SLB_VSID_C              0x0000000000000080ULL /* class */
+#define SLB_VSID_LP             0x0000000000000030ULL
+#define SLB_VSID_ATTR           0x0000000000000FFFULL
+
+/*
+ * Hash page table definitions
+ */
+
+#define HPTES_PER_GROUP         8
+#define HASH_PTE_SIZE_64        16
+#define HASH_PTEG_SIZE_64       (HASH_PTE_SIZE_64 * HPTES_PER_GROUP)
+
+#define HPTE64_V_SSIZE_SHIFT    62
+#define HPTE64_V_AVPN_SHIFT     7
+#define HPTE64_V_AVPN           0x3fffffffffffff80ULL
+#define HPTE64_V_AVPN_VAL(x)    (((x) & HPTE64_V_AVPN) >> HPTE64_V_AVPN_SHIFT)
+#define HPTE64_V_COMPARE(x, y)  (!(((x) ^ (y)) & 0xffffffffffffff80ULL))
+#define HPTE64_V_LARGE          0x0000000000000004ULL
+#define HPTE64_V_SECONDARY      0x0000000000000002ULL
+#define HPTE64_V_VALID          0x0000000000000001ULL
+
+#define HPTE64_R_PP0            0x8000000000000000ULL
+#define HPTE64_R_TS             0x4000000000000000ULL
+#define HPTE64_R_KEY_HI         0x3000000000000000ULL
+#define HPTE64_R_RPN_SHIFT      12
+#define HPTE64_R_RPN            0x0ffffffffffff000ULL
+#define HPTE64_R_FLAGS          0x00000000000003ffULL
+#define HPTE64_R_PP             0x0000000000000003ULL
+#define HPTE64_R_N              0x0000000000000004ULL
+#define HPTE64_R_G              0x0000000000000008ULL
+#define HPTE64_R_M              0x0000000000000010ULL
+#define HPTE64_R_I              0x0000000000000020ULL
+#define HPTE64_R_W              0x0000000000000040ULL
+#define HPTE64_R_WIMG           0x0000000000000078ULL
+#define HPTE64_R_C              0x0000000000000080ULL
+#define HPTE64_R_R              0x0000000000000100ULL
+#define HPTE64_R_KEY_LO         0x0000000000000e00ULL
+#define HPTE64_R_KEY(x)         ((((x) & HPTE64_R_KEY_HI) >> 60) | \
+                                 (((x) & HPTE64_R_KEY_LO) >> 9))
+
+#define HPTE64_V_1TB_SEG        0x4000000000000000ULL
+#define HPTE64_V_VRMA_MASK      0x4001ffffff000000ULL
+
+static inline target_ulong ppc_hash64_load_hpte0(CPUPPCState *env,
+                                                 hwaddr pte_offset)
+{
+    if (env->external_htab) {
+        return  ldq_p(env->external_htab + pte_offset);
+    } else {
+        return ldq_phys(env->htab_base + pte_offset);
+    }
+}
+
+static inline target_ulong ppc_hash64_load_hpte1(CPUPPCState *env,
+                                                 hwaddr pte_offset)
+{
+    if (env->external_htab) {
+        return ldq_p(env->external_htab + pte_offset + HASH_PTE_SIZE_64/2);
+    } else {
+        return ldq_phys(env->htab_base + pte_offset + HASH_PTE_SIZE_64/2);
+    }
+}
+
+static inline void ppc_hash64_store_hpte0(CPUPPCState *env,
+                                          hwaddr pte_offset, target_ulong pte0)
+{
+    if (env->external_htab) {
+        stq_p(env->external_htab + pte_offset, pte0);
+    } else {
+        stq_phys(env->htab_base + pte_offset, pte0);
+    }
+}
+
+static inline void ppc_hash64_store_hpte1(CPUPPCState *env,
+                                          hwaddr pte_offset, target_ulong pte1)
+{
+    if (env->external_htab) {
+        stq_p(env->external_htab + pte_offset + HASH_PTE_SIZE_64/2, pte1);
+    } else {
+        stq_phys(env->htab_base + pte_offset + HASH_PTE_SIZE_64/2, pte1);
+    }
+}
+
+typedef struct {
+    uint64_t pte0, pte1;
+} ppc_hash_pte64_t;
+
+#endif /* CONFIG_USER_ONLY */
+
+#endif /* !defined (__MMU_HASH64_H__) */
diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c
index 1cc1c1649a..acf01331f1 100644
--- a/target-ppc/mmu_helper.c
+++ b/target-ppc/mmu_helper.c
@@ -20,10 +20,11 @@
 #include "helper.h"
 #include "sysemu/kvm.h"
 #include "kvm_ppc.h"
+#include "mmu-hash64.h"
+#include "mmu-hash32.h"
 
 //#define DEBUG_MMU
 //#define DEBUG_BATS
-//#define DEBUG_SLB
 //#define DEBUG_SOFTWARE_TLB
 //#define DUMP_PAGE_TABLES
 //#define DEBUG_SOFTWARE_TLB
@@ -49,39 +50,21 @@
 #  define LOG_BATS(...) do { } while (0)
 #endif
 
-#ifdef DEBUG_SLB
-#  define LOG_SLB(...) qemu_log(__VA_ARGS__)
-#else
-#  define LOG_SLB(...) do { } while (0)
-#endif
-
 /*****************************************************************************/
 /* PowerPC MMU emulation */
-#if defined(CONFIG_USER_ONLY)
-int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw,
-                             int mmu_idx)
-{
-    int exception, error_code;
 
-    if (rw == 2) {
-        exception = POWERPC_EXCP_ISI;
-        error_code = 0x40000000;
-    } else {
-        exception = POWERPC_EXCP_DSI;
-        error_code = 0x40000000;
-        if (rw) {
-            error_code |= 0x02000000;
-        }
-        env->spr[SPR_DAR] = address;
-        env->spr[SPR_DSISR] = error_code;
-    }
-    env->exception_index = exception;
-    env->error_code = error_code;
-
-    return 1;
-}
+/* Context used internally during MMU translations */
+typedef struct mmu_ctx_t mmu_ctx_t;
+struct mmu_ctx_t {
+    hwaddr raddr;      /* Real address              */
+    hwaddr eaddr;      /* Effective address         */
+    int prot;                      /* Protection bits           */
+    hwaddr hash[2];    /* Pagetable hash values     */
+    target_ulong ptem;             /* Virtual segment ID | API  */
+    int key;                       /* Access key                */
+    int nx;                        /* Non-execute area          */
+};
 
-#else
 /* Common routines used by software and hardware TLBs emulation */
 static inline int pte_is_valid(target_ulong pte0)
 {
@@ -93,31 +76,14 @@ static inline void pte_invalidate(target_ulong *pte0)
     *pte0 &= ~0x80000000;
 }
 
-#if defined(TARGET_PPC64)
-static inline int pte64_is_valid(target_ulong pte0)
-{
-    return pte0 & 0x0000000000000001ULL ? 1 : 0;
-}
-
-static inline void pte64_invalidate(target_ulong *pte0)
-{
-    *pte0 &= ~0x0000000000000001ULL;
-}
-#endif
-
 #define PTE_PTEM_MASK 0x7FFFFFBF
 #define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B)
-#if defined(TARGET_PPC64)
-#define PTE64_PTEM_MASK 0xFFFFFFFFFFFFFF80ULL
-#define PTE64_CHECK_MASK (TARGET_PAGE_MASK | 0x7F)
-#endif
 
-static inline int pp_check(int key, int pp, int nx)
+static int pp_check(int key, int pp, int nx)
 {
     int access;
 
     /* Compute access rights */
-    /* When pp is 3/7, the result is undefined. Set it to noaccess */
     access = 0;
     if (key == 0) {
         switch (pp) {
@@ -127,14 +93,12 @@ static inline int pp_check(int key, int pp, int nx)
             access |= PAGE_WRITE;
             /* No break here */
         case 0x3:
-        case 0x6:
             access |= PAGE_READ;
             break;
         }
     } else {
         switch (pp) {
         case 0x0:
-        case 0x6:
             access = 0;
             break;
         case 0x1:
@@ -153,7 +117,7 @@ static inline int pp_check(int key, int pp, int nx)
     return access;
 }
 
-static inline int check_prot(int prot, int rw, int access_type)
+static int check_prot(int prot, int rw, int access_type)
 {
     int ret;
 
@@ -180,40 +144,21 @@ static inline int check_prot(int prot, int rw, int access_type)
     return ret;
 }
 
-static inline int pte_check(mmu_ctx_t *ctx, int is_64b, target_ulong pte0,
-                            target_ulong pte1, int h, int rw, int type)
+static inline int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0,
+                                       target_ulong pte1, int h, int rw, int type)
 {
     target_ulong ptem, mmask;
     int access, ret, pteh, ptev, pp;
 
     ret = -1;
     /* Check validity and table match */
-#if defined(TARGET_PPC64)
-    if (is_64b) {
-        ptev = pte64_is_valid(pte0);
-        pteh = (pte0 >> 1) & 1;
-    } else
-#endif
-    {
-        ptev = pte_is_valid(pte0);
-        pteh = (pte0 >> 6) & 1;
-    }
+    ptev = pte_is_valid(pte0);
+    pteh = (pte0 >> 6) & 1;
     if (ptev && h == pteh) {
         /* Check vsid & api */
-#if defined(TARGET_PPC64)
-        if (is_64b) {
-            ptem = pte0 & PTE64_PTEM_MASK;
-            mmask = PTE64_CHECK_MASK;
-            pp = (pte1 & 0x00000003) | ((pte1 >> 61) & 0x00000004);
-            ctx->nx  = (pte1 >> 2) & 1; /* No execute bit */
-            ctx->nx |= (pte1 >> 3) & 1; /* Guarded bit    */
-        } else
-#endif
-        {
-            ptem = pte0 & PTE_PTEM_MASK;
-            mmask = PTE_CHECK_MASK;
-            pp = pte1 & 0x00000003;
-        }
+        ptem = pte0 & PTE_PTEM_MASK;
+        mmask = PTE_CHECK_MASK;
+        pp = pte1 & 0x00000003;
         if (ptem == ctx->ptem) {
             if (ctx->raddr != (hwaddr)-1ULL) {
                 /* all matches should have equal RPN, WIMG & PP */
@@ -241,22 +186,8 @@ static inline int pte_check(mmu_ctx_t *ctx, int is_64b, target_ulong pte0,
     return ret;
 }
 
-static inline int pte32_check(mmu_ctx_t *ctx, target_ulong pte0,
-                              target_ulong pte1, int h, int rw, int type)
-{
-    return pte_check(ctx, 0, pte0, pte1, h, rw, type);
-}
-
-#if defined(TARGET_PPC64)
-static inline int pte64_check(mmu_ctx_t *ctx, target_ulong pte0,
-                              target_ulong pte1, int h, int rw, int type)
-{
-    return pte_check(ctx, 1, pte0, pte1, h, rw, type);
-}
-#endif
-
-static inline int pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p,
-                                   int ret, int rw)
+static int pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p,
+                            int ret, int rw)
 {
     int store = 0;
 
@@ -392,7 +323,7 @@ static inline int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx,
                   pte_is_valid(tlb->pte0) ? "valid" : "inval",
                   tlb->EPN, eaddr, tlb->pte1,
                   rw ? 'S' : 'L', access_type == ACCESS_CODE ? 'I' : 'D');
-        switch (pte32_check(ctx, tlb->pte0, tlb->pte1, 0, rw, access_type)) {
+        switch (ppc6xx_tlb_pte_check(ctx, tlb->pte0, tlb->pte1, 0, rw, access_type)) {
         case -3:
             /* TLB inconsistency */
             return -1;
@@ -454,34 +385,8 @@ static inline void bat_size_prot(CPUPPCState *env, target_ulong *blp,
     *protp = prot;
 }
 
-static inline void bat_601_size_prot(CPUPPCState *env, target_ulong *blp,
-                                     int *validp, int *protp,
-                                     target_ulong *BATu, target_ulong *BATl)
-{
-    target_ulong bl;
-    int key, pp, valid, prot;
-
-    bl = (*BATl & 0x0000003F) << 17;
-    LOG_BATS("b %02x ==> bl " TARGET_FMT_lx " msk " TARGET_FMT_lx "\n",
-             (uint8_t)(*BATl & 0x0000003F), bl, ~bl);
-    prot = 0;
-    valid = (*BATl >> 6) & 1;
-    if (valid) {
-        pp = *BATu & 0x00000003;
-        if (msr_pr == 0) {
-            key = (*BATu >> 3) & 1;
-        } else {
-            key = (*BATu >> 2) & 1;
-        }
-        prot = pp_check(key, pp, 0);
-    }
-    *blp = bl;
-    *validp = valid;
-    *protp = prot;
-}
-
-static inline int get_bat(CPUPPCState *env, mmu_ctx_t *ctx,
-                          target_ulong virtual, int rw, int type)
+static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx,
+                           target_ulong virtual, int rw, int type)
 {
     target_ulong *BATlt, *BATut, *BATu, *BATl;
     target_ulong BEPIl, BEPIu, bl;
@@ -505,11 +410,7 @@ static inline int get_bat(CPUPPCState *env, mmu_ctx_t *ctx,
         BATl = &BATlt[i];
         BEPIu = *BATu & 0xF0000000;
         BEPIl = *BATu & 0x0FFE0000;
-        if (unlikely(env->mmu_model == POWERPC_MMU_601)) {
-            bat_601_size_prot(env, &bl, &valid, &prot, BATu, BATl);
-        } else {
-            bat_size_prot(env, &bl, &valid, &prot, BATu, BATl);
-        }
+        bat_size_prot(env, &bl, &valid, &prot, BATu, BATl);
         LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx
                  " BATl " TARGET_FMT_lx "\n", __func__,
                  type == ACCESS_CODE ? 'I' : 'D', i, virtual, *BATu, *BATl);
@@ -556,332 +457,35 @@ static inline int get_bat(CPUPPCState *env, mmu_ctx_t *ctx,
     return ret;
 }
 
-static inline hwaddr get_pteg_offset(CPUPPCState *env,
-                                                 hwaddr hash,
-                                                 int pte_size)
-{
-    return (hash * pte_size * 8) & env->htab_mask;
-}
-
-/* PTE table lookup */
-static inline int find_pte2(CPUPPCState *env, mmu_ctx_t *ctx, int is_64b, int h,
-                            int rw, int type, int target_page_bits)
-{
-    hwaddr pteg_off;
-    target_ulong pte0, pte1;
-    int i, good = -1;
-    int ret, r;
-
-    ret = -1; /* No entry found */
-    pteg_off = get_pteg_offset(env, ctx->hash[h],
-                               is_64b ? HASH_PTE_SIZE_64 : HASH_PTE_SIZE_32);
-    for (i = 0; i < 8; i++) {
-#if defined(TARGET_PPC64)
-        if (is_64b) {
-            if (env->external_htab) {
-                pte0 = ldq_p(env->external_htab + pteg_off + (i * 16));
-                pte1 = ldq_p(env->external_htab + pteg_off + (i * 16) + 8);
-            } else {
-                pte0 = ldq_phys(env->htab_base + pteg_off + (i * 16));
-                pte1 = ldq_phys(env->htab_base + pteg_off + (i * 16) + 8);
-            }
-
-            r = pte64_check(ctx, pte0, pte1, h, rw, type);
-            LOG_MMU("Load pte from %016" HWADDR_PRIx " => " TARGET_FMT_lx " "
-                    TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n",
-                    pteg_off + (i * 16), pte0, pte1, (int)(pte0 & 1), h,
-                    (int)((pte0 >> 1) & 1), ctx->ptem);
-        } else
-#endif
-        {
-            if (env->external_htab) {
-                pte0 = ldl_p(env->external_htab + pteg_off + (i * 8));
-                pte1 = ldl_p(env->external_htab + pteg_off + (i * 8) + 4);
-            } else {
-                pte0 = ldl_phys(env->htab_base + pteg_off + (i * 8));
-                pte1 = ldl_phys(env->htab_base + pteg_off + (i * 8) + 4);
-            }
-            r = pte32_check(ctx, pte0, pte1, h, rw, type);
-            LOG_MMU("Load pte from %08" HWADDR_PRIx " => " TARGET_FMT_lx " "
-                    TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n",
-                    pteg_off + (i * 8), pte0, pte1, (int)(pte0 >> 31), h,
-                    (int)((pte0 >> 6) & 1), ctx->ptem);
-        }
-        switch (r) {
-        case -3:
-            /* PTE inconsistency */
-            return -1;
-        case -2:
-            /* Access violation */
-            ret = -2;
-            good = i;
-            break;
-        case -1:
-        default:
-            /* No PTE match */
-            break;
-        case 0:
-            /* access granted */
-            /* XXX: we should go on looping to check all PTEs consistency
-             *      but if we can speed-up the whole thing as the
-             *      result would be undefined if PTEs are not consistent.
-             */
-            ret = 0;
-            good = i;
-            goto done;
-        }
-    }
-    if (good != -1) {
-    done:
-        LOG_MMU("found PTE at addr %08" HWADDR_PRIx " prot=%01x ret=%d\n",
-                ctx->raddr, ctx->prot, ret);
-        /* Update page flags */
-        pte1 = ctx->raddr;
-        if (pte_update_flags(ctx, &pte1, ret, rw) == 1) {
-#if defined(TARGET_PPC64)
-            if (is_64b) {
-                if (env->external_htab) {
-                    stq_p(env->external_htab + pteg_off + (good * 16) + 8,
-                          pte1);
-                } else {
-                    stq_phys_notdirty(env->htab_base + pteg_off +
-                                      (good * 16) + 8, pte1);
-                }
-            } else
-#endif
-            {
-                if (env->external_htab) {
-                    stl_p(env->external_htab + pteg_off + (good * 8) + 4,
-                          pte1);
-                } else {
-                    stl_phys_notdirty(env->htab_base + pteg_off +
-                                      (good * 8) + 4, pte1);
-                }
-            }
-        }
-    }
-
-    /* We have a TLB that saves 4K pages, so let's
-     * split a huge page to 4k chunks */
-    if (target_page_bits != TARGET_PAGE_BITS) {
-        ctx->raddr |= (ctx->eaddr & ((1 << target_page_bits) - 1))
-                      & TARGET_PAGE_MASK;
-    }
-    return ret;
-}
-
-static inline int find_pte(CPUPPCState *env, mmu_ctx_t *ctx, int h, int rw,
-                           int type, int target_page_bits)
-{
-#if defined(TARGET_PPC64)
-    if (env->mmu_model & POWERPC_MMU_64) {
-        return find_pte2(env, ctx, 1, h, rw, type, target_page_bits);
-    }
-#endif
-
-    return find_pte2(env, ctx, 0, h, rw, type, target_page_bits);
-}
-
-#if defined(TARGET_PPC64)
-static inline ppc_slb_t *slb_lookup(CPUPPCState *env, target_ulong eaddr)
-{
-    uint64_t esid_256M, esid_1T;
-    int n;
-
-    LOG_SLB("%s: eaddr " TARGET_FMT_lx "\n", __func__, eaddr);
-
-    esid_256M = (eaddr & SEGMENT_MASK_256M) | SLB_ESID_V;
-    esid_1T = (eaddr & SEGMENT_MASK_1T) | SLB_ESID_V;
-
-    for (n = 0; n < env->slb_nr; n++) {
-        ppc_slb_t *slb = &env->slb[n];
-
-        LOG_SLB("%s: slot %d %016" PRIx64 " %016"
-                    PRIx64 "\n", __func__, n, slb->esid, slb->vsid);
-        /* We check for 1T matches on all MMUs here - if the MMU
-         * doesn't have 1T segment support, we will have prevented 1T
-         * entries from being inserted in the slbmte code. */
-        if (((slb->esid == esid_256M) &&
-             ((slb->vsid & SLB_VSID_B) == SLB_VSID_B_256M))
-            || ((slb->esid == esid_1T) &&
-                ((slb->vsid & SLB_VSID_B) == SLB_VSID_B_1T))) {
-            return slb;
-        }
-    }
-
-    return NULL;
-}
-
-/*****************************************************************************/
-/* SPR accesses */
-
-void helper_slbia(CPUPPCState *env)
-{
-    int n, do_invalidate;
-
-    do_invalidate = 0;
-    /* XXX: Warning: slbia never invalidates the first segment */
-    for (n = 1; n < env->slb_nr; n++) {
-        ppc_slb_t *slb = &env->slb[n];
-
-        if (slb->esid & SLB_ESID_V) {
-            slb->esid &= ~SLB_ESID_V;
-            /* XXX: given the fact that segment size is 256 MB or 1TB,
-             *      and we still don't have a tlb_flush_mask(env, n, mask)
-             *      in QEMU, we just invalidate all TLBs
-             */
-            do_invalidate = 1;
-        }
-    }
-    if (do_invalidate) {
-        tlb_flush(env, 1);
-    }
-}
-
-void helper_slbie(CPUPPCState *env, target_ulong addr)
-{
-    ppc_slb_t *slb;
-
-    slb = slb_lookup(env, addr);
-    if (!slb) {
-        return;
-    }
-
-    if (slb->esid & SLB_ESID_V) {
-        slb->esid &= ~SLB_ESID_V;
-
-        /* XXX: given the fact that segment size is 256 MB or 1TB,
-         *      and we still don't have a tlb_flush_mask(env, n, mask)
-         *      in QEMU, we just invalidate all TLBs
-         */
-        tlb_flush(env, 1);
-    }
-}
-
-int ppc_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs)
-{
-    int slot = rb & 0xfff;
-    ppc_slb_t *slb = &env->slb[slot];
-
-    if (rb & (0x1000 - env->slb_nr)) {
-        return -1; /* Reserved bits set or slot too high */
-    }
-    if (rs & (SLB_VSID_B & ~SLB_VSID_B_1T)) {
-        return -1; /* Bad segment size */
-    }
-    if ((rs & SLB_VSID_B) && !(env->mmu_model & POWERPC_MMU_1TSEG)) {
-        return -1; /* 1T segment on MMU that doesn't support it */
-    }
-
-    /* Mask out the slot number as we store the entry */
-    slb->esid = rb & (SLB_ESID_ESID | SLB_ESID_V);
-    slb->vsid = rs;
-
-    LOG_SLB("%s: %d " TARGET_FMT_lx " - " TARGET_FMT_lx " => %016" PRIx64
-            " %016" PRIx64 "\n", __func__, slot, rb, rs,
-            slb->esid, slb->vsid);
-
-    return 0;
-}
-
-static int ppc_load_slb_esid(CPUPPCState *env, target_ulong rb,
-                             target_ulong *rt)
-{
-    int slot = rb & 0xfff;
-    ppc_slb_t *slb = &env->slb[slot];
-
-    if (slot >= env->slb_nr) {
-        return -1;
-    }
-
-    *rt = slb->esid;
-    return 0;
-}
-
-static int ppc_load_slb_vsid(CPUPPCState *env, target_ulong rb,
-                             target_ulong *rt)
-{
-    int slot = rb & 0xfff;
-    ppc_slb_t *slb = &env->slb[slot];
-
-    if (slot >= env->slb_nr) {
-        return -1;
-    }
-
-    *rt = slb->vsid;
-    return 0;
-}
-#endif /* defined(TARGET_PPC64) */
-
 /* Perform segment based translation */
-static inline int get_segment(CPUPPCState *env, mmu_ctx_t *ctx,
-                              target_ulong eaddr, int rw, int type)
+static inline int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx,
+                                      target_ulong eaddr, int rw, int type)
 {
     hwaddr hash;
     target_ulong vsid;
     int ds, pr, target_page_bits;
-    int ret, ret2;
+    int ret;
+    target_ulong sr, pgidx;
 
     pr = msr_pr;
     ctx->eaddr = eaddr;
-#if defined(TARGET_PPC64)
-    if (env->mmu_model & POWERPC_MMU_64) {
-        ppc_slb_t *slb;
-        target_ulong pageaddr;
-        int segment_bits;
-
-        LOG_MMU("Check SLBs\n");
-        slb = slb_lookup(env, eaddr);
-        if (!slb) {
-            return -5;
-        }
-
-        if (slb->vsid & SLB_VSID_B) {
-            vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT_1T;
-            segment_bits = 40;
-        } else {
-            vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT;
-            segment_bits = 28;
-        }
 
-        target_page_bits = (slb->vsid & SLB_VSID_L)
-            ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS;
-        ctx->key = !!(pr ? (slb->vsid & SLB_VSID_KP)
-                      : (slb->vsid & SLB_VSID_KS));
-        ds = 0;
-        ctx->nx = !!(slb->vsid & SLB_VSID_N);
+    sr = env->sr[eaddr >> 28];
+    ctx->key = (((sr & 0x20000000) && (pr != 0)) ||
+                ((sr & 0x40000000) && (pr == 0))) ? 1 : 0;
+    ds = sr & 0x80000000 ? 1 : 0;
+    ctx->nx = sr & 0x10000000 ? 1 : 0;
+    vsid = sr & 0x00FFFFFF;
+    target_page_bits = TARGET_PAGE_BITS;
+    LOG_MMU("Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip="
+            TARGET_FMT_lx " lr=" TARGET_FMT_lx
+            " ir=%d dr=%d pr=%d %d t=%d\n",
+            eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir,
+            (int)msr_dr, pr != 0 ? 1 : 0, rw, type);
+    pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits;
+    hash = vsid ^ pgidx;
+    ctx->ptem = (vsid << 7) | (pgidx >> 10);
 
-        pageaddr = eaddr & ((1ULL << segment_bits)
-                            - (1ULL << target_page_bits));
-        if (slb->vsid & SLB_VSID_B) {
-            hash = vsid ^ (vsid << 25) ^ (pageaddr >> target_page_bits);
-        } else {
-            hash = vsid ^ (pageaddr >> target_page_bits);
-        }
-        /* Only 5 bits of the page index are used in the AVPN */
-        ctx->ptem = (slb->vsid & SLB_VSID_PTEM) |
-            ((pageaddr >> 16) & ((1ULL << segment_bits) - 0x80));
-    } else
-#endif /* defined(TARGET_PPC64) */
-    {
-        target_ulong sr, pgidx;
-
-        sr = env->sr[eaddr >> 28];
-        ctx->key = (((sr & 0x20000000) && (pr != 0)) ||
-                    ((sr & 0x40000000) && (pr == 0))) ? 1 : 0;
-        ds = sr & 0x80000000 ? 1 : 0;
-        ctx->nx = sr & 0x10000000 ? 1 : 0;
-        vsid = sr & 0x00FFFFFF;
-        target_page_bits = TARGET_PAGE_BITS;
-        LOG_MMU("Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip="
-                TARGET_FMT_lx " lr=" TARGET_FMT_lx
-                " ir=%d dr=%d pr=%d %d t=%d\n",
-                eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir,
-                (int)msr_dr, pr != 0 ? 1 : 0, rw, type);
-        pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits;
-        hash = vsid ^ pgidx;
-        ctx->ptem = (vsid << 7) | (pgidx >> 10);
-    }
     LOG_MMU("pte segment: key=%d ds %d nx %d vsid " TARGET_FMT_lx "\n",
             ctx->key, ds, ctx->nx, vsid);
     ret = -1;
@@ -897,33 +501,8 @@ static inline int get_segment(CPUPPCState *env, mmu_ctx_t *ctx,
 
             /* Initialize real address with an invalid value */
             ctx->raddr = (hwaddr)-1ULL;
-            if (unlikely(env->mmu_model == POWERPC_MMU_SOFT_6xx ||
-                         env->mmu_model == POWERPC_MMU_SOFT_74xx)) {
-                /* Software TLB search */
-                ret = ppc6xx_tlb_check(env, ctx, eaddr, rw, type);
-            } else {
-                LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
-                        " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx
-                        " hash=" TARGET_FMT_plx "\n",
-                        env->htab_base, env->htab_mask, vsid, ctx->ptem,
-                        ctx->hash[0]);
-                /* Primary table lookup */
-                ret = find_pte(env, ctx, 0, rw, type, target_page_bits);
-                if (ret < 0) {
-                    /* Secondary table lookup */
-                    if (eaddr != 0xEFFFFFFF) {
-                        LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
-                                " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx
-                                " hash=" TARGET_FMT_plx "\n", env->htab_base,
-                                env->htab_mask, vsid, ctx->ptem, ctx->hash[1]);
-                    }
-                    ret2 = find_pte(env, ctx, 1, rw, type,
-                                    target_page_bits);
-                    if (ret2 != -1) {
-                        ret = ret2;
-                    }
-                }
-            }
+            /* Software TLB search */
+            ret = ppc6xx_tlb_check(env, ctx, eaddr, rw, type);
 #if defined(DUMP_PAGE_TABLES)
             if (qemu_log_enabled()) {
                 hwaddr curaddr;
@@ -1309,8 +888,8 @@ static hwaddr booke206_tlb_to_page_size(CPUPPCState *env,
 }
 
 /* TLB check function for MAS based SoftTLBs */
-int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb,
-                     hwaddr *raddrp,
+static int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb,
+                            hwaddr *raddrp,
                      target_ulong address, uint32_t pid)
 {
     target_ulong mask;
@@ -1597,28 +1176,6 @@ static void mmubooke206_dump_mmu(FILE *f, fprintf_function cpu_fprintf,
     }
 }
 
-#if defined(TARGET_PPC64)
-static void mmubooks_dump_mmu(FILE *f, fprintf_function cpu_fprintf,
-                              CPUPPCState *env)
-{
-    int i;
-    uint64_t slbe, slbv;
-
-    cpu_synchronize_state(env);
-
-    cpu_fprintf(f, "SLB\tESID\t\t\tVSID\n");
-    for (i = 0; i < env->slb_nr; i++) {
-        slbe = env->slb[i].esid;
-        slbv = env->slb[i].vsid;
-        if (slbe == 0 && slbv == 0) {
-            continue;
-        }
-        cpu_fprintf(f, "%d\t0x%016" PRIx64 "\t0x%016" PRIx64 "\n",
-                    i, slbe, slbv);
-    }
-}
-#endif
-
 void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env)
 {
     switch (env->mmu_model) {
@@ -1632,7 +1189,7 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env)
     case POWERPC_MMU_64B:
     case POWERPC_MMU_2_06:
     case POWERPC_MMU_2_06d:
-        mmubooks_dump_mmu(f, cpu_fprintf, env);
+        dump_slb(f, cpu_fprintf, env);
         break;
 #endif
     default:
@@ -1649,8 +1206,6 @@ static inline int check_physical(CPUPPCState *env, mmu_ctx_t *ctx,
     ctx->prot = PAGE_READ | PAGE_EXEC;
     ret = 0;
     switch (env->mmu_model) {
-    case POWERPC_MMU_32B:
-    case POWERPC_MMU_601:
     case POWERPC_MMU_SOFT_6xx:
     case POWERPC_MMU_SOFT_74xx:
     case POWERPC_MMU_SOFT_4xx:
@@ -1658,16 +1213,7 @@ static inline int check_physical(CPUPPCState *env, mmu_ctx_t *ctx,
     case POWERPC_MMU_BOOKE:
         ctx->prot |= PAGE_WRITE;
         break;
-#if defined(TARGET_PPC64)
-    case POWERPC_MMU_620:
-    case POWERPC_MMU_64B:
-    case POWERPC_MMU_2_06:
-    case POWERPC_MMU_2_06d:
-        /* Real address are 60 bits long */
-        ctx->raddr &= 0x0FFFFFFFFFFFFFFFULL;
-        ctx->prot |= PAGE_WRITE;
-        break;
-#endif
+
     case POWERPC_MMU_SOFT_4xx_Z:
         if (unlikely(msr_pe != 0)) {
             /* 403 family add some particular protections,
@@ -1692,15 +1238,10 @@ static inline int check_physical(CPUPPCState *env, mmu_ctx_t *ctx,
             }
         }
         break;
-    case POWERPC_MMU_MPC8xx:
-        /* XXX: TODO */
-        cpu_abort(env, "MPC8xx MMU model is not implemented\n");
-        break;
-    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");
+        /* Caller's checks mean we should never get here for other models */
+        abort();
         return -1;
     }
 
@@ -1710,71 +1251,62 @@ static inline int check_physical(CPUPPCState *env, mmu_ctx_t *ctx,
 static int get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx,
                                 target_ulong eaddr, int rw, int access_type)
 {
-    int ret;
+    int ret = -1;
+    bool real_mode = (access_type == ACCESS_CODE && msr_ir == 0)
+        || (access_type != ACCESS_CODE && msr_dr == 0);
 
 #if 0
     qemu_log("%s\n", __func__);
 #endif
-    if ((access_type == ACCESS_CODE && msr_ir == 0) ||
-        (access_type != ACCESS_CODE && msr_dr == 0)) {
-        if (env->mmu_model == POWERPC_MMU_BOOKE) {
-            /* The BookE MMU always performs address translation. The
-               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.  */
+
+    switch (env->mmu_model) {
+    case POWERPC_MMU_SOFT_6xx:
+    case POWERPC_MMU_SOFT_74xx:
+        if (real_mode) {
             ret = check_physical(env, ctx, eaddr, rw);
-        }
-    } else {
-        ret = -1;
-        switch (env->mmu_model) {
-        case POWERPC_MMU_32B:
-        case POWERPC_MMU_601:
-        case POWERPC_MMU_SOFT_6xx:
-        case POWERPC_MMU_SOFT_74xx:
+        } else {
             /* Try to find a BAT */
             if (env->nb_BATs != 0) {
-                ret = get_bat(env, ctx, eaddr, rw, access_type);
+                ret = get_bat_6xx_tlb(env, ctx, eaddr, rw, access_type);
             }
-#if defined(TARGET_PPC64)
-        case POWERPC_MMU_620:
-        case POWERPC_MMU_64B:
-        case POWERPC_MMU_2_06:
-        case POWERPC_MMU_2_06d:
-#endif
             if (ret < 0) {
                 /* We didn't match any BAT entry or don't have BATs */
-                ret = get_segment(env, ctx, eaddr, rw, access_type);
+                ret = get_segment_6xx_tlb(env, ctx, eaddr, rw, access_type);
             }
-            break;
-        case POWERPC_MMU_SOFT_4xx:
-        case POWERPC_MMU_SOFT_4xx_Z:
+        }
+        break;
+
+    case POWERPC_MMU_SOFT_4xx:
+    case POWERPC_MMU_SOFT_4xx_Z:
+        if (real_mode) {
+            ret = check_physical(env, ctx, eaddr, rw);
+        } else {
             ret = mmu40x_get_physical_address(env, ctx, eaddr,
                                               rw, access_type);
-            break;
-        case POWERPC_MMU_BOOKE:
-            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,
+        }
+        break;
+    case POWERPC_MMU_BOOKE:
+        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_REAL:
+        break;
+    case POWERPC_MMU_MPC8xx:
+        /* XXX: TODO */
+        cpu_abort(env, "MPC8xx MMU model is not implemented\n");
+        break;
+    case POWERPC_MMU_REAL:
+        if (real_mode) {
+            ret = check_physical(env, ctx, eaddr, rw);
+        } else {
             cpu_abort(env, "PowerPC in real mode do not do any translation\n");
-            return -1;
-        default:
-            cpu_abort(env, "Unknown or invalid MMU model\n");
-            return -1;
         }
+        return -1;
+    default:
+        cpu_abort(env, "Unknown or invalid MMU model\n");
+        return -1;
     }
 #if 0
     qemu_log("%s address " TARGET_FMT_lx " => %d " TARGET_FMT_plx "\n",
@@ -1788,6 +1320,22 @@ hwaddr cpu_get_phys_page_debug(CPUPPCState *env, target_ulong addr)
 {
     mmu_ctx_t ctx;
 
+    switch (env->mmu_model) {
+#if defined(TARGET_PPC64)
+    case POWERPC_MMU_64B:
+    case POWERPC_MMU_2_06:
+    case POWERPC_MMU_2_06d:
+        return ppc_hash64_get_phys_page_debug(env, addr);
+#endif
+
+    case POWERPC_MMU_32B:
+    case POWERPC_MMU_601:
+        return ppc_hash32_get_phys_page_debug(env, addr);
+
+    default:
+        ;
+    }
+
     if (unlikely(get_physical_address(env, &ctx, addr, 0, ACCESS_INT) != 0)) {
         return -1;
     }
@@ -1836,8 +1384,8 @@ static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address,
 }
 
 /* Perform address translation */
-int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw,
-                             int mmu_idx)
+static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address,
+                                    int rw, int mmu_idx)
 {
     mmu_ctx_t ctx;
     int access_type;
@@ -1880,17 +1428,6 @@ int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw,
                     env->spr[SPR_40x_DEAR] = address;
                     env->spr[SPR_40x_ESR] = 0x00000000;
                     break;
-                case POWERPC_MMU_32B:
-                case POWERPC_MMU_601:
-#if defined(TARGET_PPC64)
-                case POWERPC_MMU_620:
-                case POWERPC_MMU_64B:
-                case POWERPC_MMU_2_06:
-                case POWERPC_MMU_2_06d:
-#endif
-                    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 */
@@ -1932,19 +1469,6 @@ int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw,
                 env->exception_index = POWERPC_EXCP_ISI;
                 env->error_code = 0x10000000;
                 break;
-#if defined(TARGET_PPC64)
-            case -5:
-                /* No match in segment table */
-                if (env->mmu_model == POWERPC_MMU_620) {
-                    env->exception_index = POWERPC_EXCP_ISI;
-                    /* XXX: this might be incorrect */
-                    env->error_code = 0x40000000;
-                } else {
-                    env->exception_index = POWERPC_EXCP_ISEG;
-                    env->error_code = 0;
-                }
-                break;
-#endif
             }
         } else {
             switch (ret) {
@@ -1964,9 +1488,9 @@ int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw,
                 tlb_miss:
                     env->error_code |= ctx.key << 19;
                     env->spr[SPR_HASH1] = env->htab_base +
-                        get_pteg_offset(env, ctx.hash[0], HASH_PTE_SIZE_32);
+                        get_pteg_offset32(env, ctx.hash[0]);
                     env->spr[SPR_HASH2] = env->htab_base +
-                        get_pteg_offset(env, ctx.hash[1], HASH_PTE_SIZE_32);
+                        get_pteg_offset32(env, ctx.hash[1]);
                     break;
                 case POWERPC_MMU_SOFT_74xx:
                     if (rw == 1) {
@@ -1992,23 +1516,6 @@ int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw,
                         env->spr[SPR_40x_ESR] = 0x00000000;
                     }
                     break;
-                case POWERPC_MMU_32B:
-                case POWERPC_MMU_601:
-#if defined(TARGET_PPC64)
-                case POWERPC_MMU_620:
-                case POWERPC_MMU_64B:
-                case POWERPC_MMU_2_06:
-                case POWERPC_MMU_2_06d:
-#endif
-                    env->exception_index = POWERPC_EXCP_DSI;
-                    env->error_code = 0;
-                    env->spr[SPR_DAR] = address;
-                    if (rw == 1) {
-                        env->spr[SPR_DSISR] = 0x42000000;
-                    } else {
-                        env->spr[SPR_DSISR] = 0x40000000;
-                    }
-                    break;
                 case POWERPC_MMU_MPC8xx:
                     /* XXX: TODO */
                     cpu_abort(env, "MPC8xx MMU model is not implemented\n");
@@ -2094,26 +1601,6 @@ int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw,
                     break;
                 }
                 break;
-#if defined(TARGET_PPC64)
-            case -5:
-                /* No match in segment table */
-                if (env->mmu_model == POWERPC_MMU_620) {
-                    env->exception_index = POWERPC_EXCP_DSI;
-                    env->error_code = 0;
-                    env->spr[SPR_DAR] = address;
-                    /* XXX: this might be incorrect */
-                    if (rw == 1) {
-                        env->spr[SPR_DSISR] = 0x42000000;
-                    } else {
-                        env->spr[SPR_DSISR] = 0x40000000;
-                    }
-                } else {
-                    env->exception_index = POWERPC_EXCP_DSEG;
-                    env->error_code = 0;
-                    env->spr[SPR_DAR] = address;
-                }
-                break;
-#endif
             }
         }
 #if 0
@@ -2326,7 +1813,6 @@ void ppc_tlb_invalidate_all(CPUPPCState *env)
     case POWERPC_MMU_32B:
     case POWERPC_MMU_601:
 #if defined(TARGET_PPC64)
-    case POWERPC_MMU_620:
     case POWERPC_MMU_64B:
     case POWERPC_MMU_2_06:
     case POWERPC_MMU_2_06d:
@@ -2396,7 +1882,6 @@ void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr)
         tlb_flush_page(env, addr | (0xF << 28));
         break;
 #if defined(TARGET_PPC64)
-    case POWERPC_MMU_620:
     case POWERPC_MMU_64B:
     case POWERPC_MMU_2_06:
     case POWERPC_MMU_2_06d:
@@ -2420,16 +1905,6 @@ void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr)
 
 /*****************************************************************************/
 /* Special registers manipulation */
-#if defined(TARGET_PPC64)
-void ppc_store_asr(CPUPPCState *env, target_ulong value)
-{
-    if (env->asr != value) {
-        env->asr = value;
-        tlb_flush(env, 1);
-    }
-}
-#endif
-
 void ppc_store_sdr1(CPUPPCState *env, target_ulong value)
 {
     LOG_MMU("%s: " TARGET_FMT_lx "\n", __func__, value);
@@ -2511,41 +1986,6 @@ void helper_store_sr(CPUPPCState *env, target_ulong srnum, target_ulong value)
 #endif
     }
 }
-#endif /* !defined(CONFIG_USER_ONLY) */
-
-#if !defined(CONFIG_USER_ONLY)
-/* SLB management */
-#if defined(TARGET_PPC64)
-void helper_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs)
-{
-    if (ppc_store_slb(env, rb, rs) < 0) {
-        helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM,
-                                   POWERPC_EXCP_INVAL);
-    }
-}
-
-target_ulong helper_load_slb_esid(CPUPPCState *env, target_ulong rb)
-{
-    target_ulong rt = 0;
-
-    if (ppc_load_slb_esid(env, rb, &rt) < 0) {
-        helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM,
-                                   POWERPC_EXCP_INVAL);
-    }
-    return rt;
-}
-
-target_ulong helper_load_slb_vsid(CPUPPCState *env, target_ulong rb)
-{
-    target_ulong rt = 0;
-
-    if (ppc_load_slb_vsid(env, rb, &rt) < 0) {
-        helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM,
-                                   POWERPC_EXCP_INVAL);
-    }
-    return rt;
-}
-#endif /* defined(TARGET_PPC64) */
 
 /* TLB management */
 void helper_tlbia(CPUPPCState *env)
@@ -3321,4 +2761,45 @@ void helper_booke206_tlbflush(CPUPPCState *env, uint32_t type)
 
     booke206_flush_tlb(env, flags, 1);
 }
-#endif
+
+
+/*****************************************************************************/
+
+#define MMUSUFFIX _mmu
+
+#define SHIFT 0
+#include "exec/softmmu_template.h"
+
+#define SHIFT 1
+#include "exec/softmmu_template.h"
+
+#define SHIFT 2
+#include "exec/softmmu_template.h"
+
+#define SHIFT 3
+#include "exec/softmmu_template.h"
+
+/* try to fill the TLB and return an exception if error. If retaddr is
+   NULL, it means that the function was called in C code (i.e. not
+   from generated code or from helper.c) */
+/* XXX: fix it to restore all registers */
+void tlb_fill(CPUPPCState *env, target_ulong addr, int is_write, int mmu_idx,
+              uintptr_t retaddr)
+{
+    CPUState *cpu = ENV_GET_CPU(env);
+    PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
+    int ret;
+
+    if (pcc->handle_mmu_fault) {
+        ret = pcc->handle_mmu_fault(env, addr, is_write, mmu_idx);
+    } else {
+        ret = cpu_ppc_handle_mmu_fault(env, addr, is_write, mmu_idx);
+    }
+    if (unlikely(ret != 0)) {
+        if (likely(retaddr)) {
+            /* now we have a real cpu fault */
+            cpu_restore_state(env, retaddr);
+        }
+        helper_raise_exception_err(env, env->exception_index, env->error_code);
+    }
+}
diff --git a/target-ppc/translate.c b/target-ppc/translate.c
index 380a884131..5e741d1ab4 100644
--- a/target-ppc/translate.c
+++ b/target-ppc/translate.c
@@ -204,6 +204,13 @@ typedef struct DisasContext {
     int singlestep_enabled;
 } DisasContext;
 
+/* True when active word size < size of target_long.  */
+#ifdef TARGET_PPC64
+# define NARROW_MODE(C)  (!(C)->sf_mode)
+#else
+# define NARROW_MODE(C)  0
+#endif
+
 struct opc_handler_t {
     /* invalid bits for instruction 1 (Rc(opcode) == 0) */
     uint32_t inval1;
@@ -260,12 +267,10 @@ static inline void gen_set_access_type(DisasContext *ctx, int access_type)
 
 static inline void gen_update_nip(DisasContext *ctx, target_ulong nip)
 {
-#if defined(TARGET_PPC64)
-    if (ctx->sf_mode)
-        tcg_gen_movi_tl(cpu_nip, nip);
-    else
-#endif
-        tcg_gen_movi_tl(cpu_nip, (uint32_t)nip);
+    if (NARROW_MODE(ctx)) {
+        nip = (uint32_t)nip;
+    }
+    tcg_gen_movi_tl(cpu_nip, nip);
 }
 
 static inline void gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t error)
@@ -627,7 +632,6 @@ static inline void gen_op_cmpi(TCGv arg0, target_ulong arg1, int s, int crf)
     tcg_temp_free(t0);
 }
 
-#if defined(TARGET_PPC64)
 static inline void gen_op_cmp32(TCGv arg0, TCGv arg1, int s, int crf)
 {
     TCGv t0, t1;
@@ -651,68 +655,62 @@ static inline void gen_op_cmpi32(TCGv arg0, target_ulong arg1, int s, int crf)
     gen_op_cmp32(arg0, t0, s, crf);
     tcg_temp_free(t0);
 }
-#endif
 
 static inline void gen_set_Rc0(DisasContext *ctx, TCGv reg)
 {
-#if defined(TARGET_PPC64)
-    if (!(ctx->sf_mode))
+    if (NARROW_MODE(ctx)) {
         gen_op_cmpi32(reg, 0, 1, 0);
-    else
-#endif
+    } else {
         gen_op_cmpi(reg, 0, 1, 0);
+    }
 }
 
 /* cmp */
 static void gen_cmp(DisasContext *ctx)
 {
-#if defined(TARGET_PPC64)
-    if (!(ctx->sf_mode && (ctx->opcode & 0x00200000)))
+    if (NARROW_MODE(ctx) || !(ctx->opcode & 0x00200000)) {
         gen_op_cmp32(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)],
                      1, crfD(ctx->opcode));
-    else
-#endif
+    } else {
         gen_op_cmp(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)],
                    1, crfD(ctx->opcode));
+    }
 }
 
 /* cmpi */
 static void gen_cmpi(DisasContext *ctx)
 {
-#if defined(TARGET_PPC64)
-    if (!(ctx->sf_mode && (ctx->opcode & 0x00200000)))
+    if (NARROW_MODE(ctx) || !(ctx->opcode & 0x00200000)) {
         gen_op_cmpi32(cpu_gpr[rA(ctx->opcode)], SIMM(ctx->opcode),
                       1, crfD(ctx->opcode));
-    else
-#endif
+    } else {
         gen_op_cmpi(cpu_gpr[rA(ctx->opcode)], SIMM(ctx->opcode),
                     1, crfD(ctx->opcode));
+    }
 }
 
 /* cmpl */
 static void gen_cmpl(DisasContext *ctx)
 {
-#if defined(TARGET_PPC64)
-    if (!(ctx->sf_mode && (ctx->opcode & 0x00200000)))
+    if (NARROW_MODE(ctx) || !(ctx->opcode & 0x00200000)) {
         gen_op_cmp32(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)],
                      0, crfD(ctx->opcode));
-    else
-#endif
+    } else {
         gen_op_cmp(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)],
                    0, crfD(ctx->opcode));
+    }
 }
 
 /* cmpli */
 static void gen_cmpli(DisasContext *ctx)
 {
-#if defined(TARGET_PPC64)
-    if (!(ctx->sf_mode && (ctx->opcode & 0x00200000)))
+    if (NARROW_MODE(ctx) || !(ctx->opcode & 0x00200000)) {
         gen_op_cmpi32(cpu_gpr[rA(ctx->opcode)], UIMM(ctx->opcode),
                       0, crfD(ctx->opcode));
-    else
-#endif
+    } else {
         gen_op_cmpi(cpu_gpr[rA(ctx->opcode)], UIMM(ctx->opcode),
                     0, crfD(ctx->opcode));
+    }
 }
 
 /* isel (PowerPC 2.03 specification) */
@@ -756,11 +754,9 @@ static inline void gen_op_arith_compute_ov(DisasContext *ctx, TCGv arg0,
         tcg_gen_andc_tl(cpu_ov, cpu_ov, t0);
     }
     tcg_temp_free(t0);
-#if defined(TARGET_PPC64)
-    if (!ctx->sf_mode) {
+    if (NARROW_MODE(ctx)) {
         tcg_gen_ext32s_tl(cpu_ov, cpu_ov);
     }
-#endif
     tcg_gen_shri_tl(cpu_ov, cpu_ov, TARGET_LONG_BITS - 1);
     tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov);
 }
@@ -778,14 +774,26 @@ static inline void gen_op_arith_add(DisasContext *ctx, TCGv ret, TCGv arg1,
     }
 
     if (compute_ca) {
-        TCGv zero = tcg_const_tl(0);
-        if (add_ca) {
-            tcg_gen_add2_tl(t0, cpu_ca, arg1, zero, cpu_ca, zero);
-            tcg_gen_add2_tl(t0, cpu_ca, t0, cpu_ca, arg2, zero);
+        if (NARROW_MODE(ctx)) {
+            TCGv t1 = tcg_temp_new();
+            tcg_gen_ext32u_tl(t1, arg2);
+            tcg_gen_ext32u_tl(t0, arg1);
+            tcg_gen_add_tl(t0, t0, t1);
+            tcg_temp_free(t1);
+            if (add_ca) {
+                tcg_gen_add_tl(t0, t0, cpu_ca);
+            }
+            tcg_gen_shri_tl(cpu_ca, t0, 32);
         } else {
-            tcg_gen_add2_tl(t0, cpu_ca, arg1, zero, arg2, zero);
+            TCGv zero = tcg_const_tl(0);
+            if (add_ca) {
+                tcg_gen_add2_tl(t0, cpu_ca, arg1, zero, cpu_ca, zero);
+                tcg_gen_add2_tl(t0, cpu_ca, t0, cpu_ca, arg2, zero);
+            } else {
+                tcg_gen_add2_tl(t0, cpu_ca, arg1, zero, arg2, zero);
+            }
+            tcg_temp_free(zero);
         }
-        tcg_temp_free(zero);
     } else {
         tcg_gen_add_tl(t0, arg1, arg2);
         if (add_ca) {
@@ -1114,14 +1122,25 @@ static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1,
 {
     TCGv t0 = ret;
 
-    if (((add_ca && compute_ca) || compute_ov)
-        && (TCGV_EQUAL(ret, arg1) || TCGV_EQUAL(ret, arg2)))  {
+    if (compute_ov && (TCGV_EQUAL(ret, arg1) || TCGV_EQUAL(ret, arg2)))  {
         t0 = tcg_temp_new();
     }
 
-    if (add_ca) {
-        /* dest = ~arg1 + arg2 + ca.  */
-        if (compute_ca) {
+    if (compute_ca) {
+        /* dest = ~arg1 + arg2 [+ ca].  */
+        if (NARROW_MODE(ctx)) {
+            TCGv inv1 = tcg_temp_new();
+            tcg_gen_not_tl(inv1, arg1);
+            tcg_gen_ext32u_tl(t0, arg2);
+            tcg_gen_ext32u_tl(inv1, inv1);
+            if (add_ca) {
+                tcg_gen_add_tl(t0, t0, cpu_ca);
+            } else {
+                tcg_gen_addi_tl(t0, t0, 1);
+            }
+            tcg_gen_add_tl(t0, t0, inv1);
+            tcg_gen_shri_tl(cpu_ca, t0, 32);
+        } else if (add_ca) {
             TCGv zero, inv1 = tcg_temp_new();
             tcg_gen_not_tl(inv1, arg1);
             zero = tcg_const_tl(0);
@@ -1130,14 +1149,16 @@ static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1,
             tcg_temp_free(zero);
             tcg_temp_free(inv1);
         } else {
+            tcg_gen_setcond_tl(TCG_COND_GEU, cpu_ca, arg2, arg1);
             tcg_gen_sub_tl(t0, arg2, arg1);
-            tcg_gen_add_tl(t0, t0, cpu_ca);
-            tcg_gen_subi_tl(t0, t0, 1);
         }
+    } else if (add_ca) {
+        /* Since we're ignoring carry-out, we can simplify the
+           standard ~arg1 + arg2 + ca to arg2 - arg1 + ca - 1.  */
+        tcg_gen_sub_tl(t0, arg2, arg1);
+        tcg_gen_add_tl(t0, t0, cpu_ca);
+        tcg_gen_subi_tl(t0, t0, 1);
     } else {
-        if (compute_ca) {
-            tcg_gen_setcond_tl(TCG_COND_GEU, cpu_ca, arg2, arg1);
-        }
         tcg_gen_sub_tl(t0, arg2, arg1);
     }
 
@@ -2311,45 +2332,37 @@ static inline void gen_addr_imm_index(DisasContext *ctx, TCGv EA,
 
     simm &= ~maskl;
     if (rA(ctx->opcode) == 0) {
-#if defined(TARGET_PPC64)
-        if (!ctx->sf_mode) {
-            tcg_gen_movi_tl(EA, (uint32_t)simm);
-        } else
-#endif
+        if (NARROW_MODE(ctx)) {
+            simm = (uint32_t)simm;
+        }
         tcg_gen_movi_tl(EA, simm);
     } else if (likely(simm != 0)) {
         tcg_gen_addi_tl(EA, cpu_gpr[rA(ctx->opcode)], simm);
-#if defined(TARGET_PPC64)
-        if (!ctx->sf_mode) {
+        if (NARROW_MODE(ctx)) {
             tcg_gen_ext32u_tl(EA, EA);
         }
-#endif
     } else {
-#if defined(TARGET_PPC64)
-        if (!ctx->sf_mode) {
+        if (NARROW_MODE(ctx)) {
             tcg_gen_ext32u_tl(EA, cpu_gpr[rA(ctx->opcode)]);
-        } else
-#endif
-        tcg_gen_mov_tl(EA, cpu_gpr[rA(ctx->opcode)]);
+        } else {
+            tcg_gen_mov_tl(EA, cpu_gpr[rA(ctx->opcode)]);
+        }
     }
 }
 
 static inline void gen_addr_reg_index(DisasContext *ctx, TCGv EA)
 {
     if (rA(ctx->opcode) == 0) {
-#if defined(TARGET_PPC64)
-        if (!ctx->sf_mode) {
+        if (NARROW_MODE(ctx)) {
             tcg_gen_ext32u_tl(EA, cpu_gpr[rB(ctx->opcode)]);
-        } else
-#endif
-        tcg_gen_mov_tl(EA, cpu_gpr[rB(ctx->opcode)]);
+        } else {
+            tcg_gen_mov_tl(EA, cpu_gpr[rB(ctx->opcode)]);
+        }
     } else {
         tcg_gen_add_tl(EA, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]);
-#if defined(TARGET_PPC64)
-        if (!ctx->sf_mode) {
+        if (NARROW_MODE(ctx)) {
             tcg_gen_ext32u_tl(EA, EA);
         }
-#endif
     }
 }
 
@@ -2357,13 +2370,10 @@ static inline void gen_addr_register(DisasContext *ctx, TCGv EA)
 {
     if (rA(ctx->opcode) == 0) {
         tcg_gen_movi_tl(EA, 0);
+    } else if (NARROW_MODE(ctx)) {
+        tcg_gen_ext32u_tl(EA, cpu_gpr[rA(ctx->opcode)]);
     } else {
-#if defined(TARGET_PPC64)
-        if (!ctx->sf_mode) {
-            tcg_gen_ext32u_tl(EA, cpu_gpr[rA(ctx->opcode)]);
-        } else
-#endif
-            tcg_gen_mov_tl(EA, cpu_gpr[rA(ctx->opcode)]);
+        tcg_gen_mov_tl(EA, cpu_gpr[rA(ctx->opcode)]);
     }
 }
 
@@ -2371,11 +2381,9 @@ static inline void gen_addr_add(DisasContext *ctx, TCGv ret, TCGv arg1,
                                 target_long val)
 {
     tcg_gen_addi_tl(ret, arg1, val);
-#if defined(TARGET_PPC64)
-    if (!ctx->sf_mode) {
+    if (NARROW_MODE(ctx)) {
         tcg_gen_ext32u_tl(ret, ret);
     }
-#endif
 }
 
 static inline void gen_check_align(DisasContext *ctx, TCGv EA, int mask)
@@ -3320,10 +3328,9 @@ static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
 {
     TranslationBlock *tb;
     tb = ctx->tb;
-#if defined(TARGET_PPC64)
-    if (!ctx->sf_mode)
+    if (NARROW_MODE(ctx)) {
         dest = (uint32_t) dest;
-#endif
+    }
     if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) &&
         likely(!ctx->singlestep_enabled)) {
         tcg_gen_goto_tb(n);
@@ -3351,12 +3358,10 @@ static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
 
 static inline void gen_setlr(DisasContext *ctx, target_ulong nip)
 {
-#if defined(TARGET_PPC64)
-    if (ctx->sf_mode == 0)
-        tcg_gen_movi_tl(cpu_lr, (uint32_t)nip);
-    else
-#endif
-        tcg_gen_movi_tl(cpu_lr, nip);
+    if (NARROW_MODE(ctx)) {
+        nip = (uint32_t)nip;
+    }
+    tcg_gen_movi_tl(cpu_lr, nip);
 }
 
 /* b ba bl bla */
@@ -3366,18 +3371,16 @@ static void gen_b(DisasContext *ctx)
 
     ctx->exception = POWERPC_EXCP_BRANCH;
     /* sign extend LI */
-#if defined(TARGET_PPC64)
-    if (ctx->sf_mode)
-        li = ((int64_t)LI(ctx->opcode) << 38) >> 38;
-    else
-#endif
-        li = ((int32_t)LI(ctx->opcode) << 6) >> 6;
-    if (likely(AA(ctx->opcode) == 0))
+    li = LI(ctx->opcode);
+    li = (li ^ 0x02000000) - 0x02000000;
+    if (likely(AA(ctx->opcode) == 0)) {
         target = ctx->nip + li - 4;
-    else
+    } else {
         target = li;
-    if (LK(ctx->opcode))
+    }
+    if (LK(ctx->opcode)) {
         gen_setlr(ctx, ctx->nip);
+    }
     gen_update_cfar(ctx, ctx->nip);
     gen_goto_tb(ctx, 0, target);
 }
@@ -3413,12 +3416,11 @@ static inline void gen_bcond(DisasContext *ctx, int type)
             return;
         }
         tcg_gen_subi_tl(cpu_ctr, cpu_ctr, 1);
-#if defined(TARGET_PPC64)
-        if (!ctx->sf_mode)
+        if (NARROW_MODE(ctx)) {
             tcg_gen_ext32u_tl(temp, cpu_ctr);
-        else
-#endif
+        } else {
             tcg_gen_mov_tl(temp, cpu_ctr);
+        }
         if (bo & 0x2) {
             tcg_gen_brcondi_tl(TCG_COND_NE, temp, 0, l1);
         } else {
@@ -3452,20 +3454,14 @@ static inline void gen_bcond(DisasContext *ctx, int type)
         gen_set_label(l1);
         gen_goto_tb(ctx, 1, ctx->nip);
     } else {
-#if defined(TARGET_PPC64)
-        if (!(ctx->sf_mode))
+        if (NARROW_MODE(ctx)) {
             tcg_gen_andi_tl(cpu_nip, target, (uint32_t)~3);
-        else
-#endif
+        } else {
             tcg_gen_andi_tl(cpu_nip, target, ~3);
+        }
         tcg_gen_exit_tb(0);
         gen_set_label(l1);
-#if defined(TARGET_PPC64)
-        if (!(ctx->sf_mode))
-            tcg_gen_movi_tl(cpu_nip, (uint32_t)ctx->nip);
-        else
-#endif
-            tcg_gen_movi_tl(cpu_nip, ctx->nip);
+        gen_update_nip(ctx, ctx->nip);
         tcg_gen_exit_tb(0);
     }
 }
@@ -4324,15 +4320,14 @@ static void gen_tlbie(DisasContext *ctx)
         gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
         return;
     }
-#if defined(TARGET_PPC64)
-    if (!ctx->sf_mode) {
+    if (NARROW_MODE(ctx)) {
         TCGv t0 = tcg_temp_new();
         tcg_gen_ext32u_tl(t0, cpu_gpr[rB(ctx->opcode)]);
         gen_helper_tlbie(cpu_env, t0);
         tcg_temp_free(t0);
-    } else
-#endif
+    } else {
         gen_helper_tlbie(cpu_env, cpu_gpr[rB(ctx->opcode)]);
+    }
 #endif
 }
 
@@ -7577,11 +7572,9 @@ static inline void gen_addr_spe_imm_index(DisasContext *ctx, TCGv EA, int sh)
         tcg_gen_movi_tl(EA, uimm << sh);
     } else {
         tcg_gen_addi_tl(EA, cpu_gpr[rA(ctx->opcode)], uimm << sh);
-#if defined(TARGET_PPC64)
-        if (!ctx->sf_mode) {
+        if (NARROW_MODE(ctx)) {
             tcg_gen_ext32u_tl(EA, EA);
         }
-#endif
     }
 }
 
@@ -9428,7 +9421,6 @@ void cpu_dump_state (CPUPPCState *env, FILE *f, fprintf_function cpu_fprintf,
     case POWERPC_MMU_SOFT_6xx:
     case POWERPC_MMU_SOFT_74xx:
 #if defined(TARGET_PPC64)
-    case POWERPC_MMU_620:
     case POWERPC_MMU_64B:
 #endif
         cpu_fprintf(f, " SDR1 " TARGET_FMT_lx "\n", env->spr[SPR_SDR1]);
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
index 15eebe9177..781170fb05 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -25,6 +25,8 @@
 #include "sysemu/arch_init.h"
 #include "sysemu/cpus.h"
 #include "cpu-models.h"
+#include "mmu-hash32.h"
+#include "mmu-hash64.h"
 
 //#define PPC_DUMP_CPU
 //#define PPC_DEBUG_SPR
@@ -365,7 +367,6 @@ static void spr_write_sdr1 (void *opaque, int sprn, int gprn)
 }
 
 /* 64 bits PowerPC specific SPRs */
-/* ASR */
 #if defined(TARGET_PPC64)
 static void spr_read_hior (void *opaque, int gprn, int sprn)
 {
@@ -379,16 +380,6 @@ static void spr_write_hior (void *opaque, int sprn, int gprn)
     tcg_gen_st_tl(t0, cpu_env, offsetof(CPUPPCState, excp_prefix));
     tcg_temp_free(t0);
 }
-
-static void spr_read_asr (void *opaque, int gprn, int sprn)
-{
-    tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, asr));
-}
-
-static void spr_write_asr (void *opaque, int sprn, int gprn)
-{
-    gen_helper_store_asr(cpu_env, cpu_gpr[gprn]);
-}
 #endif
 #endif
 
@@ -1028,6 +1019,54 @@ static void gen_spr_7xx (CPUPPCState *env)
                  0x00000000);
 }
 
+#ifdef TARGET_PPC64
+#ifndef CONFIG_USER_ONLY
+static void spr_read_uamr (void *opaque, int gprn, int sprn)
+{
+    gen_load_spr(cpu_gpr[gprn], SPR_AMR);
+    spr_load_dump_spr(SPR_AMR);
+}
+
+static void spr_write_uamr (void *opaque, int sprn, int gprn)
+{
+    gen_store_spr(SPR_AMR, cpu_gpr[gprn]);
+    spr_store_dump_spr(SPR_AMR);
+}
+
+static void spr_write_uamr_pr (void *opaque, int sprn, int gprn)
+{
+    TCGv t0 = tcg_temp_new();
+
+    gen_load_spr(t0, SPR_UAMOR);
+    tcg_gen_and_tl(t0, t0, cpu_gpr[gprn]);
+    gen_store_spr(SPR_AMR, t0);
+    spr_store_dump_spr(SPR_AMR);
+}
+#endif /* CONFIG_USER_ONLY */
+
+static void gen_spr_amr (CPUPPCState *env)
+{
+#ifndef CONFIG_USER_ONLY
+    /* Virtual Page Class Key protection */
+    /* The AMR is accessible either via SPR 13 or SPR 29.  13 is
+     * userspace accessible, 29 is privileged.  So we only need to set
+     * the kvm ONE_REG id on one of them, we use 29 */
+    spr_register(env, SPR_UAMR, "UAMR",
+                 &spr_read_uamr, &spr_write_uamr_pr,
+                 &spr_read_uamr, &spr_write_uamr,
+                 0);
+    spr_register_kvm(env, SPR_AMR, "AMR",
+                     SPR_NOACCESS, SPR_NOACCESS,
+                     &spr_read_generic, &spr_write_generic,
+                     KVM_REG_PPC_AMR, 0xffffffffffffffffULL);
+    spr_register_kvm(env, SPR_UAMOR, "UAMOR",
+                     SPR_NOACCESS, SPR_NOACCESS,
+                     &spr_read_generic, &spr_write_generic,
+                     KVM_REG_PPC_UAMOR, 0);
+#endif /* !CONFIG_USER_ONLY */
+}
+#endif /* TARGET_PPC64 */
+
 static void gen_spr_thrm (CPUPPCState *env)
 {
     /* Thermal management */
@@ -2151,173 +2190,6 @@ static void gen_spr_compress (CPUPPCState *env)
                  0x00000000);
 }
 
-#if defined (TARGET_PPC64)
-/* SPR specific to PowerPC 620 */
-static void gen_spr_620 (CPUPPCState *env)
-{
-    /* Processor identification */
-    spr_register(env, SPR_PIR, "PIR",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_pir,
-                 0x00000000);
-    spr_register(env, SPR_ASR, "ASR",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_asr, &spr_write_asr,
-                 0x00000000);
-    /* Breakpoints */
-    /* XXX : not implemented */
-    spr_register(env, SPR_IABR, "IABR",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-    /* XXX : not implemented */
-    spr_register(env, SPR_DABR, "DABR",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-    /* XXX : not implemented */
-    spr_register(env, SPR_SIAR, "SIAR",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, SPR_NOACCESS,
-                 0x00000000);
-    /* XXX : not implemented */
-    spr_register(env, SPR_SDA, "SDA",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, SPR_NOACCESS,
-                 0x00000000);
-    /* XXX : not implemented */
-    spr_register(env, SPR_620_PMC1R, "PMC1",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, SPR_NOACCESS,
-                 0x00000000);
-    spr_register(env, SPR_620_PMC1W, "PMC1",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                  SPR_NOACCESS, &spr_write_generic,
-                 0x00000000);
-    /* XXX : not implemented */
-    spr_register(env, SPR_620_PMC2R, "PMC2",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, SPR_NOACCESS,
-                 0x00000000);
-    spr_register(env, SPR_620_PMC2W, "PMC2",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                  SPR_NOACCESS, &spr_write_generic,
-                 0x00000000);
-    /* XXX : not implemented */
-    spr_register(env, SPR_620_MMCR0R, "MMCR0",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, SPR_NOACCESS,
-                 0x00000000);
-    spr_register(env, SPR_620_MMCR0W, "MMCR0",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                  SPR_NOACCESS, &spr_write_generic,
-                 0x00000000);
-    /* External access control */
-    /* XXX : not implemented */
-    spr_register(env, SPR_EAR, "EAR",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-#if 0 // XXX: check this
-    /* XXX : not implemented */
-    spr_register(env, SPR_620_PMR0, "PMR0",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-    /* XXX : not implemented */
-    spr_register(env, SPR_620_PMR1, "PMR1",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-    /* XXX : not implemented */
-    spr_register(env, SPR_620_PMR2, "PMR2",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-    /* XXX : not implemented */
-    spr_register(env, SPR_620_PMR3, "PMR3",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-    /* XXX : not implemented */
-    spr_register(env, SPR_620_PMR4, "PMR4",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-    /* XXX : not implemented */
-    spr_register(env, SPR_620_PMR5, "PMR5",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-    /* XXX : not implemented */
-    spr_register(env, SPR_620_PMR6, "PMR6",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-    /* XXX : not implemented */
-    spr_register(env, SPR_620_PMR7, "PMR7",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-    /* XXX : not implemented */
-    spr_register(env, SPR_620_PMR8, "PMR8",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-    /* XXX : not implemented */
-    spr_register(env, SPR_620_PMR9, "PMR9",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-    /* XXX : not implemented */
-    spr_register(env, SPR_620_PMRA, "PMR10",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-    /* XXX : not implemented */
-    spr_register(env, SPR_620_PMRB, "PMR11",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-    /* XXX : not implemented */
-    spr_register(env, SPR_620_PMRC, "PMR12",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-    /* XXX : not implemented */
-    spr_register(env, SPR_620_PMRD, "PMR13",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-    /* XXX : not implemented */
-    spr_register(env, SPR_620_PMRE, "PMR14",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-    /* XXX : not implemented */
-    spr_register(env, SPR_620_PMRF, "PMR15",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-#endif
-    /* XXX : not implemented */
-    spr_register(env, SPR_620_BUSCSR, "BUSCSR",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-    /* XXX : not implemented */
-    spr_register(env, SPR_620_L2CR, "L2CR",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-    /* XXX : not implemented */
-    spr_register(env, SPR_620_L2SR, "L2SR",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-}
-#endif /* defined (TARGET_PPC64) */
-
 static void gen_spr_5xx_8xx (CPUPPCState *env)
 {
     /* Exception processing */
@@ -2993,31 +2865,6 @@ static void init_excp_604 (CPUPPCState *env)
 #endif
 }
 
-#if defined(TARGET_PPC64)
-static void init_excp_620 (CPUPPCState *env)
-{
-#if !defined(CONFIG_USER_ONLY)
-    env->excp_vectors[POWERPC_EXCP_RESET]    = 0x00000100;
-    env->excp_vectors[POWERPC_EXCP_MCHECK]   = 0x00000200;
-    env->excp_vectors[POWERPC_EXCP_DSI]      = 0x00000300;
-    env->excp_vectors[POWERPC_EXCP_ISI]      = 0x00000400;
-    env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500;
-    env->excp_vectors[POWERPC_EXCP_ALIGN]    = 0x00000600;
-    env->excp_vectors[POWERPC_EXCP_PROGRAM]  = 0x00000700;
-    env->excp_vectors[POWERPC_EXCP_FPU]      = 0x00000800;
-    env->excp_vectors[POWERPC_EXCP_DECR]     = 0x00000900;
-    env->excp_vectors[POWERPC_EXCP_SYSCALL]  = 0x00000C00;
-    env->excp_vectors[POWERPC_EXCP_TRACE]    = 0x00000D00;
-    env->excp_vectors[POWERPC_EXCP_PERFM]    = 0x00000F00;
-    env->excp_vectors[POWERPC_EXCP_IABR]     = 0x00001300;
-    env->excp_vectors[POWERPC_EXCP_SMI]      = 0x00001400;
-    env->hreset_excp_prefix = 0xFFF00000UL;
-    /* Hardware reset vector */
-    env->hreset_vector = 0x0000000000000100ULL;
-#endif
-}
-#endif /* defined(TARGET_PPC64) */
-
 static void init_excp_7x0 (CPUPPCState *env)
 {
 #if !defined(CONFIG_USER_ONLY)
@@ -4951,6 +4798,9 @@ POWERPC_FAMILY(601)(ObjectClass *oc, void *data)
     pcc->insns_flags2 = PPC_NONE;
     pcc->msr_mask = 0x000000000000FD70ULL;
     pcc->mmu_model = POWERPC_MMU_601;
+#if defined(CONFIG_SOFTMMU)
+    pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
+#endif
     pcc->excp_model = POWERPC_EXCP_601;
     pcc->bus_model = PPC_FLAGS_INPUT_6xx;
     pcc->bfd_mach = bfd_mach_ppc_601;
@@ -4985,7 +4835,9 @@ POWERPC_FAMILY(601v)(ObjectClass *oc, void *data)
     pcc->insns_flags2 = PPC_NONE;
     pcc->msr_mask = 0x000000000000FD70ULL;
     pcc->mmu_model = POWERPC_MMU_601;
-    pcc->excp_model = POWERPC_EXCP_601;
+#if defined(CONFIG_SOFTMMU)
+    pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
+#endif
     pcc->bus_model = PPC_FLAGS_INPUT_6xx;
     pcc->bfd_mach = bfd_mach_ppc_601;
     pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK;
@@ -5192,6 +5044,9 @@ POWERPC_FAMILY(604)(ObjectClass *oc, void *data)
     pcc->insns_flags2 = PPC_NONE;
     pcc->msr_mask = 0x000000000005FF77ULL;
     pcc->mmu_model = POWERPC_MMU_32B;
+#if defined(CONFIG_SOFTMMU)
+    pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
+#endif
     pcc->excp_model = POWERPC_EXCP_604;
     pcc->bus_model = PPC_FLAGS_INPUT_6xx;
     pcc->bfd_mach = bfd_mach_ppc_604;
@@ -5258,6 +5113,9 @@ POWERPC_FAMILY(604E)(ObjectClass *oc, void *data)
     pcc->insns_flags2 = PPC_NONE;
     pcc->msr_mask = 0x000000000005FF77ULL;
     pcc->mmu_model = POWERPC_MMU_32B;
+#if defined(CONFIG_SOFTMMU)
+    pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
+#endif
     pcc->excp_model = POWERPC_EXCP_604;
     pcc->bus_model = PPC_FLAGS_INPUT_6xx;
     pcc->bfd_mach = bfd_mach_ppc_604;
@@ -5311,6 +5169,9 @@ POWERPC_FAMILY(740)(ObjectClass *oc, void *data)
     pcc->insns_flags2 = PPC_NONE;
     pcc->msr_mask = 0x000000000005FF77ULL;
     pcc->mmu_model = POWERPC_MMU_32B;
+#if defined(CONFIG_SOFTMMU)
+    pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
+#endif
     pcc->excp_model = POWERPC_EXCP_7x0;
     pcc->bus_model = PPC_FLAGS_INPUT_6xx;
     pcc->bfd_mach = bfd_mach_ppc_750;
@@ -5372,6 +5233,9 @@ POWERPC_FAMILY(750)(ObjectClass *oc, void *data)
     pcc->insns_flags2 = PPC_NONE;
     pcc->msr_mask = 0x000000000005FF77ULL;
     pcc->mmu_model = POWERPC_MMU_32B;
+#if defined(CONFIG_SOFTMMU)
+    pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
+#endif
     pcc->excp_model = POWERPC_EXCP_7x0;
     pcc->bus_model = PPC_FLAGS_INPUT_6xx;
     pcc->bfd_mach = bfd_mach_ppc_750;
@@ -5556,6 +5420,9 @@ POWERPC_FAMILY(750cl)(ObjectClass *oc, void *data)
     pcc->insns_flags2 = PPC_NONE;
     pcc->msr_mask = 0x000000000005FF77ULL;
     pcc->mmu_model = POWERPC_MMU_32B;
+#if defined(CONFIG_SOFTMMU)
+    pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
+#endif
     pcc->excp_model = POWERPC_EXCP_7x0;
     pcc->bus_model = PPC_FLAGS_INPUT_6xx;
     pcc->bfd_mach = bfd_mach_ppc_750;
@@ -5621,6 +5488,9 @@ POWERPC_FAMILY(750cx)(ObjectClass *oc, void *data)
     pcc->insns_flags2 = PPC_NONE;
     pcc->msr_mask = 0x000000000005FF77ULL;
     pcc->mmu_model = POWERPC_MMU_32B;
+#if defined(CONFIG_SOFTMMU)
+    pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
+#endif
     pcc->excp_model = POWERPC_EXCP_7x0;
     pcc->bus_model = PPC_FLAGS_INPUT_6xx;
     pcc->bfd_mach = bfd_mach_ppc_750;
@@ -5691,6 +5561,9 @@ POWERPC_FAMILY(750fx)(ObjectClass *oc, void *data)
     pcc->insns_flags2 = PPC_NONE;
     pcc->msr_mask = 0x000000000005FF77ULL;
     pcc->mmu_model = POWERPC_MMU_32B;
+#if defined(CONFIG_SOFTMMU)
+    pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
+#endif
     pcc->excp_model = POWERPC_EXCP_7x0;
     pcc->bus_model = PPC_FLAGS_INPUT_6xx;
     pcc->bfd_mach = bfd_mach_ppc_750;
@@ -5761,6 +5634,9 @@ POWERPC_FAMILY(750gx)(ObjectClass *oc, void *data)
     pcc->insns_flags2 = PPC_NONE;
     pcc->msr_mask = 0x000000000005FF77ULL;
     pcc->mmu_model = POWERPC_MMU_32B;
+#if defined(CONFIG_SOFTMMU)
+    pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
+#endif
     pcc->excp_model = POWERPC_EXCP_7x0;
     pcc->bus_model = PPC_FLAGS_INPUT_6xx;
     pcc->bfd_mach = bfd_mach_ppc_750;
@@ -5953,6 +5829,9 @@ POWERPC_FAMILY(7400)(ObjectClass *oc, void *data)
     pcc->insns_flags2 = PPC_NONE;
     pcc->msr_mask = 0x000000000205FF77ULL;
     pcc->mmu_model = POWERPC_MMU_32B;
+#if defined(CONFIG_SOFTMMU)
+    pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
+#endif
     pcc->excp_model = POWERPC_EXCP_74xx;
     pcc->bus_model = PPC_FLAGS_INPUT_6xx;
     pcc->bfd_mach = bfd_mach_ppc_7400;
@@ -6019,6 +5898,9 @@ POWERPC_FAMILY(7410)(ObjectClass *oc, void *data)
     pcc->insns_flags2 = PPC_NONE;
     pcc->msr_mask = 0x000000000205FF77ULL;
     pcc->mmu_model = POWERPC_MMU_32B;
+#if defined(CONFIG_SOFTMMU)
+    pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
+#endif
     pcc->excp_model = POWERPC_EXCP_74xx;
     pcc->bus_model = PPC_FLAGS_INPUT_6xx;
     pcc->bfd_mach = bfd_mach_ppc_7400;
@@ -6725,6 +6607,9 @@ POWERPC_FAMILY(970)(ObjectClass *oc, void *data)
     pcc->insns_flags2 = PPC_NONE;
     pcc->msr_mask = 0x900000000204FF36ULL;
     pcc->mmu_model = POWERPC_MMU_64B;
+#if defined(CONFIG_SOFTMMU)
+    pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault;
+#endif
     pcc->excp_model = POWERPC_EXCP_970;
     pcc->bus_model = PPC_FLAGS_INPUT_970;
     pcc->bfd_mach = bfd_mach_ppc64;
@@ -6835,6 +6720,9 @@ POWERPC_FAMILY(970FX)(ObjectClass *oc, void *data)
     pcc->insns_flags2 = PPC_NONE;
     pcc->msr_mask = 0x800000000204FF36ULL;
     pcc->mmu_model = POWERPC_MMU_64B;
+#if defined(CONFIG_SOFTMMU)
+    pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault;
+#endif
     pcc->excp_model = POWERPC_EXCP_970;
     pcc->bus_model = PPC_FLAGS_INPUT_970;
     pcc->bfd_mach = bfd_mach_ppc64;
@@ -6933,6 +6821,9 @@ POWERPC_FAMILY(970GX)(ObjectClass *oc, void *data)
     pcc->insns_flags2 = PPC_NONE;
     pcc->msr_mask = 0x800000000204FF36ULL;
     pcc->mmu_model = POWERPC_MMU_64B;
+#if defined(CONFIG_SOFTMMU)
+    pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault;
+#endif
     pcc->excp_model = POWERPC_EXCP_970;
     pcc->bus_model = PPC_FLAGS_INPUT_970;
     pcc->bfd_mach = bfd_mach_ppc64;
@@ -7031,6 +6922,9 @@ POWERPC_FAMILY(970MP)(ObjectClass *oc, void *data)
     pcc->insns_flags2 = PPC_NONE;
     pcc->msr_mask = 0x900000000204FF36ULL;
     pcc->mmu_model = POWERPC_MMU_64B;
+#if defined(CONFIG_SOFTMMU)
+    pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault;
+#endif
     pcc->excp_model = POWERPC_EXCP_970;
     pcc->bus_model = PPC_FLAGS_INPUT_970;
     pcc->bfd_mach = bfd_mach_ppc64;
@@ -7075,6 +6969,7 @@ static void init_proc_POWER7 (CPUPPCState *env)
                  SPR_NOACCESS, SPR_NOACCESS,
                  &spr_read_generic, SPR_NOACCESS,
                  0x00000000); /* TOFIX */
+    gen_spr_amr(env);
     /* XXX : not implemented */
     spr_register(env, SPR_CTRL, "SPR_CTRLT",
                  SPR_NOACCESS, SPR_NOACCESS,
@@ -7122,6 +7017,9 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
     pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX;
     pcc->msr_mask = 0x800000000204FF36ULL;
     pcc->mmu_model = POWERPC_MMU_2_06;
+#if defined(CONFIG_SOFTMMU)
+    pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault;
+#endif
     pcc->excp_model = POWERPC_EXCP_POWER7;
     pcc->bus_model = PPC_FLAGS_INPUT_POWER7;
     pcc->bfd_mach = bfd_mach_ppc64;
@@ -7129,55 +7027,6 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
                  POWERPC_FLAG_BE | POWERPC_FLAG_PMM |
                  POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR;
 }
-
-static void init_proc_620 (CPUPPCState *env)
-{
-    gen_spr_ne_601(env);
-    gen_spr_620(env);
-    /* Time base */
-    gen_tbl(env);
-    /* Hardware implementation registers */
-    /* XXX : not implemented */
-    spr_register(env, SPR_HID0, "HID0",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-    /* Memory management */
-    gen_low_BATs(env);
-    init_excp_620(env);
-    env->dcache_line_size = 64;
-    env->icache_line_size = 64;
-    /* Allocate hardware IRQ controller */
-    ppc6xx_irq_init(env);
-}
-
-POWERPC_FAMILY(620)(ObjectClass *oc, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(oc);
-    PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
-
-    dc->desc = "PowerPC 620";
-    pcc->init_proc = init_proc_620;
-    pcc->check_pow = check_pow_nocheck; /* Check this */
-    pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB |
-                       PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
-                       PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE |
-                       PPC_FLOAT_STFIWX |
-                       PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ |
-                       PPC_MEM_SYNC | PPC_MEM_EIEIO |
-                       PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
-                       PPC_SEGMENT | PPC_EXTERN |
-                       PPC_64B | PPC_SLBI;
-    pcc->insns_flags2 = PPC_NONE;
-    pcc->msr_mask = 0x800000000005FF77ULL;
-    pcc->mmu_model = POWERPC_MMU_620;
-    pcc->excp_model = POWERPC_EXCP_970;
-    pcc->bus_model = PPC_FLAGS_INPUT_6xx;
-    pcc->bfd_mach = bfd_mach_ppc64;
-    pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE |
-                 POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK;
-}
-
 #endif /* defined (TARGET_PPC64) */
 
 
@@ -7693,7 +7542,7 @@ static int gdb_set_float_reg(CPUPPCState *env, uint8_t *mem_buf, int n)
         return 8;
     }
     if (n == 32) {
-        /* FPSCR not implemented  */
+        helper_store_fpscr(env, ldl_p(mem_buf), 0xffffffff);
         return 4;
     }
     return 0;
@@ -7915,9 +7764,6 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp)
         case POWERPC_MMU_64B:
             mmu_model = "PowerPC 64";
             break;
-        case POWERPC_MMU_620:
-            mmu_model = "PowerPC 620";
-            break;
 #endif
         default:
             mmu_model = "Unknown or invalid";
diff --git a/target-ppc/user_only_helper.c b/target-ppc/user_only_helper.c
new file mode 100644
index 0000000000..56e686efd1
--- /dev/null
+++ b/target-ppc/user_only_helper.c
@@ -0,0 +1,44 @@
+/*
+ *  PowerPC MMU stub handling for user mode emulation
+ *
+ *  Copyright (c) 2003-2007 Jocelyn Mayer
+ *  Copyright (c) 2013 David Gibson, IBM Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cpu.h"
+
+int cpu_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw,
+                         int mmu_idx)
+{
+    int exception, error_code;
+
+    if (rw == 2) {
+        exception = POWERPC_EXCP_ISI;
+        error_code = 0x40000000;
+    } else {
+        exception = POWERPC_EXCP_DSI;
+        error_code = 0x40000000;
+        if (rw) {
+            error_code |= 0x02000000;
+        }
+        env->spr[SPR_DAR] = address;
+        env->spr[SPR_DSISR] = error_code;
+    }
+    env->exception_index = exception;
+    env->error_code = error_code;
+
+    return 1;
+}