]> git.proxmox.com Git - qemu.git/blobdiff - target-ppc/mmu-hash64.c
rng-egd: remove redundant free
[qemu.git] / target-ppc / mmu-hash64.c
index f3223dd38364021023adc6d37edafa8335196ad0..67fc1b5dec5ef7d29e3542d0b3a83b803b2bc751 100644 (file)
 
 #ifdef DEBUG_MMU
 #  define LOG_MMU(...) qemu_log(__VA_ARGS__)
-#  define LOG_MMU_STATE(env) log_cpu_state((env), 0)
+#  define LOG_MMU_STATE(cpu) log_cpu_state((cpu), 0)
 #else
 #  define LOG_MMU(...) do { } while (0)
-#  define LOG_MMU_STATE(...) do { } while (0)
+#  define LOG_MMU_STATE(cpu) do { } while (0)
 #endif
 
 #ifdef DEBUG_SLB
 #  define LOG_SLB(...) do { } while (0)
 #endif
 
-struct mmu_ctx_hash64 {
-    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          */
-};
-
 /*
  * SLB handling
  */
@@ -88,7 +78,7 @@ void dump_slb(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env)
     int i;
     uint64_t slbe, slbv;
 
-    cpu_synchronize_state(env);
+    cpu_synchronize_state(CPU(ppc_env_get_cpu(env)));
 
     cpu_fprintf(f, "SLB\tESID\t\t\tVSID\n");
     for (i = 0; i < env->slb_nr; i++) {
@@ -233,416 +223,324 @@ target_ulong helper_load_slb_vsid(CPUPPCState *env, target_ulong rb)
  * 64-bit hash table MMU handling
  */
 
-#define PTE64_CHECK_MASK (TARGET_PAGE_MASK | 0x7F)
-
-static int ppc_hash64_pp_check(int key, int pp, int nx)
+static int ppc_hash64_pte_prot(CPUPPCState *env,
+                               ppc_slb_t *slb, ppc_hash_pte64_t pte)
 {
-    int access;
+    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);
 
-    /* Compute access rights */
-    /* When pp is 4, 5 or 7, the result is undefined. Set it to noaccess */
-    access = 0;
     if (key == 0) {
         switch (pp) {
         case 0x0:
         case 0x1:
         case 0x2:
-            access |= PAGE_WRITE;
-            /* No break here */
+            prot = PAGE_READ | PAGE_WRITE;
+            break;
+
         case 0x3:
         case 0x6:
-            access |= PAGE_READ;
+            prot = PAGE_READ;
             break;
         }
     } else {
         switch (pp) {
         case 0x0:
         case 0x6:
-            access = 0;
+            prot = 0;
             break;
+
         case 0x1:
         case 0x3:
-            access = PAGE_READ;
+            prot = PAGE_READ;
             break;
+
         case 0x2:
-            access = PAGE_READ | PAGE_WRITE;
+            prot = PAGE_READ | PAGE_WRITE;
             break;
         }
     }
-    if (nx == 0) {
-        access |= PAGE_EXEC;
+
+    /* 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 access;
+    return prot;
 }
 
-static int ppc_hash64_check_prot(int prot, int rw, int access_type)
+static int ppc_hash64_amr_prot(CPUPPCState *env, ppc_hash_pte64_t pte)
 {
-    int ret;
+    int key, amrbits;
+    int prot = PAGE_EXEC;
 
-    if (access_type == ACCESS_CODE) {
-        if (prot & PAGE_EXEC) {
-            ret = 0;
-        } else {
-            ret = -2;
-        }
-    } else if (rw) {
-        if (prot & PAGE_WRITE) {
-            ret = 0;
-        } else {
-            ret = -2;
-        }
-    } else {
-        if (prot & PAGE_READ) {
-            ret = 0;
-        } else {
-            ret = -2;
-        }
+
+    /* Only recent MMUs implement Virtual Page Class Key Protection */
+    if (!(env->mmu_model & POWERPC_MMU_AMR)) {
+        return PAGE_READ | PAGE_WRITE | PAGE_EXEC;
     }
 
-    return ret;
-}
+    key = HPTE64_R_KEY(pte.pte1);
+    amrbits = (env->spr[SPR_AMR] >> 2*(31 - key)) & 0x3;
 
-static int pte64_check(struct mmu_ctx_hash64 *ctx, target_ulong pte0,
-                       target_ulong pte1, int h, int rw, int type)
-{
-    target_ulong mmask;
-    int access, ret, pp;
-
-    ret = -1;
-    /* Check validity and table match */
-    if ((pte0 & HPTE64_V_VALID) && (h == !!(pte0 & HPTE64_V_SECONDARY))) {
-        /* Check vsid & api */
-        mmask = PTE64_CHECK_MASK;
-        pp = (pte1 & HPTE64_R_PP) | ((pte1 & HPTE64_R_PP0) >> 61);
-        /* No execute if either noexec or guarded bits set */
-        ctx->nx = (pte1 & HPTE64_R_N) || (pte1 & HPTE64_R_G);
-        if (HPTE64_V_COMPARE(pte0, ctx->ptem)) {
-            if (ctx->raddr != (hwaddr)-1ULL) {
-                /* all matches should have equal RPN, WIMG & PP */
-                if ((ctx->raddr & mmask) != (pte1 & mmask)) {
-                    qemu_log("Bad RPN/WIMG/PP\n");
-                    return -3;
-                }
-            }
-            /* Compute access rights */
-            access = ppc_hash64_pp_check(ctx->key, pp, ctx->nx);
-            /* Keep the matching PTE informations */
-            ctx->raddr = pte1;
-            ctx->prot = access;
-            ret = ppc_hash64_check_prot(ctx->prot, rw, type);
-            if (ret == 0) {
-                /* Access granted */
-                LOG_MMU("PTE access granted !\n");
-            } else {
-                /* Access right violation */
-                LOG_MMU("PTE access rejected\n");
-            }
-        }
+    /* 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 ret;
+    return prot;
 }
 
-static int ppc_hash64_pte_update_flags(struct mmu_ctx_hash64 *ctx,
-                                       target_ulong *pte1p,
-                                       int ret, int rw)
+static hwaddr ppc_hash64_pteg_search(CPUPPCState *env, hwaddr pteg_off,
+                                     bool secondary, target_ulong ptem,
+                                     ppc_hash_pte64_t *pte)
 {
-    int store = 0;
-
-    /* Update page flags */
-    if (!(*pte1p & HPTE64_R_R)) {
-        /* Update accessed flag */
-        *pte1p |= HPTE64_R_R;
-        store = 1;
-    }
-    if (!(*pte1p & HPTE64_R_C)) {
-        if (rw == 1 && ret == 0) {
-            /* Update changed flag */
-            *pte1p |= HPTE64_R_C;
-            store = 1;
-        } else {
-            /* Force page fault for first write access */
-            ctx->prot &= ~PAGE_WRITE;
+    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 store;
+    return -1;
 }
 
-/* PTE table lookup */
-static int find_pte64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, int h,
-                      int rw, int type, int target_page_bits)
+static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env,
+                                     ppc_slb_t *slb, target_ulong eaddr,
+                                     ppc_hash_pte64_t *pte)
 {
-    hwaddr pteg_off;
-    target_ulong pte0, pte1;
-    int i, good = -1;
-    int ret, r;
+    hwaddr pteg_off, pte_offset;
+    hwaddr hash;
+    uint64_t vsid, epnshift, epnmask, epn, ptem;
 
-    ret = -1; /* No entry found */
-    pteg_off = (ctx->hash[h] * HASH_PTEG_SIZE_64) & env->htab_mask;
-    for (i = 0; i < HPTES_PER_GROUP; i++) {
-        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);
-        }
+    /* 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);
 
-        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);
-        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 (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);
     }
-    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 (ppc_hash64_pte_update_flags(ctx, &pte1, ret, rw) == 1) {
-            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);
-            }
-        }
+    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);
     }
 
-    /* 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;
+    return pte_offset;
 }
 
-static int get_segment64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx,
-                         target_ulong eaddr, int rw, int type)
+static hwaddr ppc_hash64_pte_raddr(ppc_slb_t *slb, ppc_hash_pte64_t pte,
+                                   target_ulong eaddr)
 {
-    hwaddr hash;
-    target_ulong vsid;
-    int pr, target_page_bits;
-    int ret, ret2;
+    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);
+}
 
-    pr = msr_pr;
-    ctx->eaddr = eaddr;
+int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong eaddr,
+                                int rwx, int mmu_idx)
+{
     ppc_slb_t *slb;
-    target_ulong pageaddr;
-    int segment_bits;
+    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;
+    }
 
-    LOG_MMU("Check SLBs\n");
+    /* 2. Translation is on, so look up the SLB */
     slb = slb_lookup(env, eaddr);
+
     if (!slb) {
-        return -5;
+        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;
     }
 
-    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;
+    /* 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;
     }
 
-    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));
-    ctx->nx = !!(slb->vsid & SLB_VSID_N);
+    /* 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);
 
-    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));
-
-    LOG_MMU("pte segment: key=%d nx %d vsid " TARGET_FMT_lx "\n",
-            ctx->key, ctx->nx, vsid);
-    ret = -1;
-
-    /* Check if instruction fetch is allowed, if needed */
-    if (type != ACCESS_CODE || ctx->nx == 0) {
-        /* 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);
-        ctx->hash[0] = hash;
-        ctx->hash[1] = ~hash;
-
-        /* Initialize real address with an invalid value */
-        ctx->raddr = (hwaddr)-1ULL;
-        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_pte64(env, ctx, 0, rw, type, target_page_bits);
-        if (ret < 0) {
-            /* Secondary table 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, ctx->ptem, ctx->hash[1]);
-            ret2 = find_pte64(env, ctx, 1, rw, type, target_page_bits);
-            if (ret2 != -1) {
-                ret = ret2;
+    /* 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;
         }
-    } else {
-        LOG_MMU("No access allowed\n");
-        ret = -3;
+        return 1;
     }
 
-    return ret;
-}
+    LOG_MMU("PTE access granted !\n");
 
-static int ppc_hash64_get_physical_address(CPUPPCState *env,
-                                           struct mmu_ctx_hash64 *ctx,
-                                           target_ulong eaddr, int rw,
-                                           int access_type)
-{
-    bool real_mode = (access_type == ACCESS_CODE && msr_ir == 0)
-        || (access_type != ACCESS_CODE && msr_dr == 0);
+    /* 6. Update PTE referenced and changed bits if necessary */
 
-    if (real_mode) {
-        ctx->raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL;
-        ctx->prot = PAGE_READ | PAGE_EXEC | PAGE_WRITE;
-        return 0;
+    new_pte1 = pte.pte1 | HPTE64_R_R; /* set referenced bit */
+    if (rwx == 1) {
+        new_pte1 |= HPTE64_R_C; /* set changed (dirty) bit */
     } else {
-        return get_segment64(env, ctx, eaddr, rw, access_type);
+        /* 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)
 {
-    struct mmu_ctx_hash64 ctx;
+    ppc_slb_t *slb;
+    hwaddr pte_offset;
+    ppc_hash_pte64_t pte;
 
-    if (unlikely(ppc_hash64_get_physical_address(env, &ctx, addr, 0, ACCESS_INT)
-                 != 0)) {
-        return -1;
+    if (msr_dr == 0) {
+        /* In real mode the top 4 effective address bits are ignored */
+        return addr & 0x0FFFFFFFFFFFFFFFULL;
     }
 
-    return ctx.raddr & TARGET_PAGE_MASK;
-}
+    slb = slb_lookup(env, addr);
+    if (!slb) {
+        return -1;
+    }
 
-int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw,
-                                int mmu_idx)
-{
-    struct mmu_ctx_hash64 ctx;
-    int access_type;
-    int ret = 0;
-
-    if (rw == 2) {
-        /* code access */
-        rw = 0;
-        access_type = ACCESS_CODE;
-    } else {
-        /* data access */
-        access_type = env->access_type;
-    }
-    ret = ppc_hash64_get_physical_address(env, &ctx, address, rw, access_type);
-    if (ret == 0) {
-        tlb_set_page(env, address & TARGET_PAGE_MASK,
-                     ctx.raddr & TARGET_PAGE_MASK, ctx.prot,
-                     mmu_idx, TARGET_PAGE_SIZE);
-        ret = 0;
-    } else if (ret < 0) {
-        LOG_MMU_STATE(env);
-        if (access_type == ACCESS_CODE) {
-            switch (ret) {
-            case -1:
-                env->exception_index = POWERPC_EXCP_ISI;
-                env->error_code = 0x40000000;
-                break;
-            case -2:
-                /* Access rights violation */
-                env->exception_index = POWERPC_EXCP_ISI;
-                env->error_code = 0x08000000;
-                break;
-            case -3:
-                /* No execute protection violation */
-                env->exception_index = POWERPC_EXCP_ISI;
-                env->error_code = 0x10000000;
-                break;
-            case -5:
-                /* No match in segment table */
-                env->exception_index = POWERPC_EXCP_ISEG;
-                env->error_code = 0;
-                break;
-            }
-        } else {
-            switch (ret) {
-            case -1:
-                /* No matches in page tables or TLB */
-                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 -2:
-                /* Access rights violation */
-                env->exception_index = POWERPC_EXCP_DSI;
-                env->error_code = 0;
-                env->spr[SPR_DAR] = address;
-                if (rw == 1) {
-                    env->spr[SPR_DSISR] = 0x0A000000;
-                } else {
-                    env->spr[SPR_DSISR] = 0x08000000;
-                }
-                break;
-            case -5:
-                /* No match in segment table */
-                env->exception_index = POWERPC_EXCP_DSEG;
-                env->error_code = 0;
-                env->spr[SPR_DAR] = address;
-                break;
-            }
-        }
-#if 0
-        printf("%s: set exception to %d %02x\n", __func__,
-               env->exception, env->error_code);
-#endif
-        ret = 1;
+    pte_offset = ppc_hash64_htab_lookup(env, slb, addr, &pte);
+    if (pte_offset == -1) {
+        return -1;
     }
 
-    return ret;
+    return ppc_hash64_pte_raddr(slb, pte, addr) & TARGET_PAGE_MASK;
 }