diff mbox

[1/2] ppc: Add proper real mode translation support

Message ID 1467096514-18905-2-git-send-email-clg@kaod.org
State New
Headers show

Commit Message

Cédric Le Goater June 28, 2016, 6:48 a.m. UTC
From: Benjamin Herrenschmidt <benh@kernel.crashing.org>

This adds proper support for translating real mode addresses based
on the combination of HV and LPCR bits. This handles HRMOR offset
for hypervisor real mode, and both RMA and VRMA modes for guest
real mode. PAPR mode adjusts the offsets appropriately to match the
RMA used in TCG, but we need to limit to the max supported by the
implementation (16G).

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
[clg: fixed checkpatch.pl errors ]
Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/ppc/spapr.c              |   7 +++
 target-ppc/mmu-hash64.c     | 146 ++++++++++++++++++++++++++++++++++++++------
 target-ppc/mmu-hash64.h     |   1 +
 target-ppc/translate_init.c |  10 ++-
 4 files changed, 144 insertions(+), 20 deletions(-)

Comments

David Gibson June 29, 2016, 2:41 a.m. UTC | #1
On Tue, Jun 28, 2016 at 08:48:33AM +0200, Cédric Le Goater wrote:
> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> 
> This adds proper support for translating real mode addresses based
> on the combination of HV and LPCR bits. This handles HRMOR offset
> for hypervisor real mode, and both RMA and VRMA modes for guest
> real mode. PAPR mode adjusts the offsets appropriately to match the
> RMA used in TCG, but we need to limit to the max supported by the
> implementation (16G).
> 
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> [clg: fixed checkpatch.pl errors ]
> Signed-off-by: Cédric Le Goater <clg@kaod.org>

This looks correct and I've applied it.  There are a couple of
possible cleanups which might be a good idea to follow up with though.


> ---
>  hw/ppc/spapr.c              |   7 +++
>  target-ppc/mmu-hash64.c     | 146 ++++++++++++++++++++++++++++++++++++++------
>  target-ppc/mmu-hash64.h     |   1 +
>  target-ppc/translate_init.c |  10 ++-
>  4 files changed, 144 insertions(+), 20 deletions(-)
> 
> diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
> index d26b4c26ed10..53ab1f84fb11 100644
> --- a/hw/ppc/spapr.c
> +++ b/hw/ppc/spapr.c
> @@ -1770,6 +1770,13 @@ static void ppc_spapr_init(MachineState *machine)
>              spapr->vrma_adjust = 1;
>              spapr->rma_size = MIN(spapr->rma_size, 0x10000000);
>          }
> +
> +        /* Actually we don't support unbounded RMA anymore since we
> +         * added proper emulation of HV mode. The max we can get is
> +         * 16G which also happens to be what we configure for PAPR
> +         * mode so make sure we don't do anything bigger than that
> +         */
> +        spapr->rma_size = MIN(spapr->rma_size, 0x400000000ull);

#1 - Instead of the various KVM / non-KVM cases here, it might be
simpler to just always clamp the RMA to 256MiB.

