[RFC PATCH 27/43] KVM: PPC: Book3S HV P9: Move host OS save/restore functions to built-in
Athira Rajeev
atrajeev at linux.vnet.ibm.com
Thu Jul 8 15:32:17 AEST 2021
> On 22-Jun-2021, at 4:27 PM, Nicholas Piggin <npiggin at gmail.com> wrote:
>
> Move the P9 guest/host register switching functions to the built-in
> P9 entry code, and export it for nested to use as well.
>
> This allows more flexibility in scheduling these supervisor privileged
> SPR accesses with the HV privileged and PR SPR accesses in the low level
> entry code.
>
> Signed-off-by: Nicholas Piggin <npiggin at gmail.com>
> ---
> arch/powerpc/kvm/book3s_hv.c | 351 +-------------------------
> arch/powerpc/kvm/book3s_hv.h | 39 +++
> arch/powerpc/kvm/book3s_hv_p9_entry.c | 332 ++++++++++++++++++++++++
> 3 files changed, 372 insertions(+), 350 deletions(-)
> create mode 100644 arch/powerpc/kvm/book3s_hv.h
>
> diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
> index 35749b0b663f..a7660af22161 100644
> --- a/arch/powerpc/kvm/book3s_hv.c
> +++ b/arch/powerpc/kvm/book3s_hv.c
> @@ -79,6 +79,7 @@
> #include <asm/dtl.h>
>
> #include "book3s.h"
> +#include "book3s_hv.h"
>
> #define CREATE_TRACE_POINTS
> #include "trace_hv.h"
> @@ -3675,356 +3676,6 @@ static noinline void kvmppc_run_core(struct kvmppc_vcore *vc)
> trace_kvmppc_run_core(vc, 1);
> }
>
> -/*
> - * Privileged (non-hypervisor) host registers to save.
> - */
> -struct p9_host_os_sprs {
> - unsigned long dscr;
> - unsigned long tidr;
> - unsigned long iamr;
> - unsigned long amr;
> - unsigned long fscr;
> -
> - unsigned int pmc1;
> - unsigned int pmc2;
> - unsigned int pmc3;
> - unsigned int pmc4;
> - unsigned int pmc5;
> - unsigned int pmc6;
> - unsigned long mmcr0;
> - unsigned long mmcr1;
> - unsigned long mmcr2;
> - unsigned long mmcr3;
> - unsigned long mmcra;
> - unsigned long siar;
> - unsigned long sier1;
> - unsigned long sier2;
> - unsigned long sier3;
> - unsigned long sdar;
> -};
> -
> -static void freeze_pmu(unsigned long mmcr0, unsigned long mmcra)
> -{
> - if (!(mmcr0 & MMCR0_FC))
> - goto do_freeze;
> - if (mmcra & MMCRA_SAMPLE_ENABLE)
> - goto do_freeze;
> - if (cpu_has_feature(CPU_FTR_ARCH_31)) {
> - if (!(mmcr0 & MMCR0_PMCCEXT))
> - goto do_freeze;
> - if (!(mmcra & MMCRA_BHRB_DISABLE))
> - goto do_freeze;
> - }
> - return;
> -
> -do_freeze:
> - mmcr0 = MMCR0_FC;
> - mmcra = 0;
> - if (cpu_has_feature(CPU_FTR_ARCH_31)) {
> - mmcr0 |= MMCR0_PMCCEXT;
> - mmcra = MMCRA_BHRB_DISABLE;
> - }
> -
> - mtspr(SPRN_MMCR0, mmcr0);
> - mtspr(SPRN_MMCRA, mmcra);
> - isync();
> -}
> -
> -static void switch_pmu_to_guest(struct kvm_vcpu *vcpu,
> - struct p9_host_os_sprs *host_os_sprs)
> -{
> - struct lppaca *lp;
> - int load_pmu = 1;
> -
> - lp = vcpu->arch.vpa.pinned_addr;
> - if (lp)
> - load_pmu = lp->pmcregs_in_use;
> -
> - if (load_pmu)
> - vcpu->arch.hfscr |= HFSCR_PM;
> -
> - /* Save host */
> - if (ppc_get_pmu_inuse()) {
> - /*
> - * It might be better to put PMU handling (at least for the
> - * host) in the perf subsystem because it knows more about what
> - * is being used.
> - */
> -
> - /* POWER9, POWER10 do not implement HPMC or SPMC */
> -
> - host_os_sprs->mmcr0 = mfspr(SPRN_MMCR0);
> - host_os_sprs->mmcra = mfspr(SPRN_MMCRA);
> -
> - freeze_pmu(host_os_sprs->mmcr0, host_os_sprs->mmcra);
> -
> - host_os_sprs->pmc1 = mfspr(SPRN_PMC1);
> - host_os_sprs->pmc2 = mfspr(SPRN_PMC2);
> - host_os_sprs->pmc3 = mfspr(SPRN_PMC3);
> - host_os_sprs->pmc4 = mfspr(SPRN_PMC4);
> - host_os_sprs->pmc5 = mfspr(SPRN_PMC5);
> - host_os_sprs->pmc6 = mfspr(SPRN_PMC6);
> - host_os_sprs->mmcr1 = mfspr(SPRN_MMCR1);
> - host_os_sprs->mmcr2 = mfspr(SPRN_MMCR2);
> - host_os_sprs->sdar = mfspr(SPRN_SDAR);
> - host_os_sprs->siar = mfspr(SPRN_SIAR);
> - host_os_sprs->sier1 = mfspr(SPRN_SIER);
> -
> - if (cpu_has_feature(CPU_FTR_ARCH_31)) {
> - host_os_sprs->mmcr3 = mfspr(SPRN_MMCR3);
> - host_os_sprs->sier2 = mfspr(SPRN_SIER2);
> - host_os_sprs->sier3 = mfspr(SPRN_SIER3);
> - }
> - }
> -
> -#ifdef CONFIG_PPC_PSERIES
> - if (kvmhv_on_pseries()) {
> - if (vcpu->arch.vpa.pinned_addr) {
> - struct lppaca *lp = vcpu->arch.vpa.pinned_addr;
> - get_lppaca()->pmcregs_in_use = lp->pmcregs_in_use;
> - } else {
> - get_lppaca()->pmcregs_in_use = 1;
> - }
> - }
> -#endif
> -
> - /* Load guest */
> - if (vcpu->arch.hfscr & HFSCR_PM) {
> - mtspr(SPRN_PMC1, vcpu->arch.pmc[0]);
> - mtspr(SPRN_PMC2, vcpu->arch.pmc[1]);
> - mtspr(SPRN_PMC3, vcpu->arch.pmc[2]);
> - mtspr(SPRN_PMC4, vcpu->arch.pmc[3]);
> - mtspr(SPRN_PMC5, vcpu->arch.pmc[4]);
> - mtspr(SPRN_PMC6, vcpu->arch.pmc[5]);
> - mtspr(SPRN_MMCR1, vcpu->arch.mmcr[1]);
> - mtspr(SPRN_MMCR2, vcpu->arch.mmcr[2]);
> - mtspr(SPRN_SDAR, vcpu->arch.sdar);
> - mtspr(SPRN_SIAR, vcpu->arch.siar);
> - mtspr(SPRN_SIER, vcpu->arch.sier[0]);
> -
> - if (cpu_has_feature(CPU_FTR_ARCH_31)) {
> - mtspr(SPRN_MMCR3, vcpu->arch.mmcr[4]);
Hi Nick,
Have a doubt here..
For MMCR3, it is vcpu->arch.mmcr[3) ?
Thanks
Athira
> - mtspr(SPRN_SIER2, vcpu->arch.sier[1]);
> - mtspr(SPRN_SIER3, vcpu->arch.sier[2]);
> - }
> -
> - /* Set MMCRA then MMCR0 last */
> - mtspr(SPRN_MMCRA, vcpu->arch.mmcra);
> - mtspr(SPRN_MMCR0, vcpu->arch.mmcr[0]);
> - /* No isync necessary because we're starting counters */
> - }
> -}
> -
> -static void switch_pmu_to_host(struct kvm_vcpu *vcpu,
> - struct p9_host_os_sprs *host_os_sprs)
> -{
> - struct lppaca *lp;
> - int save_pmu = 1;
> -
> - lp = vcpu->arch.vpa.pinned_addr;
> - if (lp)
> - save_pmu = lp->pmcregs_in_use;
> -
> - if (save_pmu) {
> - vcpu->arch.mmcr[0] = mfspr(SPRN_MMCR0);
> - vcpu->arch.mmcra = mfspr(SPRN_MMCRA);
> -
> - freeze_pmu(vcpu->arch.mmcr[0], vcpu->arch.mmcra);
> -
> - vcpu->arch.pmc[0] = mfspr(SPRN_PMC1);
> - vcpu->arch.pmc[1] = mfspr(SPRN_PMC2);
> - vcpu->arch.pmc[2] = mfspr(SPRN_PMC3);
> - vcpu->arch.pmc[3] = mfspr(SPRN_PMC4);
> - vcpu->arch.pmc[4] = mfspr(SPRN_PMC5);
> - vcpu->arch.pmc[5] = mfspr(SPRN_PMC6);
> - vcpu->arch.mmcr[1] = mfspr(SPRN_MMCR1);
> - vcpu->arch.mmcr[2] = mfspr(SPRN_MMCR2);
> - vcpu->arch.sdar = mfspr(SPRN_SDAR);
> - vcpu->arch.siar = mfspr(SPRN_SIAR);
> - vcpu->arch.sier[0] = mfspr(SPRN_SIER);
> -
> - if (cpu_has_feature(CPU_FTR_ARCH_31)) {
> - vcpu->arch.mmcr[3] = mfspr(SPRN_MMCR3);
> - vcpu->arch.sier[1] = mfspr(SPRN_SIER2);
> - vcpu->arch.sier[2] = mfspr(SPRN_SIER3);
> - }
> -
> - } else if (vcpu->arch.hfscr & HFSCR_PM) {
> - /*
> - * The guest accessed PMC SPRs without specifying they should
> - * be preserved. Stop them from counting if the guest had
> - * started anything.
> - */
> - freeze_pmu(mfspr(SPRN_MMCR0), mfspr(SPRN_MMCRA));
> -
> - /*
> - * Demand-fault PMU register access in the guest.
> - *
> - * This is used to grab the guest's VPA pmcregs_in_use value
> - * and reflect it into the host's VPA in the case of a nested
> - * hypervisor.
> - *
> - * It also avoids having to zero-out SPRs after each guest
> - * exit to avoid side-channels when.
> - *
> - * This is cleared here when we exit the guest, so later HFSCR
> - * interrupt handling can add it back to run the guest with
> - * PM enabled next time.
> - */
> - vcpu->arch.hfscr &= ~HFSCR_PM;
> - } /* otherwise the PMU should still be frozen from guest entry */
> -
> -#ifdef CONFIG_PPC_PSERIES
> - if (kvmhv_on_pseries())
> - get_lppaca()->pmcregs_in_use = ppc_get_pmu_inuse();
> -#endif
> -
> - if (ppc_get_pmu_inuse()) {
> - mtspr(SPRN_PMC1, host_os_sprs->pmc1);
> - mtspr(SPRN_PMC2, host_os_sprs->pmc2);
> - mtspr(SPRN_PMC3, host_os_sprs->pmc3);
> - mtspr(SPRN_PMC4, host_os_sprs->pmc4);
> - mtspr(SPRN_PMC5, host_os_sprs->pmc5);
> - mtspr(SPRN_PMC6, host_os_sprs->pmc6);
> - mtspr(SPRN_MMCR1, host_os_sprs->mmcr1);
> - mtspr(SPRN_MMCR2, host_os_sprs->mmcr2);
> - mtspr(SPRN_SDAR, host_os_sprs->sdar);
> - mtspr(SPRN_SIAR, host_os_sprs->siar);
> - mtspr(SPRN_SIER, host_os_sprs->sier1);
> -
> - if (cpu_has_feature(CPU_FTR_ARCH_31)) {
> - mtspr(SPRN_MMCR3, host_os_sprs->mmcr3);
> - mtspr(SPRN_SIER2, host_os_sprs->sier2);
> - mtspr(SPRN_SIER3, host_os_sprs->sier3);
> - }
> -
> - /* Set MMCRA then MMCR0 last */
> - mtspr(SPRN_MMCRA, host_os_sprs->mmcra);
> - mtspr(SPRN_MMCR0, host_os_sprs->mmcr0);
> - isync();
> - }
> -}
> -
> -static void load_spr_state(struct kvm_vcpu *vcpu,
> - struct p9_host_os_sprs *host_os_sprs)
> -{
> - mtspr(SPRN_TAR, vcpu->arch.tar);
> - mtspr(SPRN_EBBHR, vcpu->arch.ebbhr);
> - mtspr(SPRN_EBBRR, vcpu->arch.ebbrr);
> - mtspr(SPRN_BESCR, vcpu->arch.bescr);
> -
> - if (!cpu_has_feature(CPU_FTR_ARCH_31))
> - mtspr(SPRN_TIDR, vcpu->arch.tid);
> - if (host_os_sprs->iamr != vcpu->arch.iamr)
> - mtspr(SPRN_IAMR, vcpu->arch.iamr);
> - if (host_os_sprs->amr != vcpu->arch.amr)
> - mtspr(SPRN_AMR, vcpu->arch.amr);
> - if (vcpu->arch.uamor != 0)
> - mtspr(SPRN_UAMOR, vcpu->arch.uamor);
> - if (host_os_sprs->fscr != vcpu->arch.fscr)
> - mtspr(SPRN_FSCR, vcpu->arch.fscr);
> - if (host_os_sprs->dscr != vcpu->arch.dscr)
> - mtspr(SPRN_DSCR, vcpu->arch.dscr);
> - if (vcpu->arch.pspb != 0)
> - mtspr(SPRN_PSPB, vcpu->arch.pspb);
> -
> - /*
> - * DAR, DSISR, and for nested HV, SPRGs must be set with MSR[RI]
> - * clear (or hstate set appropriately to catch those registers
> - * being clobbered if we take a MCE or SRESET), so those are done
> - * later.
> - */
> -
> - if (!(vcpu->arch.ctrl & 1))
> - mtspr(SPRN_CTRLT, 0);
> -}
> -
> -static void store_spr_state(struct kvm_vcpu *vcpu)
> -{
> - vcpu->arch.tar = mfspr(SPRN_TAR);
> - vcpu->arch.ebbhr = mfspr(SPRN_EBBHR);
> - vcpu->arch.ebbrr = mfspr(SPRN_EBBRR);
> - vcpu->arch.bescr = mfspr(SPRN_BESCR);
> -
> - if (!cpu_has_feature(CPU_FTR_ARCH_31))
> - vcpu->arch.tid = mfspr(SPRN_TIDR);
> - vcpu->arch.iamr = mfspr(SPRN_IAMR);
> - vcpu->arch.amr = mfspr(SPRN_AMR);
> - vcpu->arch.uamor = mfspr(SPRN_UAMOR);
> - vcpu->arch.fscr = mfspr(SPRN_FSCR);
> - vcpu->arch.dscr = mfspr(SPRN_DSCR);
> - vcpu->arch.pspb = mfspr(SPRN_PSPB);
> -
> - vcpu->arch.ctrl = mfspr(SPRN_CTRLF);
> -}
> -
> -static void load_vcpu_state(struct kvm_vcpu *vcpu,
> - struct p9_host_os_sprs *host_os_sprs)
> -{
> - if (cpu_has_feature(CPU_FTR_TM) ||
> - cpu_has_feature(CPU_FTR_P9_TM_HV_ASSIST))
> - kvmppc_restore_tm_hv(vcpu, vcpu->arch.shregs.msr, true);
> -
> - load_spr_state(vcpu, host_os_sprs);
> -
> - load_fp_state(&vcpu->arch.fp);
> -#ifdef CONFIG_ALTIVEC
> - load_vr_state(&vcpu->arch.vr);
> -#endif
> - mtspr(SPRN_VRSAVE, vcpu->arch.vrsave);
> -}
> -
> -static void store_vcpu_state(struct kvm_vcpu *vcpu)
> -{
> - store_spr_state(vcpu);
> -
> - store_fp_state(&vcpu->arch.fp);
> -#ifdef CONFIG_ALTIVEC
> - store_vr_state(&vcpu->arch.vr);
> -#endif
> - vcpu->arch.vrsave = mfspr(SPRN_VRSAVE);
> -
> - if (cpu_has_feature(CPU_FTR_TM) ||
> - cpu_has_feature(CPU_FTR_P9_TM_HV_ASSIST))
> - kvmppc_save_tm_hv(vcpu, vcpu->arch.shregs.msr, true);
> -}
> -
> -static void save_p9_host_os_sprs(struct p9_host_os_sprs *host_os_sprs)
> -{
> - if (!cpu_has_feature(CPU_FTR_ARCH_31))
> - host_os_sprs->tidr = mfspr(SPRN_TIDR);
> - host_os_sprs->iamr = mfspr(SPRN_IAMR);
> - host_os_sprs->amr = mfspr(SPRN_AMR);
> - host_os_sprs->fscr = mfspr(SPRN_FSCR);
> - host_os_sprs->dscr = mfspr(SPRN_DSCR);
> -}
> -
> -/* vcpu guest regs must already be saved */
> -static void restore_p9_host_os_sprs(struct kvm_vcpu *vcpu,
> - struct p9_host_os_sprs *host_os_sprs)
> -{
> - mtspr(SPRN_SPRG_VDSO_WRITE, local_paca->sprg_vdso);
> -
> - if (!cpu_has_feature(CPU_FTR_ARCH_31))
> - mtspr(SPRN_TIDR, host_os_sprs->tidr);
> - if (host_os_sprs->iamr != vcpu->arch.iamr)
> - mtspr(SPRN_IAMR, host_os_sprs->iamr);
> - if (vcpu->arch.uamor != 0)
> - mtspr(SPRN_UAMOR, 0);
> - if (host_os_sprs->amr != vcpu->arch.amr)
> - mtspr(SPRN_AMR, host_os_sprs->amr);
> - if (host_os_sprs->fscr != vcpu->arch.fscr)
> - mtspr(SPRN_FSCR, host_os_sprs->fscr);
> - if (host_os_sprs->dscr != vcpu->arch.dscr)
> - mtspr(SPRN_DSCR, host_os_sprs->dscr);
> - if (vcpu->arch.pspb != 0)
> - mtspr(SPRN_PSPB, 0);
> -
> - /* Save guest CTRL register, set runlatch to 1 */
> - if (!(vcpu->arch.ctrl & 1))
> - mtspr(SPRN_CTRLT, 1);
> -}
> -
> static inline bool hcall_is_xics(unsigned long req)
> {
> return req == H_EOI || req == H_CPPR || req == H_IPI ||
> diff --git a/arch/powerpc/kvm/book3s_hv.h b/arch/powerpc/kvm/book3s_hv.h
> new file mode 100644
> index 000000000000..72e3a8f4c2cf
> --- /dev/null
> +++ b/arch/powerpc/kvm/book3s_hv.h
> @@ -0,0 +1,39 @@
> +
> +/*
> + * Privileged (non-hypervisor) host registers to save.
> + */
> +struct p9_host_os_sprs {
> + unsigned long dscr;
> + unsigned long tidr;
> + unsigned long iamr;
> + unsigned long amr;
> + unsigned long fscr;
> +
> + unsigned int pmc1;
> + unsigned int pmc2;
> + unsigned int pmc3;
> + unsigned int pmc4;
> + unsigned int pmc5;
> + unsigned int pmc6;
> + unsigned long mmcr0;
> + unsigned long mmcr1;
> + unsigned long mmcr2;
> + unsigned long mmcr3;
> + unsigned long mmcra;
> + unsigned long siar;
> + unsigned long sier1;
> + unsigned long sier2;
> + unsigned long sier3;
> + unsigned long sdar;
> +};
> +
> +void load_vcpu_state(struct kvm_vcpu *vcpu,
> + struct p9_host_os_sprs *host_os_sprs);
> +void store_vcpu_state(struct kvm_vcpu *vcpu);
> +void save_p9_host_os_sprs(struct p9_host_os_sprs *host_os_sprs);
> +void restore_p9_host_os_sprs(struct kvm_vcpu *vcpu,
> + struct p9_host_os_sprs *host_os_sprs);
> +void switch_pmu_to_guest(struct kvm_vcpu *vcpu,
> + struct p9_host_os_sprs *host_os_sprs);
> +void switch_pmu_to_host(struct kvm_vcpu *vcpu,
> + struct p9_host_os_sprs *host_os_sprs);
> diff --git a/arch/powerpc/kvm/book3s_hv_p9_entry.c b/arch/powerpc/kvm/book3s_hv_p9_entry.c
> index afdd7dfa1c08..cc74cd314fcf 100644
> --- a/arch/powerpc/kvm/book3s_hv_p9_entry.c
> +++ b/arch/powerpc/kvm/book3s_hv_p9_entry.c
> @@ -4,8 +4,340 @@
> #include <asm/asm-prototypes.h>
> #include <asm/dbell.h>
> #include <asm/kvm_ppc.h>
> +#include <asm/pmc.h>
> #include <asm/ppc-opcode.h>
>
> +#include "book3s_hv.h"
> +
> +static void freeze_pmu(unsigned long mmcr0, unsigned long mmcra)
> +{
> + if (!(mmcr0 & MMCR0_FC))
> + goto do_freeze;
> + if (mmcra & MMCRA_SAMPLE_ENABLE)
> + goto do_freeze;
> + if (cpu_has_feature(CPU_FTR_ARCH_31)) {
> + if (!(mmcr0 & MMCR0_PMCCEXT))
> + goto do_freeze;
> + if (!(mmcra & MMCRA_BHRB_DISABLE))
> + goto do_freeze;
> + }
> + return;
> +
> +do_freeze:
> + mmcr0 = MMCR0_FC;
> + mmcra = 0;
> + if (cpu_has_feature(CPU_FTR_ARCH_31)) {
> + mmcr0 |= MMCR0_PMCCEXT;
> + mmcra = MMCRA_BHRB_DISABLE;
> + }
> +
> + mtspr(SPRN_MMCR0, mmcr0);
> + mtspr(SPRN_MMCRA, mmcra);
> + isync();
> +}
> +
> +void switch_pmu_to_guest(struct kvm_vcpu *vcpu,
> + struct p9_host_os_sprs *host_os_sprs)
> +{
> + struct lppaca *lp;
> + int load_pmu = 1;
> +
> + lp = vcpu->arch.vpa.pinned_addr;
> + if (lp)
> + load_pmu = lp->pmcregs_in_use;
> +
> + if (load_pmu)
> + vcpu->arch.hfscr |= HFSCR_PM;
> +
> + /* Save host */
> + if (ppc_get_pmu_inuse()) {
> + /*
> + * It might be better to put PMU handling (at least for the
> + * host) in the perf subsystem because it knows more about what
> + * is being used.
> + */
> +
> + /* POWER9, POWER10 do not implement HPMC or SPMC */
> +
> + host_os_sprs->mmcr0 = mfspr(SPRN_MMCR0);
> + host_os_sprs->mmcra = mfspr(SPRN_MMCRA);
> +
> + freeze_pmu(host_os_sprs->mmcr0, host_os_sprs->mmcra);
> +
> + host_os_sprs->pmc1 = mfspr(SPRN_PMC1);
> + host_os_sprs->pmc2 = mfspr(SPRN_PMC2);
> + host_os_sprs->pmc3 = mfspr(SPRN_PMC3);
> + host_os_sprs->pmc4 = mfspr(SPRN_PMC4);
> + host_os_sprs->pmc5 = mfspr(SPRN_PMC5);
> + host_os_sprs->pmc6 = mfspr(SPRN_PMC6);
> + host_os_sprs->mmcr1 = mfspr(SPRN_MMCR1);
> + host_os_sprs->mmcr2 = mfspr(SPRN_MMCR2);
> + host_os_sprs->sdar = mfspr(SPRN_SDAR);
> + host_os_sprs->siar = mfspr(SPRN_SIAR);
> + host_os_sprs->sier1 = mfspr(SPRN_SIER);
> +
> + if (cpu_has_feature(CPU_FTR_ARCH_31)) {
> + host_os_sprs->mmcr3 = mfspr(SPRN_MMCR3);
> + host_os_sprs->sier2 = mfspr(SPRN_SIER2);
> + host_os_sprs->sier3 = mfspr(SPRN_SIER3);
> + }
> + }
> +
> +#ifdef CONFIG_PPC_PSERIES
> + if (kvmhv_on_pseries()) {
> + if (vcpu->arch.vpa.pinned_addr) {
> + struct lppaca *lp = vcpu->arch.vpa.pinned_addr;
> + get_lppaca()->pmcregs_in_use = lp->pmcregs_in_use;
> + } else {
> + get_lppaca()->pmcregs_in_use = 1;
> + }
> + }
> +#endif
> +
> + /* Load guest */
> + if (vcpu->arch.hfscr & HFSCR_PM) {
> + mtspr(SPRN_PMC1, vcpu->arch.pmc[0]);
> + mtspr(SPRN_PMC2, vcpu->arch.pmc[1]);
> + mtspr(SPRN_PMC3, vcpu->arch.pmc[2]);
> + mtspr(SPRN_PMC4, vcpu->arch.pmc[3]);
> + mtspr(SPRN_PMC5, vcpu->arch.pmc[4]);
> + mtspr(SPRN_PMC6, vcpu->arch.pmc[5]);
> + mtspr(SPRN_MMCR1, vcpu->arch.mmcr[1]);
> + mtspr(SPRN_MMCR2, vcpu->arch.mmcr[2]);
> + mtspr(SPRN_SDAR, vcpu->arch.sdar);
> + mtspr(SPRN_SIAR, vcpu->arch.siar);
> + mtspr(SPRN_SIER, vcpu->arch.sier[0]);
> +
> + if (cpu_has_feature(CPU_FTR_ARCH_31)) {
> + mtspr(SPRN_MMCR3, vcpu->arch.mmcr[4]);
> + mtspr(SPRN_SIER2, vcpu->arch.sier[1]);
> + mtspr(SPRN_SIER3, vcpu->arch.sier[2]);
> + }
> +
> + /* Set MMCRA then MMCR0 last */
> + mtspr(SPRN_MMCRA, vcpu->arch.mmcra);
> + mtspr(SPRN_MMCR0, vcpu->arch.mmcr[0]);
> + /* No isync necessary because we're starting counters */
> + }
> +}
> +EXPORT_SYMBOL_GPL(switch_pmu_to_guest);
> +
> +void switch_pmu_to_host(struct kvm_vcpu *vcpu,
> + struct p9_host_os_sprs *host_os_sprs)
> +{
> + struct lppaca *lp;
> + int save_pmu = 1;
> +
> + lp = vcpu->arch.vpa.pinned_addr;
> + if (lp)
> + save_pmu = lp->pmcregs_in_use;
> +
> + if (save_pmu) {
> + vcpu->arch.mmcr[0] = mfspr(SPRN_MMCR0);
> + vcpu->arch.mmcra = mfspr(SPRN_MMCRA);
> +
> + freeze_pmu(vcpu->arch.mmcr[0], vcpu->arch.mmcra);
> +
> + vcpu->arch.pmc[0] = mfspr(SPRN_PMC1);
> + vcpu->arch.pmc[1] = mfspr(SPRN_PMC2);
> + vcpu->arch.pmc[2] = mfspr(SPRN_PMC3);
> + vcpu->arch.pmc[3] = mfspr(SPRN_PMC4);
> + vcpu->arch.pmc[4] = mfspr(SPRN_PMC5);
> + vcpu->arch.pmc[5] = mfspr(SPRN_PMC6);
> + vcpu->arch.mmcr[1] = mfspr(SPRN_MMCR1);
> + vcpu->arch.mmcr[2] = mfspr(SPRN_MMCR2);
> + vcpu->arch.sdar = mfspr(SPRN_SDAR);
> + vcpu->arch.siar = mfspr(SPRN_SIAR);
> + vcpu->arch.sier[0] = mfspr(SPRN_SIER);
> +
> + if (cpu_has_feature(CPU_FTR_ARCH_31)) {
> + vcpu->arch.mmcr[3] = mfspr(SPRN_MMCR3);
> + vcpu->arch.sier[1] = mfspr(SPRN_SIER2);
> + vcpu->arch.sier[2] = mfspr(SPRN_SIER3);
> + }
> +
> + } else if (vcpu->arch.hfscr & HFSCR_PM) {
> + /*
> + * The guest accessed PMC SPRs without specifying they should
> + * be preserved. Stop them from counting if the guest had
> + * started anything.
> + */
> + freeze_pmu(mfspr(SPRN_MMCR0), mfspr(SPRN_MMCRA));
> +
> + /*
> + * Demand-fault PMU register access in the guest.
> + *
> + * This is used to grab the guest's VPA pmcregs_in_use value
> + * and reflect it into the host's VPA in the case of a nested
> + * hypervisor.
> + *
> + * It also avoids having to zero-out SPRs after each guest
> + * exit to avoid side-channels when.
> + *
> + * This is cleared here when we exit the guest, so later HFSCR
> + * interrupt handling can add it back to run the guest with
> + * PM enabled next time.
> + */
> + vcpu->arch.hfscr &= ~HFSCR_PM;
> + } /* otherwise the PMU should still be frozen from guest entry */
> +
> +
> +#ifdef CONFIG_PPC_PSERIES
> + if (kvmhv_on_pseries())
> + get_lppaca()->pmcregs_in_use = ppc_get_pmu_inuse();
> +#endif
> +
> + if (ppc_get_pmu_inuse()) {
> + mtspr(SPRN_PMC1, host_os_sprs->pmc1);
> + mtspr(SPRN_PMC2, host_os_sprs->pmc2);
> + mtspr(SPRN_PMC3, host_os_sprs->pmc3);
> + mtspr(SPRN_PMC4, host_os_sprs->pmc4);
> + mtspr(SPRN_PMC5, host_os_sprs->pmc5);
> + mtspr(SPRN_PMC6, host_os_sprs->pmc6);
> + mtspr(SPRN_MMCR1, host_os_sprs->mmcr1);
> + mtspr(SPRN_MMCR2, host_os_sprs->mmcr2);
> + mtspr(SPRN_SDAR, host_os_sprs->sdar);
> + mtspr(SPRN_SIAR, host_os_sprs->siar);
> + mtspr(SPRN_SIER, host_os_sprs->sier1);
> +
> + if (cpu_has_feature(CPU_FTR_ARCH_31)) {
> + mtspr(SPRN_MMCR3, host_os_sprs->mmcr3);
> + mtspr(SPRN_SIER2, host_os_sprs->sier2);
> + mtspr(SPRN_SIER3, host_os_sprs->sier3);
> + }
> +
> + /* Set MMCRA then MMCR0 last */
> + mtspr(SPRN_MMCRA, host_os_sprs->mmcra);
> + mtspr(SPRN_MMCR0, host_os_sprs->mmcr0);
> + isync();
> + }
> +}
> +EXPORT_SYMBOL_GPL(switch_pmu_to_host);
> +
> +static void load_spr_state(struct kvm_vcpu *vcpu,
> + struct p9_host_os_sprs *host_os_sprs)
> +{
> + mtspr(SPRN_TAR, vcpu->arch.tar);
> + mtspr(SPRN_EBBHR, vcpu->arch.ebbhr);
> + mtspr(SPRN_EBBRR, vcpu->arch.ebbrr);
> + mtspr(SPRN_BESCR, vcpu->arch.bescr);
> +
> + if (!cpu_has_feature(CPU_FTR_ARCH_31))
> + mtspr(SPRN_TIDR, vcpu->arch.tid);
> + if (host_os_sprs->iamr != vcpu->arch.iamr)
> + mtspr(SPRN_IAMR, vcpu->arch.iamr);
> + if (host_os_sprs->amr != vcpu->arch.amr)
> + mtspr(SPRN_AMR, vcpu->arch.amr);
> + if (vcpu->arch.uamor != 0)
> + mtspr(SPRN_UAMOR, vcpu->arch.uamor);
> + if (host_os_sprs->fscr != vcpu->arch.fscr)
> + mtspr(SPRN_FSCR, vcpu->arch.fscr);
> + if (host_os_sprs->dscr != vcpu->arch.dscr)
> + mtspr(SPRN_DSCR, vcpu->arch.dscr);
> + if (vcpu->arch.pspb != 0)
> + mtspr(SPRN_PSPB, vcpu->arch.pspb);
> +
> + /*
> + * DAR, DSISR, and for nested HV, SPRGs must be set with MSR[RI]
> + * clear (or hstate set appropriately to catch those registers
> + * being clobbered if we take a MCE or SRESET), so those are done
> + * later.
> + */
> +
> + if (!(vcpu->arch.ctrl & 1))
> + mtspr(SPRN_CTRLT, 0);
> +}
> +
> +static void store_spr_state(struct kvm_vcpu *vcpu)
> +{
> + vcpu->arch.tar = mfspr(SPRN_TAR);
> + vcpu->arch.ebbhr = mfspr(SPRN_EBBHR);
> + vcpu->arch.ebbrr = mfspr(SPRN_EBBRR);
> + vcpu->arch.bescr = mfspr(SPRN_BESCR);
> +
> + if (!cpu_has_feature(CPU_FTR_ARCH_31))
> + vcpu->arch.tid = mfspr(SPRN_TIDR);
> + vcpu->arch.iamr = mfspr(SPRN_IAMR);
> + vcpu->arch.amr = mfspr(SPRN_AMR);
> + vcpu->arch.uamor = mfspr(SPRN_UAMOR);
> + vcpu->arch.fscr = mfspr(SPRN_FSCR);
> + vcpu->arch.dscr = mfspr(SPRN_DSCR);
> + vcpu->arch.pspb = mfspr(SPRN_PSPB);
> +
> + vcpu->arch.ctrl = mfspr(SPRN_CTRLF);
> +}
> +
> +void load_vcpu_state(struct kvm_vcpu *vcpu,
> + struct p9_host_os_sprs *host_os_sprs)
> +{
> + if (cpu_has_feature(CPU_FTR_TM) ||
> + cpu_has_feature(CPU_FTR_P9_TM_HV_ASSIST))
> + kvmppc_restore_tm_hv(vcpu, vcpu->arch.shregs.msr, true);
> +
> + load_spr_state(vcpu, host_os_sprs);
> +
> + load_fp_state(&vcpu->arch.fp);
> +#ifdef CONFIG_ALTIVEC
> + load_vr_state(&vcpu->arch.vr);
> +#endif
> + mtspr(SPRN_VRSAVE, vcpu->arch.vrsave);
> +}
> +EXPORT_SYMBOL_GPL(load_vcpu_state);
> +
> +void store_vcpu_state(struct kvm_vcpu *vcpu)
> +{
> + store_spr_state(vcpu);
> +
> + store_fp_state(&vcpu->arch.fp);
> +#ifdef CONFIG_ALTIVEC
> + store_vr_state(&vcpu->arch.vr);
> +#endif
> + vcpu->arch.vrsave = mfspr(SPRN_VRSAVE);
> +
> + if (cpu_has_feature(CPU_FTR_TM) ||
> + cpu_has_feature(CPU_FTR_P9_TM_HV_ASSIST))
> + kvmppc_save_tm_hv(vcpu, vcpu->arch.shregs.msr, true);
> +}
> +EXPORT_SYMBOL_GPL(store_vcpu_state);
> +
> +void save_p9_host_os_sprs(struct p9_host_os_sprs *host_os_sprs)
> +{
> + if (!cpu_has_feature(CPU_FTR_ARCH_31))
> + host_os_sprs->tidr = mfspr(SPRN_TIDR);
> + host_os_sprs->iamr = mfspr(SPRN_IAMR);
> + host_os_sprs->amr = mfspr(SPRN_AMR);
> + host_os_sprs->fscr = mfspr(SPRN_FSCR);
> + host_os_sprs->dscr = mfspr(SPRN_DSCR);
> +}
> +EXPORT_SYMBOL_GPL(save_p9_host_os_sprs);
> +
> +/* vcpu guest regs must already be saved */
> +void restore_p9_host_os_sprs(struct kvm_vcpu *vcpu,
> + struct p9_host_os_sprs *host_os_sprs)
> +{
> + mtspr(SPRN_SPRG_VDSO_WRITE, local_paca->sprg_vdso);
> +
> + if (!cpu_has_feature(CPU_FTR_ARCH_31))
> + mtspr(SPRN_TIDR, host_os_sprs->tidr);
> + if (host_os_sprs->iamr != vcpu->arch.iamr)
> + mtspr(SPRN_IAMR, host_os_sprs->iamr);
> + if (vcpu->arch.uamor != 0)
> + mtspr(SPRN_UAMOR, 0);
> + if (host_os_sprs->amr != vcpu->arch.amr)
> + mtspr(SPRN_AMR, host_os_sprs->amr);
> + if (host_os_sprs->fscr != vcpu->arch.fscr)
> + mtspr(SPRN_FSCR, host_os_sprs->fscr);
> + if (host_os_sprs->dscr != vcpu->arch.dscr)
> + mtspr(SPRN_DSCR, host_os_sprs->dscr);
> + if (vcpu->arch.pspb != 0)
> + mtspr(SPRN_PSPB, 0);
> +
> + /* Save guest CTRL register, set runlatch to 1 */
> + if (!(vcpu->arch.ctrl & 1))
> + mtspr(SPRN_CTRLT, 1);
> +}
> +EXPORT_SYMBOL_GPL(restore_p9_host_os_sprs);
> +
> #ifdef CONFIG_KVM_BOOK3S_HV_EXIT_TIMING
> static void __start_timing(struct kvm_vcpu *vcpu, struct kvmhv_tb_accumulator *next)
> {
> --
> 2.23.0
>
More information about the Linuxppc-dev
mailing list