| From bdf7509bbefa20855d5f6bacdc5b62a8489477c9 Mon Sep 17 00:00:00 2001 |
| From: Claudio Imbrenda <imbrenda@linux.ibm.com> |
| Date: Mon, 1 Feb 2021 21:54:13 +0100 |
| Subject: KVM: s390: VSIE: correctly handle MVPG when in VSIE |
| |
| From: Claudio Imbrenda <imbrenda@linux.ibm.com> |
| |
| commit bdf7509bbefa20855d5f6bacdc5b62a8489477c9 upstream. |
| |
| Correctly handle the MVPG instruction when issued by a VSIE guest. |
| |
| Fixes: a3508fbe9dc6d ("KVM: s390: vsie: initial support for nested virtualization") |
| Cc: stable@vger.kernel.org # f85f1baaa189: KVM: s390: split kvm_s390_logical_to_effective |
| Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com> |
| Acked-by: Janosch Frank <frankja@linux.ibm.com> |
| Reviewed-by: David Hildenbrand <david@redhat.com> |
| Acked-by: Christian Borntraeger <borntraeger@de.ibm.com> |
| Link: https://lore.kernel.org/r/20210302174443.514363-4-imbrenda@linux.ibm.com |
| [borntraeger@de.ibm.com: apply fixup from Claudio] |
| Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| arch/s390/kvm/vsie.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++--- |
| 1 file changed, 93 insertions(+), 5 deletions(-) |
| |
| --- a/arch/s390/kvm/vsie.c |
| +++ b/arch/s390/kvm/vsie.c |
| @@ -416,11 +416,6 @@ static void unshadow_scb(struct kvm_vcpu |
| memcpy((void *)((u64)scb_o + 0xc0), |
| (void *)((u64)scb_s + 0xc0), 0xf0 - 0xc0); |
| break; |
| - case ICPT_PARTEXEC: |
| - /* MVPG only */ |
| - memcpy((void *)((u64)scb_o + 0xc0), |
| - (void *)((u64)scb_s + 0xc0), 0xd0 - 0xc0); |
| - break; |
| } |
| |
| if (scb_s->ihcpu != 0xffffU) |
| @@ -983,6 +978,95 @@ static int handle_stfle(struct kvm_vcpu |
| } |
| |
| /* |
| + * Get a register for a nested guest. |
| + * @vcpu the vcpu of the guest |
| + * @vsie_page the vsie_page for the nested guest |
| + * @reg the register number, the upper 4 bits are ignored. |
| + * returns: the value of the register. |
| + */ |
| +static u64 vsie_get_register(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page, u8 reg) |
| +{ |
| + /* no need to validate the parameter and/or perform error handling */ |
| + reg &= 0xf; |
| + switch (reg) { |
| + case 15: |
| + return vsie_page->scb_s.gg15; |
| + case 14: |
| + return vsie_page->scb_s.gg14; |
| + default: |
| + return vcpu->run->s.regs.gprs[reg]; |
| + } |
| +} |
| + |
| +static int vsie_handle_mvpg(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) |
| +{ |
| + struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s; |
| + unsigned long pei_dest, pei_src, src, dest, mask; |
| + u64 *pei_block = &vsie_page->scb_o->mcic; |
| + int edat, rc_dest, rc_src; |
| + union ctlreg0 cr0; |
| + |
| + cr0.val = vcpu->arch.sie_block->gcr[0]; |
| + edat = cr0.edat && test_kvm_facility(vcpu->kvm, 8); |
| + mask = _kvm_s390_logical_to_effective(&scb_s->gpsw, PAGE_MASK); |
| + |
| + dest = vsie_get_register(vcpu, vsie_page, scb_s->ipb >> 20) & mask; |
| + src = vsie_get_register(vcpu, vsie_page, scb_s->ipb >> 16) & mask; |
| + |
| + rc_dest = kvm_s390_shadow_fault(vcpu, vsie_page->gmap, dest, &pei_dest); |
| + rc_src = kvm_s390_shadow_fault(vcpu, vsie_page->gmap, src, &pei_src); |
| + /* |
| + * Either everything went well, or something non-critical went wrong |
| + * e.g. because of a race. In either case, simply retry. |
| + */ |
| + if (rc_dest == -EAGAIN || rc_src == -EAGAIN || (!rc_dest && !rc_src)) { |
| + retry_vsie_icpt(vsie_page); |
| + return -EAGAIN; |
| + } |
| + /* Something more serious went wrong, propagate the error */ |
| + if (rc_dest < 0) |
| + return rc_dest; |
| + if (rc_src < 0) |
| + return rc_src; |
| + |
| + /* The only possible suppressing exception: just deliver it */ |
| + if (rc_dest == PGM_TRANSLATION_SPEC || rc_src == PGM_TRANSLATION_SPEC) { |
| + clear_vsie_icpt(vsie_page); |
| + rc_dest = kvm_s390_inject_program_int(vcpu, PGM_TRANSLATION_SPEC); |
| + WARN_ON_ONCE(rc_dest); |
| + return 1; |
| + } |
| + |
| + /* |
| + * Forward the PEI intercept to the guest if it was a page fault, or |
| + * also for segment and region table faults if EDAT applies. |
| + */ |
| + if (edat) { |
| + rc_dest = rc_dest == PGM_ASCE_TYPE ? rc_dest : 0; |
| + rc_src = rc_src == PGM_ASCE_TYPE ? rc_src : 0; |
| + } else { |
| + rc_dest = rc_dest != PGM_PAGE_TRANSLATION ? rc_dest : 0; |
| + rc_src = rc_src != PGM_PAGE_TRANSLATION ? rc_src : 0; |
| + } |
| + if (!rc_dest && !rc_src) { |
| + pei_block[0] = pei_dest; |
| + pei_block[1] = pei_src; |
| + return 1; |
| + } |
| + |
| + retry_vsie_icpt(vsie_page); |
| + |
| + /* |
| + * The host has edat, and the guest does not, or it was an ASCE type |
| + * exception. The host needs to inject the appropriate DAT interrupts |
| + * into the guest. |
| + */ |
| + if (rc_dest) |
| + return inject_fault(vcpu, rc_dest, dest, 1); |
| + return inject_fault(vcpu, rc_src, src, 0); |
| +} |
| + |
| +/* |
| * Run the vsie on a shadow scb and a shadow gmap, without any further |
| * sanity checks, handling SIE faults. |
| * |
| @@ -1068,6 +1152,10 @@ static int do_vsie_run(struct kvm_vcpu * |
| if ((scb_s->ipa & 0xf000) != 0xf000) |
| scb_s->ipa += 0x1000; |
| break; |
| + case ICPT_PARTEXEC: |
| + if (scb_s->ipa == 0xb254) |
| + rc = vsie_handle_mvpg(vcpu, vsie_page); |
| + break; |
| } |
| return rc; |
| } |