diff mbox

[1/2] target-ppc: Add helpers for updating a CPU's SDR1 and external HPT

Message ID 1457069753-13123-2-git-send-email-david@gibson.dropbear.id.au
State New, archived
Headers show

Commit Message

David Gibson March 4, 2016, 5:35 a.m. UTC
When a Power cpu with 64-bit hash MMU has it's hash page table (HPT)
pointer updated by a write to the SDR1 register we need to update some
derived variables.  Likewise, when the cpu is configured for an external
HPT (one not in the guest memory space) some derived variables need to be
updated.

Currently the logic for this is (partially) duplicated in ppc_store_sdr1()
and in spapr_cpu_reset().  In future we're going to need it in some other
places, so make some common helpers for this update.

In addition the new ppc_hash64_set_external_hpt() helper also updates
SDR1 in KVM - it's not updated by the normal runtime KVM <-> qemu CPU
synchronization.  In a sense this belongs logically in the
ppc_hash64_set_sdr1() helper, but that is called from
kvm_arch_get_registers() so can't itself call cpu_synchronize_state()
without infinite recursion.  In practice this doesn't matter because
the only other caller is TCG specific.

Currently there aren't situations where updating SDR1 at runtime in KVM
matters, but there are going to be in future.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 hw/ppc/spapr.c          | 13 ++-----------
 target-ppc/kvm.c        | 15 +++++++++++++++
 target-ppc/kvm_ppc.h    |  6 ++++++
 target-ppc/mmu-hash64.c | 42 ++++++++++++++++++++++++++++++++++++++++++
 target-ppc/mmu-hash64.h |  6 ++++++
 target-ppc/mmu_helper.c | 13 ++++++-------
 6 files changed, 77 insertions(+), 18 deletions(-)

Comments

Thomas Huth March 4, 2016, 9:59 a.m. UTC | #1
On 04.03.2016 06:35, David Gibson wrote:
> When a Power cpu with 64-bit hash MMU has it's hash page table (HPT)
> pointer updated by a write to the SDR1 register we need to update some
> derived variables.  Likewise, when the cpu is configured for an external
> HPT (one not in the guest memory space) some derived variables need to be
> updated.
> 
> Currently the logic for this is (partially) duplicated in ppc_store_sdr1()
> and in spapr_cpu_reset().  In future we're going to need it in some other
> places, so make some common helpers for this update.
> 
> In addition the new ppc_hash64_set_external_hpt() helper also updates
> SDR1 in KVM - it's not updated by the normal runtime KVM <-> qemu CPU
> synchronization.  In a sense this belongs logically in the
> ppc_hash64_set_sdr1() helper, but that is called from
> kvm_arch_get_registers() so can't itself call cpu_synchronize_state()
> without infinite recursion.  In practice this doesn't matter because
> the only other caller is TCG specific.
> 
> Currently there aren't situations where updating SDR1 at runtime in KVM
> matters, but there are going to be in future.
> 
> Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
> ---
>  hw/ppc/spapr.c          | 13 ++-----------
>  target-ppc/kvm.c        | 15 +++++++++++++++
>  target-ppc/kvm_ppc.h    |  6 ++++++
>  target-ppc/mmu-hash64.c | 42 ++++++++++++++++++++++++++++++++++++++++++
>  target-ppc/mmu-hash64.h |  6 ++++++
>  target-ppc/mmu_helper.c | 13 ++++++-------
>  6 files changed, 77 insertions(+), 18 deletions(-)
> 
> diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
> index e9d4abf..a88e3af 100644
> --- a/hw/ppc/spapr.c
> +++ b/hw/ppc/spapr.c
> @@ -1196,17 +1196,8 @@ static void spapr_cpu_reset(void *opaque)
>  
>      env->spr[SPR_HIOR] = 0;
>  
> -    env->external_htab = (uint8_t *)spapr->htab;
> -    env->htab_base = -1;
> -    /*
> -     * htab_mask is the mask used to normalize hash value to PTEG index.
> -     * htab_shift is log2 of hash table size.
> -     * We have 8 hpte per group, and each hpte is 16 bytes.
> -     * ie have 128 bytes per hpte entry.
> -     */
> -    env->htab_mask = (1ULL << (spapr->htab_shift - 7)) - 1;
> -    env->spr[SPR_SDR1] = (target_ulong)(uintptr_t)spapr->htab |
> -        (spapr->htab_shift - 18);
> +    ppc_hash64_set_external_hpt(cpu, spapr->htab, spapr->htab_shift,
> +                                &error_fatal);
>  }
>  
>  static void spapr_create_nvram(sPAPRMachineState *spapr)
> diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c
> index d67c169..99b3231 100644
> --- a/target-ppc/kvm.c
> +++ b/target-ppc/kvm.c
> @@ -2537,3 +2537,18 @@ int kvmppc_enable_hwrng(void)
>  
>      return kvmppc_enable_hcall(kvm_state, H_RANDOM);
>  }
> +
> +int kvmppc_update_sdr1(PowerPCCPU *cpu)
> +{
> +    CPUState *cs = CPU(cpu);
> +
> +    if (!kvm_enabled()) {
> +        return 0; /* nothing to do */
> +    }

Since you're updating more than SDR1 below ... Could you maybe add a
"assert(cs->kvm_vcpu_dirty)" here, just to make sure that nobody
accidentally ever calls this function without synchronizing the
registers first?

> +    /* The normal KVM_PUT_RUNTIME_STATE doesn't include SDR1, which is
> +     * why we need an explicit update for it.  KVM_PUT_RESET_STATE is
> +     * overkill, but this is a pretty rare operation, so it's simpler
> +     * than writing a special purpose updater */
> +    return kvm_arch_put_registers(cs, KVM_PUT_RESET_STATE);
> +}

