From mboxrd@z Thu Jan 1 00:00:00 1970 From: David Gibson Date: Wed, 26 Sep 2018 05:41:07 +0000 Subject: Re: [RFC PATCH 20/32] KVM: PPC: Book3S HV: Nested guest entry via hypercall Message-Id: <20180926054107.GW30868@umbus.fritz.box> MIME-Version: 1 Content-Type: multipart/mixed; boundary="LOiyZ6wsohFvXHPb" List-Id: References: <1537524123-9578-21-git-send-email-paulus@ozlabs.org> In-Reply-To: <1537524123-9578-21-git-send-email-paulus@ozlabs.org> To: kvm-ppc@vger.kernel.org --LOiyZ6wsohFvXHPb Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable On Fri, Sep 21, 2018 at 08:01:51PM +1000, Paul Mackerras wrote: > This adds a new hypercall, H_ENTER_NESTED, which is used by a nested > hypervisor to enter one of its nested guests. The hypercall supplies > register values in two structs. Those values are copied by the level 0 > (L0) hypervisor (the one which is running in hypervisor mode) into the > vcpu struct of the L1 guest, and then the guest is run until an > interrupt or error occurs which needs to be reported to L1 via the > hypercall return value. >=20 > Currently this assumes that the L0 and L1 hypervisors are the same > endianness, and the structs passed as arguments are in native > endianness. That's nasty. It'd be good to at least detect this and bail. >=20 > Nested hypervisors do not support indep_threads_mode=3DN, so this adds > code to print a warning message if the administrator has set > indep_threads_mode=3DN, and treat it as Y. >=20 > Signed-off-by: Paul Mackerras > --- > arch/powerpc/include/asm/hvcall.h | 36 +++++ > arch/powerpc/include/asm/kvm_book3s.h | 7 + > arch/powerpc/include/asm/kvm_host.h | 5 + > arch/powerpc/kernel/asm-offsets.c | 1 + > arch/powerpc/kvm/book3s_hv.c | 194 +++++++++++++++++++++++---- > arch/powerpc/kvm/book3s_hv_nested.c | 230 ++++++++++++++++++++++++++= ++++++ > arch/powerpc/kvm/book3s_hv_rmhandlers.S | 4 + > 7 files changed, 452 insertions(+), 25 deletions(-) >=20 > diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm= /hvcall.h > index 9afaa82..dfcf43d 100644 > --- a/arch/powerpc/include/asm/hvcall.h > +++ b/arch/powerpc/include/asm/hvcall.h > @@ -487,6 +487,42 @@ struct h_cpu_char_result { > u64 behaviour; > }; > =20 > +/* Register state for entering a nested guest with H_ENTER_NESTED */ > +struct hv_guest_state { > + u64 version; /* version of this structure layout */ > + u32 lpid; > + u32 vcpu_token; > + /* These registers are hypervisor privileged (at least for writing) */ > + u64 lpcr; > + u64 pcr; > + u64 amor; > + u64 dpdes; > + u64 hfscr; > + s64 tb_offset; > + u64 dawr0; > + u64 dawrx0; > + u64 ciabr; > + u64 hdec_expiry; > + u64 purr; > + u64 spurr; > + u64 ic; > + u64 vtb; > + u64 hdar; > + u64 hdsisr; > + u64 heir; > + u64 asdr; > + /* These are OS privileged but need to be set late in guest entry */ > + u64 srr0; > + u64 srr1; > + u64 sprg[4]; > + u64 pidr; > + u64 cfar; > + u64 ppr; > +}; > + > +/* Latest version of hv_guest_state structure */ > +#define HV_GUEST_STATE_VERSION 1 > + > #endif /* __ASSEMBLY__ */ > #endif /* __KERNEL__ */ > #endif /* _ASM_POWERPC_HVCALL_H */ > diff --git a/arch/powerpc/include/asm/kvm_book3s.h b/arch/powerpc/include= /asm/kvm_book3s.h > index 7719ca5..125bc5b 100644 > --- a/arch/powerpc/include/asm/kvm_book3s.h > +++ b/arch/powerpc/include/asm/kvm_book3s.h > @@ -280,6 +280,13 @@ void kvmhv_vm_nested_init(struct kvm *kvm); > long kvmhv_set_partition_table(struct kvm_vcpu *vcpu); > void kvmhv_set_ptbl_entry(unsigned int lpid, u64 dw0, u64 dw1); > void kvmhv_release_all_nested(struct kvm *kvm); > +long kvmhv_enter_nested_guest(struct kvm_vcpu *vcpu); > +int kvmhv_run_single_vcpu(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu, > + u64 time_limit); > +void kvmhv_save_hv_regs(struct kvm_vcpu *vcpu, struct hv_guest_state *hr= ); > +void kvmhv_restore_hv_return_state(struct kvm_vcpu *vcpu, > + struct hv_guest_state *hr); > +long int kvmhv_nested_page_fault(struct kvm_vcpu *vcpu); > =20 > void kvmppc_giveup_fac(struct kvm_vcpu *vcpu, ulong fac); > =20 > diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/a= sm/kvm_host.h > index c35d4f2..ceb9f20 100644 > --- a/arch/powerpc/include/asm/kvm_host.h > +++ b/arch/powerpc/include/asm/kvm_host.h > @@ -95,6 +95,7 @@ struct dtl_entry; > =20 > struct kvmppc_vcpu_book3s; > struct kvmppc_book3s_shadow_vcpu; > +struct kvm_nested_guest; > =20 > struct kvm_vm_stat { > ulong remote_tlb_flush; > @@ -786,6 +787,10 @@ struct kvm_vcpu_arch { > u32 emul_inst; > =20 > u32 online; > + > + /* For support of nested guests */ > + struct kvm_nested_guest *nested; > + u32 nested_vcpu_id; > #endif > =20 > #ifdef CONFIG_KVM_BOOK3S_HV_EXIT_TIMING > diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-= offsets.c > index 7c3738d..d0abcbb 100644 > --- a/arch/powerpc/kernel/asm-offsets.c > +++ b/arch/powerpc/kernel/asm-offsets.c > @@ -503,6 +503,7 @@ int main(void) > OFFSET(VCPU_VPA, kvm_vcpu, arch.vpa.pinned_addr); > OFFSET(VCPU_VPA_DIRTY, kvm_vcpu, arch.vpa.dirty); > OFFSET(VCPU_HEIR, kvm_vcpu, arch.emul_inst); > + OFFSET(VCPU_NESTED, kvm_vcpu, arch.nested); > OFFSET(VCPU_CPU, kvm_vcpu, cpu); > OFFSET(VCPU_THREAD_CPU, kvm_vcpu, arch.thread_cpu); > #endif > diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c > index 82c9a1e..da380cb 100644 > --- a/arch/powerpc/kvm/book3s_hv.c > +++ b/arch/powerpc/kvm/book3s_hv.c > @@ -942,6 +942,13 @@ int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu) > break; > case H_ENTER_NESTED: > ret =3D H_FUNCTION; > + if (!vcpu->kvm->arch.nested_enable) > + break; Wouldn't H_AUTHORITY make more sense than H_FUNCTION for the no-nested-allowed case? > + ret =3D kvmhv_enter_nested_guest(vcpu); > + if (ret =3D=3D H_INTERRUPT) { > + kvmppc_set_gpr(vcpu, 3, 0); > + return RESUME_HOST; > + } > break; > =20 > default: > @@ -1269,6 +1276,104 @@ static int kvmppc_handle_exit_hv(struct kvm_run *= run, struct kvm_vcpu *vcpu, > return r; > } > =20 > +static int kvmppc_handle_nested_exit(struct kvm_vcpu *vcpu) Might be nice to rename to make it clear if this is the L0 or L1 handling for a nested exit. > +{ > + int r; > + int srcu_idx; > + > + vcpu->stat.sum_exits++; > + > + /* > + * This can happen if an interrupt occurs in the last stages > + * of guest entry or the first stages of guest exit (i.e. after > + * setting paca->kvm_hstate.in_guest to KVM_GUEST_MODE_GUEST_HV > + * and before setting it to KVM_GUEST_MODE_HOST_HV). > + * That can happen due to a bug, or due to a machine check > + * occurring at just the wrong time. > + */ > + if (vcpu->arch.shregs.msr & MSR_HV) { > + printk(KERN_EMERG "KVM trap in HV mode while nested!\n"); > + printk(KERN_EMERG "trap=3D0x%x | pc=3D0x%lx | msr=3D0x%llx\n", > + vcpu->arch.trap, kvmppc_get_pc(vcpu), > + vcpu->arch.shregs.msr); > + kvmppc_dump_regs(vcpu); > + BUG(); > + } > + switch (vcpu->arch.trap) { > + /* We're good on these - the host merely wanted to get our attention */ > + case BOOK3S_INTERRUPT_HV_DECREMENTER: > + vcpu->stat.dec_exits++; > + r =3D RESUME_GUEST; > + break; > + case BOOK3S_INTERRUPT_EXTERNAL: > + vcpu->stat.ext_intr_exits++; > + r =3D RESUME_HOST; > + break; > + case BOOK3S_INTERRUPT_H_DOORBELL: > + case BOOK3S_INTERRUPT_H_VIRT: > + vcpu->stat.ext_intr_exits++; > + r =3D RESUME_GUEST; > + break; > + /* SR/HMI/PMI are HV interrupts that host has handled. Resume guest.*/ > + case BOOK3S_INTERRUPT_HMI: > + case BOOK3S_INTERRUPT_PERFMON: > + case BOOK3S_INTERRUPT_SYSTEM_RESET: > + r =3D RESUME_GUEST; > + break; > + case BOOK3S_INTERRUPT_MACHINE_CHECK: > + /* Pass the machine check to the L1 guest */ > + r =3D RESUME_HOST; > + /* Print the MCE event to host console. */ > + machine_check_print_event_info(&vcpu->arch.mce_evt, false); > + break; > + /* > + * We get these next two if the guest accesses a page which it thinks > + * it has mapped but which is not actually present, either because > + * it is for an emulated I/O device or because the corresonding > + * host page has been paged out. > + */ > + case BOOK3S_INTERRUPT_H_DATA_STORAGE: > + srcu_idx =3D srcu_read_lock(&vcpu->kvm->srcu); > + r =3D kvmhv_nested_page_fault(vcpu); > + srcu_read_unlock(&vcpu->kvm->srcu, srcu_idx); > + break; > + case BOOK3S_INTERRUPT_H_INST_STORAGE: > + vcpu->arch.fault_dar =3D kvmppc_get_pc(vcpu); > + vcpu->arch.fault_dsisr =3D kvmppc_get_msr(vcpu) & > + DSISR_SRR1_MATCH_64S; > + if (vcpu->arch.shregs.msr & HSRR1_HISI_WRITE) > + vcpu->arch.fault_dsisr |=3D DSISR_ISSTORE; > + srcu_idx =3D srcu_read_lock(&vcpu->kvm->srcu); > + r =3D kvmhv_nested_page_fault(vcpu); > + srcu_read_unlock(&vcpu->kvm->srcu, srcu_idx); > + break; > + > +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM > + case BOOK3S_INTERRUPT_HV_SOFTPATCH: > + /* > + * This occurs for various TM-related instructions that > + * we need to emulate on POWER9 DD2.2. We have already > + * handled the cases where the guest was in real-suspend > + * mode and was transitioning to transactional state. > + */ > + r =3D kvmhv_p9_tm_emulation(vcpu); > + break; > +#endif > + > + case BOOK3S_INTERRUPT_HV_RM_HARD: > + vcpu->arch.trap =3D 0; > + r =3D RESUME_GUEST; > + if (!xive_enabled()) > + kvmppc_xics_rm_complete(vcpu, 0); > + break; > + default: > + r =3D RESUME_HOST; > + break; > + } > + > + return r; > +} > + > static int kvm_arch_vcpu_ioctl_get_sregs_hv(struct kvm_vcpu *vcpu, > struct kvm_sregs *sregs) > { > @@ -3095,7 +3200,7 @@ static noinline void kvmppc_run_core(struct kvmppc_= vcore *vc) > /* > * Load up hypervisor-mode registers on P9. > */ > -static int kvmhv_load_hv_regs_and_go(struct kvm_vcpu *vcpu) > +static int kvmhv_load_hv_regs_and_go(struct kvm_vcpu *vcpu, u64 time_lim= it) > { > struct kvmppc_vcore *vc =3D vcpu->arch.vcore; > s64 hdec; > @@ -3108,7 +3213,7 @@ static int kvmhv_load_hv_regs_and_go(struct kvm_vcp= u *vcpu) > unsigned long host_psscr =3D mfspr(SPRN_PSSCR); > unsigned long host_pidr =3D mfspr(SPRN_PID); > =20 > - hdec =3D local_paca->kvm_hstate.dec_expires - mftb(); > + hdec =3D time_limit - mftb(); Looks like this change might better belong in the earlier patch creating kvmhv_load_hv_regs_and_go(). > if (hdec < 0) > return BOOK3S_INTERRUPT_HV_DECREMENTER; > mtspr(SPRN_HDEC, hdec); > @@ -3222,7 +3327,7 @@ static int kvmhv_load_hv_regs_and_go(struct kvm_vcp= u *vcpu) > * Virtual-mode guest entry for POWER9 and later when the host and > * guest are both using the radix MMU. The LPIDR has already been set. > */ > -int kvmhv_p9_guest_entry(struct kvm_vcpu *vcpu) > +int kvmhv_p9_guest_entry(struct kvm_vcpu *vcpu, u64 time_limit) > { > struct kvmppc_vcore *vc =3D vcpu->arch.vcore; > unsigned long host_dscr =3D mfspr(SPRN_DSCR); > @@ -3237,6 +3342,8 @@ int kvmhv_p9_guest_entry(struct kvm_vcpu *vcpu) > if (dec < 512) > return BOOK3S_INTERRUPT_HV_DECREMENTER; > local_paca->kvm_hstate.dec_expires =3D dec + tb; > + if (local_paca->kvm_hstate.dec_expires < time_limit) > + time_limit =3D local_paca->kvm_hstate.dec_expires; > =20 > vcpu->arch.ceded =3D 0; > =20 > @@ -3290,7 +3397,28 @@ int kvmhv_p9_guest_entry(struct kvm_vcpu *vcpu) > vcpu->arch.doorbell_request =3D 0; > } > =20 > - trap =3D kvmhv_load_hv_regs_and_go(vcpu); > + if (!cpu_has_feature(CPU_FTR_HVMODE)) { > + /* call our hypervisor to load up HV regs and go */ > + struct hv_guest_state hvregs; > + > + kvmhv_save_hv_regs(vcpu, &hvregs); > + vcpu->arch.regs.msr =3D vcpu->arch.shregs.msr; > + hvregs.version =3D HV_GUEST_STATE_VERSION; > + hvregs.lpid =3D vcpu->kvm->arch.lpid; > + hvregs.vcpu_token =3D vcpu->vcpu_id; > + hvregs.hdec_expiry =3D time_limit; > + if (test_bit(BOOK3S_IRQPRIO_EXTERNAL, > + &vcpu->arch.pending_exceptions)) > + hvregs.lpcr |=3D LPCR_MER; > + trap =3D plpar_hcall_norets(H_ENTER_NESTED, __pa(&hvregs), > + __pa(&vcpu->arch.regs)); > + kvmhv_restore_hv_return_state(vcpu, &hvregs); > + vcpu->arch.shregs.msr =3D vcpu->arch.regs.msr; > + vcpu->arch.shregs.dar =3D mfspr(SPRN_DAR); > + vcpu->arch.shregs.dsisr =3D mfspr(SPRN_DSISR); > + } else { > + trap =3D kvmhv_load_hv_regs_and_go(vcpu, time_limit); > + } > =20 > vcpu->arch.slb_max =3D 0; > dec =3D mfspr(SPRN_DEC); > @@ -3530,6 +3658,10 @@ static void kvmppc_vcore_blocked(struct kvmppc_vco= re *vc) > trace_kvmppc_vcore_wakeup(do_sleep, block_ns); > } > =20 > +/* > + * This is assumed not to be able to fail for a radix guest in > + * kvmhv_run_single_vcpu(). > + */ Might be clearer to split that change out with a rationale as to why it's correct. > static int kvmhv_setup_mmu(struct kvm_vcpu *vcpu) > { > int r =3D 0; > @@ -3679,12 +3811,14 @@ static int kvmppc_run_vcpu(struct kvm_run *kvm_ru= n, struct kvm_vcpu *vcpu) > return vcpu->arch.ret; > } > =20 > -static int kvmppc_run_single_vcpu(struct kvm_run *kvm_run, > - struct kvm_vcpu *vcpu) > +int kvmhv_run_single_vcpu(struct kvm_run *kvm_run, > + struct kvm_vcpu *vcpu, u64 time_limit) > { > int trap, r, pcpu, pcpu0; > int srcu_idx; > struct kvmppc_vcore *vc; > + struct kvm_nested_guest *nested =3D vcpu->arch.nested; > + unsigned long lpid; > =20 > trace_kvmppc_run_vcpu_enter(vcpu); > =20 > @@ -3705,16 +3839,8 @@ static int kvmppc_run_single_vcpu(struct kvm_run *= kvm_run, > vc->runner =3D vcpu; > =20 > /* See if the MMU is ready to go */ > - if (!vcpu->kvm->arch.mmu_ready) { > - r =3D kvmhv_setup_mmu(vcpu); > - if (r) { > - kvm_run->exit_reason =3D KVM_EXIT_FAIL_ENTRY; > - kvm_run->fail_entry. > - hardware_entry_failure_reason =3D 0; > - vcpu->arch.ret =3D r; > - goto out; > - } > - } > + if (!vcpu->kvm->arch.mmu_ready) > + kvmhv_setup_mmu(vcpu); > =20 > if (need_resched()) > cond_resched(); > @@ -3736,7 +3862,12 @@ static int kvmppc_run_single_vcpu(struct kvm_run *= kvm_run, > if (lazy_irq_pending() || need_resched() || !vcpu->kvm->arch.mmu_ready) > goto out; > =20 > - kvmppc_core_prepare_to_enter(vcpu); > + if (!nested) { > + kvmppc_core_prepare_to_enter(vcpu); > + } else if (vcpu->arch.pending_exceptions) { > + vcpu->arch.ret =3D RESUME_HOST; > + goto out; > + } > =20 > kvmppc_clear_host_core(pcpu); > =20 > @@ -3750,7 +3881,10 @@ static int kvmppc_run_single_vcpu(struct kvm_run *= kvm_run, > vc->vcore_state =3D VCORE_RUNNING; > trace_kvmppc_run_core(vc, 0); > =20 > - mtspr(SPRN_LPID, vc->kvm->arch.lpid); > + lpid =3D vc->kvm->arch.lpid; > + if (nested) > + lpid =3D nested->shadow_lpid; > + mtspr(SPRN_LPID, lpid); > isync(); > =20 > /* See comment above in kvmppc_run_core() about this */ > @@ -3759,7 +3893,7 @@ static int kvmppc_run_single_vcpu(struct kvm_run *k= vm_run, > pcpu0 &=3D ~0x3UL; > =20 > if (cpumask_test_cpu(pcpu0, &vc->kvm->arch.need_tlb_flush)) { > - radix__local_flush_tlb_lpid_guest(vc->kvm->arch.lpid); > + radix__local_flush_tlb_lpid_guest(lpid); > /* Clear the bit after the TLB flush */ > cpumask_clear_cpu(pcpu0, &vc->kvm->arch.need_tlb_flush); > } > @@ -3771,7 +3905,7 @@ static int kvmppc_run_single_vcpu(struct kvm_run *k= vm_run, > =20 > this_cpu_disable_ftrace(); > =20 > - trap =3D kvmhv_p9_guest_entry(vcpu); > + trap =3D kvmhv_p9_guest_entry(vcpu, time_limit); > vcpu->arch.trap =3D trap; > =20 > this_cpu_enable_ftrace(); > @@ -3796,8 +3930,12 @@ static int kvmppc_run_single_vcpu(struct kvm_run *= kvm_run, > =20 > trace_kvm_guest_exit(vcpu); > r =3D RESUME_GUEST; > - if (trap) > - r =3D kvmppc_handle_exit_hv(kvm_run, vcpu, current); > + if (trap) { > + if (!nested) > + r =3D kvmppc_handle_exit_hv(kvm_run, vcpu, current); > + else > + r =3D kvmppc_handle_nested_exit(vcpu); > + } > vcpu->arch.ret =3D r; > =20 > if (is_kvmppc_resume_guest(r) && vcpu->arch.ceded && > @@ -3912,7 +4050,7 @@ static int kvmppc_vcpu_run_hv(struct kvm_run *run, = struct kvm_vcpu *vcpu) > =20 > do { > if (kvm->arch.threads_indep && kvm_is_radix(kvm)) > - r =3D kvmppc_run_single_vcpu(run, vcpu); > + r =3D kvmhv_run_single_vcpu(run, vcpu, ~(u64)0); > else > r =3D kvmppc_run_vcpu(run, vcpu); > =20 > @@ -4462,8 +4600,14 @@ static int kvmppc_core_init_vm_hv(struct kvm *kvm) > * On POWER9, we only need to do this if the "indep_threads_mode" > * module parameter has been set to N. > */ > - if (cpu_has_feature(CPU_FTR_ARCH_300)) > - kvm->arch.threads_indep =3D indep_threads_mode; > + if (cpu_has_feature(CPU_FTR_ARCH_300)) { > + if (!indep_threads_mode && !cpu_has_feature(CPU_FTR_HVMODE)) { > + pr_warn("KVM: Ignoring indep_threads_mode=3DN in nested hypervisor\n"= ); > + kvm->arch.threads_indep =3D true; Wouldn't it be cleaner to enforce this at the point indep_threads_mode is set, rather than altering the value here? > + } else { > + kvm->arch.threads_indep =3D indep_threads_mode; > + } > + } > if (!kvm->arch.threads_indep) > kvm_hv_vm_activated(); > =20 > diff --git a/arch/powerpc/kvm/book3s_hv_nested.c b/arch/powerpc/kvm/book3= s_hv_nested.c > index 5fe3ea4..a7f3da9 100644 > --- a/arch/powerpc/kvm/book3s_hv_nested.c > +++ b/arch/powerpc/kvm/book3s_hv_nested.c > @@ -20,6 +20,231 @@ static struct patb_entry *pseries_partition_tb; > =20 > static void kvmhv_update_ptbl_cache(struct kvm_nested_guest *gp); > =20 > +void kvmhv_save_hv_regs(struct kvm_vcpu *vcpu, struct hv_guest_state *hr) > +{ > + struct kvmppc_vcore *vc =3D vcpu->arch.vcore; > + > + hr->lpcr =3D vc->lpcr; > + hr->pcr =3D vc->pcr; > + hr->dpdes =3D vc->dpdes; > + hr->hfscr =3D vcpu->arch.hfscr; > + hr->tb_offset =3D vc->tb_offset; > + hr->dawr0 =3D vcpu->arch.dawr; > + hr->dawrx0 =3D vcpu->arch.dawrx; > + hr->ciabr =3D vcpu->arch.ciabr; > + hr->purr =3D vcpu->arch.purr; > + hr->spurr =3D vcpu->arch.spurr; > + hr->ic =3D vcpu->arch.ic; > + hr->vtb =3D vc->vtb; > + hr->srr0 =3D vcpu->arch.shregs.srr0; > + hr->srr1 =3D vcpu->arch.shregs.srr1; > + hr->sprg[0] =3D vcpu->arch.shregs.sprg0; > + hr->sprg[1] =3D vcpu->arch.shregs.sprg1; > + hr->sprg[2] =3D vcpu->arch.shregs.sprg2; > + hr->sprg[3] =3D vcpu->arch.shregs.sprg3; > + hr->pidr =3D vcpu->arch.pid; > + hr->cfar =3D vcpu->arch.cfar; > + hr->ppr =3D vcpu->arch.ppr; > +} > + > +static void save_hv_return_state(struct kvm_vcpu *vcpu, int trap, > + struct hv_guest_state *hr) > +{ > + struct kvmppc_vcore *vc =3D vcpu->arch.vcore; > + > + hr->dpdes =3D vc->dpdes; > + hr->hfscr =3D vcpu->arch.hfscr; > + hr->purr =3D vcpu->arch.purr; > + hr->spurr =3D vcpu->arch.spurr; > + hr->ic =3D vcpu->arch.ic; > + hr->vtb =3D vc->vtb; > + hr->srr0 =3D vcpu->arch.shregs.srr0; > + hr->srr1 =3D vcpu->arch.shregs.srr1; > + hr->sprg[0] =3D vcpu->arch.shregs.sprg0; > + hr->sprg[1] =3D vcpu->arch.shregs.sprg1; > + hr->sprg[2] =3D vcpu->arch.shregs.sprg2; > + hr->sprg[3] =3D vcpu->arch.shregs.sprg3; > + hr->pidr =3D vcpu->arch.pid; > + hr->cfar =3D vcpu->arch.cfar; > + hr->ppr =3D vcpu->arch.ppr; > + switch (trap) { > + case BOOK3S_INTERRUPT_H_DATA_STORAGE: > + hr->hdar =3D vcpu->arch.fault_dar; > + hr->hdsisr =3D vcpu->arch.fault_dsisr; > + hr->asdr =3D vcpu->arch.fault_gpa; > + break; > + case BOOK3S_INTERRUPT_H_INST_STORAGE: > + hr->asdr =3D vcpu->arch.fault_gpa; > + break; > + case BOOK3S_INTERRUPT_H_EMUL_ASSIST: > + hr->heir =3D vcpu->arch.emul_inst; > + break; > + } > +} > + > +static void restore_hv_regs(struct kvm_vcpu *vcpu, struct hv_guest_state= *hr) > +{ > + struct kvmppc_vcore *vc =3D vcpu->arch.vcore; > + > + vc->pcr =3D hr->pcr; > + vc->dpdes =3D hr->dpdes; > + vcpu->arch.hfscr =3D hr->hfscr; > + vcpu->arch.dawr =3D hr->dawr0; > + vcpu->arch.dawrx =3D hr->dawrx0; > + vcpu->arch.ciabr =3D hr->ciabr; > + vcpu->arch.purr =3D hr->purr; > + vcpu->arch.spurr =3D hr->spurr; > + vcpu->arch.ic =3D hr->ic; > + vc->vtb =3D hr->vtb; > + vcpu->arch.shregs.srr0 =3D hr->srr0; > + vcpu->arch.shregs.srr1 =3D hr->srr1; > + vcpu->arch.shregs.sprg0 =3D hr->sprg[0]; > + vcpu->arch.shregs.sprg1 =3D hr->sprg[1]; > + vcpu->arch.shregs.sprg2 =3D hr->sprg[2]; > + vcpu->arch.shregs.sprg3 =3D hr->sprg[3]; > + vcpu->arch.pid =3D hr->pidr; > + vcpu->arch.cfar =3D hr->cfar; > + vcpu->arch.ppr =3D hr->ppr; > +} > + > +void kvmhv_restore_hv_return_state(struct kvm_vcpu *vcpu, > + struct hv_guest_state *hr) > +{ > + struct kvmppc_vcore *vc =3D vcpu->arch.vcore; > + > + vc->dpdes =3D hr->dpdes; > + vcpu->arch.hfscr =3D hr->hfscr; > + vcpu->arch.purr =3D hr->purr; > + vcpu->arch.spurr =3D hr->spurr; > + vcpu->arch.ic =3D hr->ic; > + vc->vtb =3D hr->vtb; > + vcpu->arch.fault_dar =3D hr->hdar; > + vcpu->arch.fault_dsisr =3D hr->hdsisr; > + vcpu->arch.fault_gpa =3D hr->asdr; > + vcpu->arch.emul_inst =3D hr->heir; > + vcpu->arch.shregs.srr0 =3D hr->srr0; > + vcpu->arch.shregs.srr1 =3D hr->srr1; > + vcpu->arch.shregs.sprg0 =3D hr->sprg[0]; > + vcpu->arch.shregs.sprg1 =3D hr->sprg[1]; > + vcpu->arch.shregs.sprg2 =3D hr->sprg[2]; > + vcpu->arch.shregs.sprg3 =3D hr->sprg[3]; > + vcpu->arch.pid =3D hr->pidr; > + vcpu->arch.cfar =3D hr->cfar; > + vcpu->arch.ppr =3D hr->ppr; > +} > + > +long kvmhv_enter_nested_guest(struct kvm_vcpu *vcpu) > +{ > + long int err, r; > + struct kvm_nested_guest *l2; > + struct pt_regs l2_regs, saved_l1_regs; > + struct hv_guest_state l2_hv, saved_l1_hv; > + struct kvmppc_vcore *vc =3D vcpu->arch.vcore; > + u64 hv_ptr, regs_ptr; > + u64 hdec_exp; > + s64 delta_purr, delta_spurr, delta_ic, delta_vtb; > + u64 mask; > + > + if (!kvm_is_radix(vcpu->kvm)) > + return H_FUNCTION; Would it be safer / cleaner to have this instead check that the L1 has completed an H_SET_PARTITION_TABLE? Which wouldn't be allowed for an HPT guest. > + > + /* copy parameters in */ > + hv_ptr =3D kvmppc_get_gpr(vcpu, 4); > + err =3D kvm_vcpu_read_guest(vcpu, hv_ptr, &l2_hv, > + sizeof(struct hv_guest_state)); > + if (err) > + return H_PARAMETER; > + if (l2_hv.version !=3D HV_GUEST_STATE_VERSION) > + return H_P2; > + > + regs_ptr =3D kvmppc_get_gpr(vcpu, 5); > + err =3D kvm_vcpu_read_guest(vcpu, regs_ptr, &l2_regs, > + sizeof(struct pt_regs)); > + if (err) > + return H_PARAMETER; > + > + /* translate lpid */ > + l2 =3D kvmhv_get_nested(vcpu->kvm, l2_hv.lpid, true); > + if (!l2) > + return H_PARAMETER; > + if (!l2->l1_gr_to_hr) { > + mutex_lock(&l2->tlb_lock); > + kvmhv_update_ptbl_cache(l2); > + mutex_unlock(&l2->tlb_lock); > + } > + > + /* save l1 values of things */ > + vcpu->arch.regs.msr =3D vcpu->arch.shregs.msr; > + saved_l1_regs =3D vcpu->arch.regs; > + kvmhv_save_hv_regs(vcpu, &saved_l1_hv); > + > + /* convert TB values/offsets to host (L0) values */ > + hdec_exp =3D l2_hv.hdec_expiry - vc->tb_offset; > + vc->tb_offset +=3D l2_hv.tb_offset; > + > + /* set L1 state to L2 state */ > + vcpu->arch.nested =3D l2; > + vcpu->arch.nested_vcpu_id =3D l2_hv.vcpu_token; > + vcpu->arch.regs =3D l2_regs; > + vcpu->arch.shregs.msr =3D vcpu->arch.regs.msr; > + mask =3D LPCR_DPFD | LPCR_ILE | LPCR_TC | LPCR_AIL | LPCR_LD | > + LPCR_LPES | LPCR_MER; > + vc->lpcr =3D (vc->lpcr & ~mask) | (l2_hv.lpcr & mask); > + restore_hv_regs(vcpu, &l2_hv); > + > + vcpu->arch.ret =3D RESUME_GUEST; > + vcpu->arch.trap =3D 0; > + do { > + if (mftb() >=3D hdec_exp) { > + vcpu->arch.trap =3D BOOK3S_INTERRUPT_HV_DECREMENTER; > + r =3D RESUME_HOST; > + break; > + } > + r =3D kvmhv_run_single_vcpu(vcpu->arch.kvm_run, vcpu, hdec_exp); > + } while (is_kvmppc_resume_guest(r)); > + > + /* save L2 state for return */ > + l2_regs =3D vcpu->arch.regs; > + l2_regs.msr =3D vcpu->arch.shregs.msr; > + delta_purr =3D vcpu->arch.purr - l2_hv.purr; > + delta_spurr =3D vcpu->arch.spurr - l2_hv.spurr; > + delta_ic =3D vcpu->arch.ic - l2_hv.ic; > + delta_vtb =3D vc->vtb - l2_hv.vtb; > + save_hv_return_state(vcpu, vcpu->arch.trap, &l2_hv); > + > + /* restore L1 state */ > + vcpu->arch.nested =3D NULL; > + vcpu->arch.regs =3D saved_l1_regs; > + vcpu->arch.shregs.msr =3D saved_l1_regs.msr & ~MSR_TS_MASK; > + /* set L1 MSR TS field according to L2 transaction state */ > + if (l2_regs.msr & MSR_TS_MASK) > + vcpu->arch.shregs.msr |=3D MSR_TS_S; > + vc->lpcr =3D saved_l1_hv.lpcr; > + vc->tb_offset =3D saved_l1_hv.tb_offset; > + restore_hv_regs(vcpu, &saved_l1_hv); > + vcpu->arch.purr +=3D delta_purr; > + vcpu->arch.spurr +=3D delta_spurr; > + vcpu->arch.ic +=3D delta_ic; > + vc->vtb +=3D delta_vtb; > + > + kvmhv_put_nested(l2); > + > + /* copy l2_hv_state and regs back to guest */ > + err =3D kvm_vcpu_write_guest(vcpu, hv_ptr, &l2_hv, > + sizeof(struct hv_guest_state)); > + if (err) > + return H_AUTHORITY; > + err =3D kvm_vcpu_write_guest(vcpu, regs_ptr, &l2_regs, > + sizeof(struct pt_regs)); > + if (err) > + return H_AUTHORITY; > + > + if (r =3D=3D -EINTR) > + return H_INTERRUPT; > + > + return vcpu->arch.trap; > +} > + > /* Only called when we're not in hypervisor mode */ > bool kvmhv_nested_init(void) > { > @@ -284,3 +509,8 @@ struct kvm_nested_guest *kvmhv_find_nested(struct kvm= *kvm, int lpid) > return NULL; > return kvm->arch.nested_guests[lpid]; > } > + > +long kvmhv_nested_page_fault(struct kvm_vcpu *vcpu) > +{ > + return RESUME_HOST; > +} > diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/b= ook3s_hv_rmhandlers.S > index 83efc13..04fcaa4 100644 > --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S > +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S > @@ -2199,6 +2199,10 @@ hcall_try_real_mode: > andi. r0,r11,MSR_PR > /* sc 1 from userspace - reflect to guest syscall */ > bne sc_1_fast_return > + /* sc 1 from nested guest - give it to L1 to handle */ > + ld r0, VCPU_NESTED(r9) > + cmpdi r0, 0 > + bne guest_exit_cont > clrrdi r3,r3,2 > cmpldi r3,hcall_real_table_end - hcall_real_table > bge guest_exit_cont --=20 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 --LOiyZ6wsohFvXHPb Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iQIzBAEBCAAdFiEEdfRlhq5hpmzETofcbDjKyiDZs5IFAlurG/AACgkQbDjKyiDZ s5LDbw//fzTN1hZbfVCP1KgCokvAW9rNbX6V/c3BoIJLTtFrtI8WThQgsK8bBtLJ WaLpLEJclph6KgyJynjahVCzT2jkqLevV9pDFspD40n4U9LdR1JkfpB/uha8kPG9 R5PVZKUvkw/08YUy6UdGIW5FWZA6NSTqLMqAtiPwhAMqTCuLUxknGHsYAGiuwxHu +ROdJ9utD+PVinUvAq5SNN1oU/q2HfVDu12Xc1Y1ZE/9vYjpl6yfZqyfBGbD8cOS 7c8Xgk4/IDMLg+v5ZdP9Fn9PdK1YE/zaNR2+vG1xYX5U4Rimf71zywx3oaNnCUTs g3cHGhqf9fTEsN+pJ0LctY+cChz/e7i0PnoO81P5/uJj/HbwviB0Y9n1FPsK2qeg Y9pka6DrZBHF5d95En2kQ4MMM/BFUzGGxLCe+KLWsAZa/0Oro5nrbR/Ix43rgfgB 0atMo3MJ0CbOGudMZLmrSo3PWwSQ4rS2nhjnFoWmM+9xOnmkHfD6SAlWKj02ZJ/q 3bg9roH0+J+5YKzJ2LdKAG8e3nrvdqqSK2ac0QTKOid5ngPMmpweg5btHYDYdAJ+ +Hd3uszwTcAgSGfo7viNVlC43grZxZQQVwITmn5kIsdqJEuezTLDkppgBkApoXJV 6E8qiw8LqjgqW78acisjbF//QvGRINqUQdexWtw+6ZJBAE5rStY= =qoXE -----END PGP SIGNATURE----- --LOiyZ6wsohFvXHPb--