On Tue, Jun 28, 2016 at 08:48:33AM +0200, Cédric Le Goater wrote: > From: Benjamin Herrenschmidt <b...@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 <b...@kernel.crashing.org> > [clg: fixed checkpatch.pl errors ] > Signed-off-by: Cédric Le Goater <c...@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 -- David Gibson | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_ | _way_ _around_! http://www.ozlabs.org/~dgibson
signature.asc
Description: PGP signature