That indeed looks like a big overkill ... what about extracting the
block from kvm_arch_put_registers() which sets the the "struct
kvm_sregs" via KVM_SET_SREGS into a separate function, and then call
that function here instead?
kvm_arch_put_registers() is IMHO way too big anyway, so separating some
code into external functions there would improve readability there, too.

Apart from that, the patch looks right to me.

 Thomas
David Gibson March 7, 2016, 2:23 a.m. UTC | #2
On Fri, Mar 04, 2016 at 10:59:17AM +0100, Thomas Huth wrote:
> On 04.03.2016 06:35, David Gibson wrote:
> > When a Power cpu with 64-bit hash MMU has it's hash page table (HPT)
> > pointer updated by a write to the SDR1 register we need to update some
> > derived variables.  Likewise, when the cpu is configured for an external
> > HPT (one not in the guest memory space) some derived variables need to be
> > updated.
> > 
> > Currently the logic for this is (partially) duplicated in ppc_store_sdr1()
> > and in spapr_cpu_reset().  In future we're going to need it in some other
> > places, so make some common helpers for this update.
> > 
> > In addition the new ppc_hash64_set_external_hpt() helper also updates
> > SDR1 in KVM - it's not updated by the normal runtime KVM <-> qemu CPU
> > synchronization.  In a sense this belongs logically in the
> > ppc_hash64_set_sdr1() helper, but that is called from
> > kvm_arch_get_registers() so can't itself call cpu_synchronize_state()
> > without infinite recursion.  In practice this doesn't matter because
> > the only other caller is TCG specific.
> > 
> > Currently there aren't situations where updating SDR1 at runtime in KVM
> > matters, but there are going to be in future.
> > 
> > Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
> > ---
> >  hw/ppc/spapr.c          | 13 ++-----------
> >  target-ppc/kvm.c        | 15 +++++++++++++++
> >  target-ppc/kvm_ppc.h    |  6 ++++++
> >  target-ppc/mmu-hash64.c | 42 ++++++++++++++++++++++++++++++++++++++++++
> >  target-ppc/mmu-hash64.h |  6 ++++++
> >  target-ppc/mmu_helper.c | 13 ++++++-------
> >  6 files changed, 77 insertions(+), 18 deletions(-)
> > 
> > diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
> > index e9d4abf..a88e3af 100644
> > --- a/hw/ppc/spapr.c
> > +++ b/hw/ppc/spapr.c
> > @@ -1196,17 +1196,8 @@ static void spapr_cpu_reset(void *opaque)
> >  
> >      env->spr[SPR_HIOR] = 0;
> >  
> > -    env->external_htab = (uint8_t *)spapr->htab;
> > -    env->htab_base = -1;
> > -    /*
> > -     * htab_mask is the mask used to normalize hash value to PTEG index.
> > -     * htab_shift is log2 of hash table size.
> > -     * We have 8 hpte per group, and each hpte is 16 bytes.
> > -     * ie have 128 bytes per hpte entry.
> > -     */
> > -    env->htab_mask = (1ULL << (spapr->htab_shift - 7)) - 1;
> > -    env->spr[SPR_SDR1] = (target_ulong)(uintptr_t)spapr->htab |
> > -        (spapr->htab_shift - 18);
> > +    ppc_hash64_set_external_hpt(cpu, spapr->htab, spapr->htab_shift,
> > +                                &error_fatal);
> >  }
> >  
> >  static void spapr_create_nvram(sPAPRMachineState *spapr)
> > diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c
> > index d67c169..99b3231 100644
> > --- a/target-ppc/kvm.c
> > +++ b/target-ppc/kvm.c
> > @@ -2537,3 +2537,18 @@ int kvmppc_enable_hwrng(void)
> >  
> >      return kvmppc_enable_hcall(kvm_state, H_RANDOM);
> >  }
> > +
> > +int kvmppc_update_sdr1(PowerPCCPU *cpu)
> > +{
> > +    CPUState *cs = CPU(cpu);
> > +
> > +    if (!kvm_enabled()) {
> > +        return 0; /* nothing to do */
> > +    }
> 
> Since you're updating more than SDR1 below ... Could you maybe add a
> "assert(cs->kvm_vcpu_dirty)" here, just to make sure that nobody
> accidentally ever calls this function without synchronizing the
> registers first?

Hmm.. that's a good idea, but it doesn't work so well with the one below.

> 
> > +    /* The normal KVM_PUT_RUNTIME_STATE doesn't include SDR1, which is
> > +     * why we need an explicit update for it.  KVM_PUT_RESET_STATE is
> > +     * overkill, but this is a pretty rare operation, so it's simpler
> > +     * than writing a special purpose updater */
> > +    return kvm_arch_put_registers(cs, KVM_PUT_RESET_STATE);
> > +}
> 
> That indeed looks like a big overkill ... what about extracting the
> block from kvm_arch_put_registers() which sets the the "struct
> kvm_sregs" via KVM_SET_SREGS into a separate function, and then call
> that function here instead?
> kvm_arch_put_registers() is IMHO way too big anyway, so separating some
> code into external functions there would improve readability there, too.

