diff mbox

[53/77] ppc: Add proper real mode translation support

Message ID 1447201710-10229-54-git-send-email-benh@kernel.crashing.org
State New
Headers show

Commit Message

Benjamin Herrenschmidt Nov. 11, 2015, 12:28 a.m. UTC
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>
---
 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 mbox

Patch

diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index d8a84ca..13fe2d5 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -1741,6 +1741,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 835245a..328998f 100644
--- a/target-ppc/mmu-hash64.c
+++ b/target-ppc/mmu-hash64.c
@@ -504,12 +504,41 @@  static void ppc_hash64_set_dsi(CPUState *cs, CPUPPCState *env, uint64_t dar, uin
     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, target_ulong 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;
     hwaddr pte_offset;
     ppc_hash_pte64_t pte;
     int pp_prot, amr_prot, prot;
@@ -519,11 +548,52 @@  int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong 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);
@@ -531,9 +601,8 @@  int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr,
     }
 
     /* 2. Translation is on, so look up the SLB */
-    slb = slb_lookup(env, eaddr);
-
-    if (!slb) {
+    slb_ptr = slb_lookup(env, eaddr);
+    if (!slb_ptr) {
         if (rwx == 2) {
             cs->exception_index = POWERPC_EXCP_ISEG;
             env->error_code = 0;
@@ -545,14 +614,29 @@  int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong 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(env, slb, eaddr, &pte);
+    pte_offset = ppc_hash64_htab_lookup(env, &slb, eaddr, &pte);
     if (pte_offset == -1) {
         dsisr = 0x40000000;
         if (rwx == 2) {
@@ -570,7 +654,7 @@  int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr,
 
     /* 5. Check access permissions */
 
-    pp_prot = ppc_hash64_pte_prot(env, slb, pte);
+    pp_prot = ppc_hash64_pte_prot(env, &slb, pte);
     amr_prot = ppc_hash64_amr_prot(env, pte);
     prot = pp_prot & amr_prot;
 
@@ -615,7 +699,7 @@  int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr,
 
     /* 7. Determine the real address from the PTE */
 
-    raddr = ppc_hash64_pte_raddr(slb, pte, eaddr);
+    raddr = ppc_hash64_pte_raddr(&slb, pte, eaddr);
 
     tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
                  prot, mmu_idx, TARGET_PAGE_SIZE);
@@ -625,26 +709,50 @@  int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr,
 
 hwaddr ppc_hash64_get_phys_page_debug(CPUPPCState *env, target_ulong addr)
 {
-    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;
 
+    /* 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(env, 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(env, slb, addr, &pte);
+    slb_ptr = slb_lookup(env, addr);
+    if (!slb_ptr) {
+        return -1;
+    }
+    slb = *slb_ptr;
+ skip_slb:
+    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;
+    return ppc_hash64_pte_raddr(&slb, pte, addr) & TARGET_PAGE_MASK;
 }
 
 void ppc_hash64_store_hpte(CPUPPCState *env,
diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h
index 291750f..729f718 100644
--- a/target-ppc/mmu-hash64.h
+++ b/target-ppc/mmu-hash64.h
@@ -29,6 +29,7 @@  void ppc_hash64_store_hpte(CPUPPCState *env, target_ulong index,
 #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 853a084..504564d 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -8558,11 +8558,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