qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH 1/2] ppc: Add proper real mode translation suppo


From: David Gibson
Subject: Re: [Qemu-devel] [PATCH 1/2] ppc: Add proper real mode translation support
Date: Wed, 29 Jun 2016 12:41:25 +1000
User-agent: Mutt/1.6.1 (2016-04-27)

On Tue, Jun 28, 2016 at 08:48:33AM +0200, Cédric Le Goater wrote:
> From: Benjamin Herrenschmidt <address@hidden>
> 
> 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 <address@hidden>
> [clg: fixed checkpatch.pl errors ]
> Signed-off-by: Cédric Le Goater <address@hidden>

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

Attachment: signature.asc
Description: PGP signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]