Yeah, fair enough.  I'll split that up in a prelim patch for v2.
diff mbox

Patch

diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index e9d4abf..a88e3af 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -1196,17 +1196,8 @@  static void spapr_cpu_reset(void *opaque)
 
     env->spr[SPR_HIOR] = 0;
 
-    env->external_htab = (uint8_t *)spapr->htab;
-    env->htab_base = -1;
-    /*
-     * htab_mask is the mask used to normalize hash value to PTEG index.
-     * htab_shift is log2 of hash table size.
-     * We have 8 hpte per group, and each hpte is 16 bytes.
-     * ie have 128 bytes per hpte entry.
-     */
-    env->htab_mask = (1ULL << (spapr->htab_shift - 7)) - 1;
-    env->spr[SPR_SDR1] = (target_ulong)(uintptr_t)spapr->htab |
-        (spapr->htab_shift - 18);
+    ppc_hash64_set_external_hpt(cpu, spapr->htab, spapr->htab_shift,
+                                &error_fatal);
 }
 
 static void spapr_create_nvram(sPAPRMachineState *spapr)
diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c
index d67c169..99b3231 100644
--- a/target-ppc/kvm.c
+++ b/target-ppc/kvm.c
@@ -2537,3 +2537,18 @@  int kvmppc_enable_hwrng(void)
 
     return kvmppc_enable_hcall(kvm_state, H_RANDOM);
 }
+
+int kvmppc_update_sdr1(PowerPCCPU *cpu)
+{
+    CPUState *cs = CPU(cpu);
+
+    if (!kvm_enabled()) {
+        return 0; /* nothing to do */
+    }
+
+    /* The normal KVM_PUT_RUNTIME_STATE doesn't include SDR1, which is
+     * why we need an explicit update for it.  KVM_PUT_RESET_STATE is
+     * overkill, but this is a pretty rare operation, so it's simpler
+     * than writing a special purpose updater */
+    return kvm_arch_put_registers(cs, KVM_PUT_RESET_STATE);
+}
diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h
index fd64c44..5564988 100644
--- a/target-ppc/kvm_ppc.h
+++ b/target-ppc/kvm_ppc.h
@@ -55,6 +55,7 @@  void kvmppc_hash64_write_pte(CPUPPCState *env, target_ulong pte_index,
                              target_ulong pte0, target_ulong pte1);
 bool kvmppc_has_cap_fixup_hcalls(void);
 int kvmppc_enable_hwrng(void);