>      }
>  
>      if (spapr->rma_size > node0_size) {
> diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c
> index 6d6f26c92957..ed353b2d1539 100644
> --- a/target-ppc/mmu-hash64.c
> +++ b/target-ppc/mmu-hash64.c
> @@ -653,13 +653,41 @@ static void ppc_hash64_set_dsi(CPUState *cs, CPUPPCState *env, uint64_t dar,
>      env->error_code = 0;
>  }
>  
> +static int64_t ppc_hash64_get_rmls(CPUPPCState *env)
> +{
> +    uint64_t lpcr = env->spr[SPR_LPCR];
> +
> +    /*
> +     * This is the full 4 bits encoding of POWER8. Previous
> +     * CPUs only support a subset of these but the filtering
> +     * is done when writing LPCR
> +     */
> +    switch ((lpcr & LPCR_RMLS) >> LPCR_RMLS_SHIFT) {
> +    case 0x8: /* 32MB */
> +        return 0x2000000ull;
> +    case 0x3: /* 64MB */
> +        return 0x4000000ull;
> +    case 0x7: /* 128MB */
> +        return 0x8000000ull;
> +    case 0x4: /* 256MB */
> +        return 0x10000000ull;
> +    case 0x2: /* 1GB */
> +        return 0x40000000ull;
> +    case 0x1: /* 16GB */
> +        return 0x400000000ull;
> +    default:
> +        /* What to do here ??? */
> +        return 0;
> +    }
> +}
>  
>  int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
>                                  int rwx, int mmu_idx)
>  {
>      CPUState *cs = CPU(cpu);
>      CPUPPCState *env = &cpu->env;
> -    ppc_slb_t *slb;
> +    ppc_slb_t *slb_ptr;
> +    ppc_slb_t slb;
>      unsigned apshift;
>      hwaddr pte_offset;
>      ppc_hash_pte64_t pte;
> @@ -670,11 +698,53 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
>  
>      assert((rwx == 0) || (rwx == 1) || (rwx == 2));
>  
> +    /* Note on LPCR usage: 970 uses HID4, but our special variant
> +     * of store_spr copies relevant fields into env->spr[SPR_LPCR].
> +     * Similarily we filter unimplemented bits when storing into
> +     * LPCR depending on the MMU version. This code can thus just
> +     * use the LPCR "as-is".
> +     */
> +
>      /* 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 */
> +        /* Translation is supposedly "off"  */
> +        /* In real mode the top 4 effective address bits are (mostly) ignored */
>          raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL;
> +
> +        /* In HV mode, add HRMOR if top EA bit is clear */
> +        if (msr_hv) {
> +            if (!(eaddr >> 63)) {
> +                raddr |= env->spr[SPR_HRMOR];
> +            }
> +        } else {
> +            /* Otherwise, check VPM for RMA vs VRMA */
> +            if (env->spr[SPR_LPCR] & LPCR_VPM0) {
> +                uint32_t vrmasd;
> +                /* VRMA, we make up an SLB entry */
> +                slb.vsid = SLB_VSID_VRMA;
> +                vrmasd = (env->spr[SPR_LPCR] & LPCR_VRMASD) >>
> +                    LPCR_VRMASD_SHIFT;
> +                slb.vsid |= (vrmasd << 4) & (SLB_VSID_L | SLB_VSID_LP);
> +                slb.esid = SLB_ESID_V;
> +                goto skip_slb;
> +            }
> +            /* RMA. Check bounds in RMLS */
> +            if (raddr < ppc_hash64_get_rmls(env)) {
> +                raddr |= env->spr[SPR_RMOR];
> +            } else {
> +                /* The access failed, generate the approriate interrupt */
> +                if (rwx == 2) {
> +                    ppc_hash64_set_isi(cs, env, 0x08000000);
> +                } else {
> +                    dsisr = 0x08000000;
> +                    if (rwx == 1) {
> +                        dsisr |= 0x02000000;
> +                    }
> +                    ppc_hash64_set_dsi(cs, env, eaddr, dsisr);
> +                }
> +                return 1;
> +            }
> +        }
>          tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
>                       PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx,
>                       TARGET_PAGE_SIZE);
> @@ -682,9 +752,8 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
>      }
>  
>      /* 2. Translation is on, so look up the SLB */
> -    slb = slb_lookup(cpu, eaddr);
> -
> -    if (!slb) {
> +    slb_ptr = slb_lookup(cpu, eaddr);
> +    if (!slb_ptr) {
>          if (rwx == 2) {
>              cs->exception_index = POWERPC_EXCP_ISEG;
>              env->error_code = 0;
> @@ -696,14 +765,29 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
>          return 1;
>      }
>  
> +    /* We grab a local copy because we can modify it (or get a
> +     * pre-cooked one from the VRMA code
> +     */
> +    slb = *slb_ptr;
> +
> +    /* 2.5 Clamp L||LP in ISL mode */
> +    if (env->spr[SPR_LPCR] & LPCR_ISL) {
> +        slb.vsid &= ~SLB_VSID_LLP_MASK;
> +    }
> +
>      /* 3. Check for segment level no-execute violation */
> -    if ((rwx == 2) && (slb->vsid & SLB_VSID_N)) {
> +    if ((rwx == 2) && (slb.vsid & SLB_VSID_N)) {
>          ppc_hash64_set_isi(cs, env, 0x10000000);
>          return 1;
>      }
>  
> +    /* We go straight here for VRMA translations as none of the
> +     * above applies in that case
> +     */
> + skip_slb:
> +
>      /* 4. Locate the PTE in the hash table */
> -    pte_offset = ppc_hash64_htab_lookup(cpu, slb, eaddr, &pte);
> +    pte_offset = ppc_hash64_htab_lookup(cpu, &slb, eaddr, &pte);
>      if (pte_offset == -1) {
>          dsisr = 0x40000000;
>          if (rwx == 2) {
> @@ -720,7 +804,7 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
>                  "found PTE at offset %08" HWADDR_PRIx "\n", pte_offset);
>  
>      /* Validate page size encoding */
> -    apshift = hpte_page_shift(slb->sps, pte.pte0, pte.pte1);
> +    apshift = hpte_page_shift(slb.sps, pte.pte0, pte.pte1);
>      if (!apshift) {
>          error_report("Bad page size encoding in HPTE 0x%"PRIx64" - 0x%"PRIx64
>                       " @ 0x%"HWADDR_PRIx, pte.pte0, pte.pte1, pte_offset);
> @@ -733,7 +817,7 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
>  
>      /* 5. Check access permissions */
>  
> -    pp_prot = ppc_hash64_pte_prot(cpu, slb, pte);
> +    pp_prot = ppc_hash64_pte_prot(cpu, &slb, pte);
>      amr_prot = ppc_hash64_amr_prot(cpu, pte);
>      prot = pp_prot & amr_prot;
>  
> @@ -789,27 +873,51 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
>  hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr)
>  {
>      CPUPPCState *env = &cpu->env;
> -    ppc_slb_t *slb;
> -    hwaddr pte_offset;
> +    ppc_slb_t slb;
> +    ppc_slb_t *slb_ptr;
> +    hwaddr pte_offset, raddr;
>      ppc_hash_pte64_t pte;
>      unsigned apshift;
>  
> +    /* Handle real mode */
>      if (msr_dr == 0) {
> -        /* In real mode the top 4 effective address bits are ignored */
> -        return addr & 0x0FFFFFFFFFFFFFFFULL;
> -    }
> +        raddr = addr & 0x0FFFFFFFFFFFFFFFULL;
>  
> -    slb = slb_lookup(cpu, addr);
> -    if (!slb) {
> +        /* In HV mode, add HRMOR if top EA bit is clear */
> +        if (msr_hv & !(addr >> 63)) {
> +            return raddr | env->spr[SPR_HRMOR];
> +        }
> +
> +        /* Otherwise, check VPM for RMA vs VRMA */
> +        if (env->spr[SPR_LPCR] & LPCR_VPM0) {
> +            uint32_t vrmasd;
> +
> +            /* VRMA, we make up an SLB entry */
> +            slb.vsid = SLB_VSID_VRMA;
> +            vrmasd = (env->spr[SPR_LPCR] & LPCR_VRMASD) >> LPCR_VRMASD_SHIFT;
> +            slb.vsid |= (vrmasd << 4) & (SLB_VSID_L | SLB_VSID_LP);
> +            slb.esid = SLB_ESID_V;
> +            goto skip_slb;
> +        }
> +        /* RMA. Check bounds in RMLS */
> +        if (raddr < ppc_hash64_get_rmls(env)) {
> +            return raddr | env->spr[SPR_RMOR];
> +        }

Now that the real-mode case is non-trivial, it would be nice if we
could factor out some of this logic from the fault and page_debug
cases into a common helper function.

>          return -1;
>      }
>  
> -    pte_offset = ppc_hash64_htab_lookup(cpu, slb, addr, &pte);
> +    slb_ptr = slb_lookup(cpu, addr);
> +    if (!slb_ptr) {
> +        return -1;
> +    }
> +    slb = *slb_ptr;
> + skip_slb:
> +    pte_offset = ppc_hash64_htab_lookup(cpu, &slb, addr, &pte);
>      if (pte_offset == -1) {
>          return -1;
>      }
>  
> -    apshift = hpte_page_shift(slb->sps, pte.pte0, pte.pte1);
> +    apshift = hpte_page_shift(slb.sps, pte.pte0, pte.pte1);
>      if (!apshift) {
>          return -1;
>      }
> diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h
> index 6423b9f791e7..13ad060cfefb 100644
> --- a/target-ppc/mmu-hash64.h
> +++ b/target-ppc/mmu-hash64.h
> @@ -37,6 +37,7 @@ unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu,
>  #define SLB_VSID_B_256M         0x0000000000000000ULL
>  #define SLB_VSID_B_1T           0x4000000000000000ULL
>  #define SLB_VSID_VSID           0x3FFFFFFFFFFFF000ULL
> +#define SLB_VSID_VRMA           (0x0001FFFFFF000000ULL | SLB_VSID_B_1T)
>  #define SLB_VSID_PTEM           (SLB_VSID_B | SLB_VSID_VSID)
>  #define SLB_VSID_KS             0x0000000000000800ULL
>  #define SLB_VSID_KP             0x0000000000000400ULL
> diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
> index 55d1bfac97c4..4820c0bc99fb 100644
> --- a/target-ppc/translate_init.c
> +++ b/target-ppc/translate_init.c
> @@ -8791,11 +8791,19 @@ void cpu_ppc_set_papr(PowerPCCPU *cpu)
>      /* Set emulated LPCR to not send interrupts to hypervisor. Note that
>       * under KVM, the actual HW LPCR will be set differently by KVM itself,
>       * the settings below ensure proper operations with TCG in absence of
> -     * a real hypervisor
> +     * a real hypervisor.
> +     *
> +     * Clearing VPM0 will also cause us to use RMOR in mmu-hash64.c for
> +     * real mode accesses, which thankfully defaults to 0 and isn't
> +     * accessible in guest mode.
>       */
>      lpcr->default_value &= ~(LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV);
>      lpcr->default_value |= LPCR_LPES0 | LPCR_LPES1;
>  
> +    /* Set RMLS to the max (ie, 16G) */
> +    lpcr->default_value &= ~LPCR_RMLS;
> +    lpcr->default_value |= 1ull << LPCR_RMLS_SHIFT;
> +
>      /* P7 and P8 has slightly different PECE bits, mostly because P8 adds
>       * bit 47 and 48 which are reserved on P7. Here we set them all, which
>       * will work as expected for both implementations
Benjamin Herrenschmidt June 29, 2016, 2:59 a.m. UTC | #2
On Wed, 2016-06-29 at 12:41 +1000, David Gibson wrote:
> > +        /* Actually we don't support unbounded RMA anymore since

> we

> > +         * added proper emulation of HV mode. The max we can get

> is

> > +         * 16G which also happens to be what we configure for PAPR

> > +         * mode so make sure we don't do anything bigger than that

> > +         */

> > +        spapr->rma_size = MIN(spapr->rma_size, 0x400000000ull);

> 

> #1 - Instead of the various KVM / non-KVM cases here, it might be

> simpler to just always clamp the RMA to 256MiB.


That would be sad ... we benefit from having a larger RMA..

Cheers,
Ben.
David Gibson June 29, 2016, 3:05 a.m. UTC | #3
On Wed, Jun 29, 2016 at 12:59:05PM +1000, Benjamin Herrenschmidt wrote:
> On Wed, 2016-06-29 at 12:41 +1000, David Gibson wrote:
> > > +        /* Actually we don't support unbounded RMA anymore since
> > we
> > > +         * added proper emulation of HV mode. The max we can get
> > is
> > > +         * 16G which also happens to be what we configure for PAPR
> > > +         * mode so make sure we don't do anything bigger than that
> > > +         */
> > > +        spapr->rma_size = MIN(spapr->rma_size, 0x400000000ull);
> > 
> > #1 - Instead of the various KVM / non-KVM cases here, it might be
> > simpler to just always clamp the RMA to 256MiB.
> 
> That would be sad ... we benefit from having a larger RMA..

Ah, ok.  Let's leave it as is, then.
diff mbox

Patch

diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index d26b4c26ed10..53ab1f84fb11 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -1770,6 +1770,13 @@  static void ppc_spapr_init(MachineState *machine)
             spapr->vrma_adjust = 1;
             spapr->rma_size = MIN(spapr->rma_size, 0x10000000);
         }
+
+        /* Actually we don't support unbounded RMA anymore since we
+         * added proper emulation of HV mode. The max we can get is
+         * 16G which also happens to be what we configure for PAPR
+         * mode so make sure we don't do anything bigger than that
+         */
+        spapr->rma_size = MIN(spapr->rma_size, 0x400000000ull);
     }
 
     if (spapr->rma_size > node0_size) {
diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c
index 6d6f26c92957..ed353b2d1539 100644
--- a/target-ppc/mmu-hash64.c
+++ b/target-ppc/mmu-hash64.c
@@ -653,13 +653,41 @@  static void ppc_hash64_set_dsi(CPUState *cs, CPUPPCState *env, uint64_t dar,
     env->error_code = 0;
 }
 
+static int64_t ppc_hash64_get_rmls(CPUPPCState *env)
+{
+    uint64_t lpcr = env->spr[SPR_LPCR];
+
+    /*
+     * This is the full 4 bits encoding of POWER8. Previous
+     * CPUs only support a subset of these but the filtering
+     * is done when writing LPCR
+     */
+    switch ((lpcr & LPCR_RMLS) >> LPCR_RMLS_SHIFT) {
+    case 0x8: /* 32MB */
+        return 0x2000000ull;
+    case 0x3: /* 64MB */
+        return 0x4000000ull;
+    case 0x7: /* 128MB */
+        return 0x8000000ull;
+    case 0x4: /* 256MB */
+        return 0x10000000ull;
+    case 0x2: /* 1GB */
+        return 0x40000000ull;
+    case 0x1: /* 16GB */
+        return 0x400000000ull;
+    default:
+        /* What to do here ??? */
+        return 0;
+    }
+}
 
 int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
                                 int rwx, int mmu_idx)
 {
     CPUState *cs = CPU(cpu);
     CPUPPCState *env = &cpu->env;
-    ppc_slb_t *slb;
+    ppc_slb_t *slb_ptr;
+    ppc_slb_t slb;
     unsigned apshift;
     hwaddr pte_offset;
     ppc_hash_pte64_t pte;
@@ -670,11 +698,53 @@  int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
 
     assert((rwx == 0) || (rwx == 1) || (rwx == 2));
 
+    /* Note on LPCR usage: 970 uses HID4, but our special variant
+     * of store_spr copies relevant fields into env->spr[SPR_LPCR].
+     * Similarily we filter unimplemented bits when storing into
+     * LPCR depending on the MMU version. This code can thus just
+     * use the LPCR "as-is".
+     */
+
     /* 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 */
+        /* Translation is supposedly "off"  */
+        /* In real mode the top 4 effective address bits are (mostly) ignored */
         raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL;
+
+        /* In HV mode, add HRMOR if top EA bit is clear */
+        if (msr_hv) {
+            if (!(eaddr >> 63)) {
+                raddr |= env->spr[SPR_HRMOR];
+            }
+        } else {
+            /* Otherwise, check VPM for RMA vs VRMA */
+            if (env->spr[SPR_LPCR] & LPCR_VPM0) {
+                uint32_t vrmasd;
+                /* VRMA, we make up an SLB entry */
+                slb.vsid = SLB_VSID_VRMA;
+                vrmasd = (env->spr[SPR_LPCR] & LPCR_VRMASD) >>
+                    LPCR_VRMASD_SHIFT;
+                slb.vsid |= (vrmasd << 4) & (SLB_VSID_L | SLB_VSID_LP);
+                slb.esid = SLB_ESID_V;
+                goto skip_slb;
+            }
+            /* RMA. Check bounds in RMLS */
+            if (raddr < ppc_hash64_get_rmls(env)) {
+                raddr |= env->spr[SPR_RMOR];
+            } else {
+                /* The access failed, generate the approriate interrupt */
+                if (rwx == 2) {
+                    ppc_hash64_set_isi(cs, env, 0x08000000);
+                } else {
+                    dsisr = 0x08000000;
+                    if (rwx == 1) {
+                        dsisr |= 0x02000000;
+                    }
+                    ppc_hash64_set_dsi(cs, env, eaddr, dsisr);
+                }
+                return 1;
+            }
+        }
         tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
                      PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx,
                      TARGET_PAGE_SIZE);
@@ -682,9 +752,8 @@  int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
     }
 
     /* 2. Translation is on, so look up the SLB */
-    slb = slb_lookup(cpu, eaddr);
-
-    if (!slb) {
+    slb_ptr = slb_lookup(cpu, eaddr);
+    if (!slb_ptr) {
         if (rwx == 2) {
             cs->exception_index = POWERPC_EXCP_ISEG;
             env->error_code = 0;
@@ -696,14 +765,29 @@  int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
         return 1;
     }
 
+    /* We grab a local copy because we can modify it (or get a
+     * pre-cooked one from the VRMA code
+     */
+    slb = *slb_ptr;
+
+    /* 2.5 Clamp L||LP in ISL mode */
+    if (env->spr[SPR_LPCR] & LPCR_ISL) {
+        slb.vsid &= ~SLB_VSID_LLP_MASK;
+    }
+
     /* 3. Check for segment level no-execute violation */
-    if ((rwx == 2) && (slb->vsid & SLB_VSID_N)) {
+    if ((rwx == 2) && (slb.vsid & SLB_VSID_N)) {
         ppc_hash64_set_isi(cs, env, 0x10000000);
         return 1;
     }
 
+    /* We go straight here for VRMA translations as none of the
+     * above applies in that case
+     */
+ skip_slb:
+
     /* 4. Locate the PTE in the hash table */
-    pte_offset = ppc_hash64_htab_lookup(cpu, slb, eaddr, &pte);
+    pte_offset = ppc_hash64_htab_lookup(cpu, &slb, eaddr, &pte);
     if (pte_offset == -1) {
         dsisr = 0x40000000;
         if (rwx == 2) {
@@ -720,7 +804,7 @@  int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
                 "found PTE at offset %08" HWADDR_PRIx "\n", pte_offset);
 
     /* Validate page size encoding */
-    apshift = hpte_page_shift(slb->sps, pte.pte0, pte.pte1);
+    apshift = hpte_page_shift(slb.sps, pte.pte0, pte.pte1);
     if (!apshift) {
         error_report("Bad page size encoding in HPTE 0x%"PRIx64" - 0x%"PRIx64
                      " @ 0x%"HWADDR_PRIx, pte.pte0, pte.pte1, pte_offset);
@@ -733,7 +817,7 @@  int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
 
     /* 5. Check access permissions */
 
-    pp_prot = ppc_hash64_pte_prot(cpu, slb, pte);
+    pp_prot = ppc_hash64_pte_prot(cpu, &slb, pte);
     amr_prot = ppc_hash64_amr_prot(cpu, pte);
     prot = pp_prot & amr_prot;
 
@@ -789,27 +873,51 @@  int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
 hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr)
 {
     CPUPPCState *env = &cpu->env;
-    ppc_slb_t *slb;
-    hwaddr pte_offset;
+    ppc_slb_t slb;
+    ppc_slb_t *slb_ptr;
+    hwaddr pte_offset, raddr;
     ppc_hash_pte64_t pte;
     unsigned apshift;
 
+    /* Handle real mode */
     if (msr_dr == 0) {
-        /* In real mode the top 4 effective address bits are ignored */
-        return addr & 0x0FFFFFFFFFFFFFFFULL;
-    }
+        raddr = addr & 0x0FFFFFFFFFFFFFFFULL;
 
-    slb = slb_lookup(cpu, addr);
-    if (!slb) {
+        /* In HV mode, add HRMOR if top EA bit is clear */
+        if (msr_hv & !(addr >> 63)) {
+            return raddr | env->spr[SPR_HRMOR];
+        }
+
+        /* Otherwise, check VPM for RMA vs VRMA */
+        if (env->spr[SPR_LPCR] & LPCR_VPM0) {
+            uint32_t vrmasd;
+
+            /* VRMA, we make up an SLB entry */
+            slb.vsid = SLB_VSID_VRMA;
+            vrmasd = (env->spr[SPR_LPCR] & LPCR_VRMASD) >> LPCR_VRMASD_SHIFT;
+            slb.vsid |= (vrmasd << 4) & (SLB_VSID_L | SLB_VSID_LP);
+            slb.esid = SLB_ESID_V;
+            goto skip_slb;
+        }
+        /* RMA. Check bounds in RMLS */
+        if (raddr < ppc_hash64_get_rmls(env)) {
+            return raddr | env->spr[SPR_RMOR];
+        }
         return -1;
     }
 
-    pte_offset = ppc_hash64_htab_lookup(cpu, slb, addr, &pte);
+    slb_ptr = slb_lookup(cpu, addr);
+    if (!slb_ptr) {
+        return -1;
+    }
+    slb = *slb_ptr;
+ skip_slb:
+    pte_offset = ppc_hash64_htab_lookup(cpu, &slb, addr, &pte);
     if (pte_offset == -1) {
         return -1;
     }
 
-    apshift = hpte_page_shift(slb->sps, pte.pte0, pte.pte1);
+    apshift = hpte_page_shift(slb.sps, pte.pte0, pte.pte1);
     if (!apshift) {
         return -1;
     }
diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h
index 6423b9f791e7..13ad060cfefb 100644
--- a/target-ppc/mmu-hash64.h
+++ b/target-ppc/mmu-hash64.h
@@ -37,6 +37,7 @@  unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu,
 #define SLB_VSID_B_256M         0x0000000000000000ULL
 #define SLB_VSID_B_1T           0x4000000000000000ULL
 #define SLB_VSID_VSID           0x3FFFFFFFFFFFF000ULL
+#define SLB_VSID_VRMA           (0x0001FFFFFF000000ULL | SLB_VSID_B_1T)
 #define SLB_VSID_PTEM           (SLB_VSID_B | SLB_VSID_VSID)
 #define SLB_VSID_KS             0x0000000000000800ULL
 #define SLB_VSID_KP             0x0000000000000400ULL
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
index 55d1bfac97c4..4820c0bc99fb 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -8791,11 +8791,19 @@  void cpu_ppc_set_papr(PowerPCCPU *cpu)
     /* Set emulated LPCR to not send interrupts to hypervisor. Note that
      * under KVM, the actual HW LPCR will be set differently by KVM itself,
      * the settings below ensure proper operations with TCG in absence of
-     * a real hypervisor
+     * a real hypervisor.
+     *
+     * Clearing VPM0 will also cause us to use RMOR in mmu-hash64.c for
+     * real mode accesses, which thankfully defaults to 0 and isn't
+     * accessible in guest mode.
      */
     lpcr->default_value &= ~(LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV);
     lpcr->default_value |= LPCR_LPES0 | LPCR_LPES1;
 
+    /* Set RMLS to the max (ie, 16G) */
+    lpcr->default_value &= ~LPCR_RMLS;
+    lpcr->default_value |= 1ull << LPCR_RMLS_SHIFT;
+
     /* P7 and P8 has slightly different PECE bits, mostly because P8 adds
      * bit 47 and 48 which are reserved on P7. Here we set them all, which
      * will work as expected for both implementations