+int kvmppc_update_sdr1(PowerPCCPU *cpu);
 
 #else
 
@@ -246,6 +247,11 @@  static inline int kvmppc_enable_hwrng(void)
 {
     return -1;
 }
+
+static inline int kvmppc_update_sdr1(PowerPCCPU *cpu)
+{
+    return 0; /* nothing to do */
+}
 #endif
 
 #ifndef CONFIG_KVM
diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c
index 9c58fbf..5507781 100644
--- a/target-ppc/mmu-hash64.c
+++ b/target-ppc/mmu-hash64.c
@@ -258,6 +258,48 @@  target_ulong helper_load_slb_vsid(CPUPPCState *env, target_ulong rb)
 /*
  * 64-bit hash table MMU handling
  */
+void ppc_hash64_set_sdr1(PowerPCCPU *cpu, target_ulong value,
+                         Error **errp)
+{
+    CPUPPCState *env = &cpu->env;
+    target_ulong htabsize = value & SDR_64_HTABSIZE;
+
+    env->spr[SPR_SDR1] = value;
+    if (htabsize > 28) {
+        error_setg(errp,
+                   "Invalid HTABSIZE 0x" TARGET_FMT_lx" stored in SDR1",
+                   htabsize);
+        htabsize = 28;
+    }
+    env->htab_mask = (1ULL << (htabsize + 18 - 7)) - 1;
+    env->htab_base = value & SDR_64_HTABORG;
+}
+
+void ppc_hash64_set_external_hpt(PowerPCCPU *cpu, void *hpt, int shift,
+                                 Error **errp)
+{
+    CPUPPCState *env = &cpu->env;
+    Error *local_err = NULL;
+
+    cpu_synchronize_state(CPU(cpu));
+
+    env->external_htab = hpt;
+    ppc_hash64_set_sdr1(cpu, (target_ulong)(uintptr_t)hpt | (shift - 18),
+                        &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    /* Not strictly necessary, but makes it clearer that an external
+     * htab is in use when debugging */
+    env->htab_base = -1;
+
+    if (kvmppc_update_sdr1(cpu) < 0) {
+        error_setg(errp,
+                   "Unable to update SDR1 in KVM");
+    }
+}
 
 static int ppc_hash64_pte_prot(PowerPCCPU *cpu,
                                ppc_slb_t *slb, ppc_hash_pte64_t pte)
diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h
index e7d9925..138cd7e 100644
--- a/target-ppc/mmu-hash64.h
+++ b/target-ppc/mmu-hash64.h
@@ -92,6 +92,12 @@  unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu,
 
 
 extern bool kvmppc_kern_htab;
+
+void ppc_hash64_set_sdr1(PowerPCCPU *cpu, target_ulong value,
+                         Error **errp);
+void ppc_hash64_set_external_hpt(PowerPCCPU *cpu, void *hpt, int shift,
+                                 Error **errp);
+
 uint64_t ppc_hash64_start_access(PowerPCCPU *cpu, target_ulong pte_index);
 void ppc_hash64_stop_access(uint64_t token);
 
diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c
index e5ec8d6..fcb2cc5 100644
--- a/target-ppc/mmu_helper.c
+++ b/target-ppc/mmu_helper.c
@@ -2005,15 +2005,14 @@  void ppc_store_sdr1(CPUPPCState *env, target_ulong value)
     env->spr[SPR_SDR1] = value;
 #if defined(TARGET_PPC64)
     if (env->mmu_model & POWERPC_MMU_64) {
-        target_ulong htabsize = value & SDR_64_HTABSIZE;
+        PowerPCCPU *cpu = ppc_env_get_cpu(env);
+        Error *local_err = NULL;
 
-        if (htabsize > 28) {
-            fprintf(stderr, "Invalid HTABSIZE 0x" TARGET_FMT_lx
-                    " stored in SDR1\n", htabsize);
-            htabsize = 28;
+        ppc_hash64_set_sdr1(cpu, value, &local_err);
+        if (local_err) {
+            error_report_err(local_err);
+            error_free(local_err);
         }
-        env->htab_mask = (1ULL << (htabsize + 18 - 7)) - 1;
-        env->htab_base = value & SDR_64_HTABORG;
     } else
 #endif /* defined(TARGET_PPC64) */
     {