162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * kvm nested virtualization support for s390x 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright IBM Corp. 2016, 2018 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author(s): David Hildenbrand <dahi@linux.vnet.ibm.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/vmalloc.h> 1062306a36Sopenharmony_ci#include <linux/kvm_host.h> 1162306a36Sopenharmony_ci#include <linux/bug.h> 1262306a36Sopenharmony_ci#include <linux/list.h> 1362306a36Sopenharmony_ci#include <linux/bitmap.h> 1462306a36Sopenharmony_ci#include <linux/sched/signal.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <asm/gmap.h> 1762306a36Sopenharmony_ci#include <asm/mmu_context.h> 1862306a36Sopenharmony_ci#include <asm/sclp.h> 1962306a36Sopenharmony_ci#include <asm/nmi.h> 2062306a36Sopenharmony_ci#include <asm/dis.h> 2162306a36Sopenharmony_ci#include <asm/fpu/api.h> 2262306a36Sopenharmony_ci#include "kvm-s390.h" 2362306a36Sopenharmony_ci#include "gaccess.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct vsie_page { 2662306a36Sopenharmony_ci struct kvm_s390_sie_block scb_s; /* 0x0000 */ 2762306a36Sopenharmony_ci /* 2862306a36Sopenharmony_ci * the backup info for machine check. ensure it's at 2962306a36Sopenharmony_ci * the same offset as that in struct sie_page! 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_ci struct mcck_volatile_info mcck_info; /* 0x0200 */ 3262306a36Sopenharmony_ci /* 3362306a36Sopenharmony_ci * The pinned original scb. Be aware that other VCPUs can modify 3462306a36Sopenharmony_ci * it while we read from it. Values that are used for conditions or 3562306a36Sopenharmony_ci * are reused conditionally, should be accessed via READ_ONCE. 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_ci struct kvm_s390_sie_block *scb_o; /* 0x0218 */ 3862306a36Sopenharmony_ci /* the shadow gmap in use by the vsie_page */ 3962306a36Sopenharmony_ci struct gmap *gmap; /* 0x0220 */ 4062306a36Sopenharmony_ci /* address of the last reported fault to guest2 */ 4162306a36Sopenharmony_ci unsigned long fault_addr; /* 0x0228 */ 4262306a36Sopenharmony_ci /* calculated guest addresses of satellite control blocks */ 4362306a36Sopenharmony_ci gpa_t sca_gpa; /* 0x0230 */ 4462306a36Sopenharmony_ci gpa_t itdba_gpa; /* 0x0238 */ 4562306a36Sopenharmony_ci gpa_t gvrd_gpa; /* 0x0240 */ 4662306a36Sopenharmony_ci gpa_t riccbd_gpa; /* 0x0248 */ 4762306a36Sopenharmony_ci gpa_t sdnx_gpa; /* 0x0250 */ 4862306a36Sopenharmony_ci __u8 reserved[0x0700 - 0x0258]; /* 0x0258 */ 4962306a36Sopenharmony_ci struct kvm_s390_crypto_cb crycb; /* 0x0700 */ 5062306a36Sopenharmony_ci __u8 fac[S390_ARCH_FAC_LIST_SIZE_BYTE]; /* 0x0800 */ 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* trigger a validity icpt for the given scb */ 5462306a36Sopenharmony_cistatic int set_validity_icpt(struct kvm_s390_sie_block *scb, 5562306a36Sopenharmony_ci __u16 reason_code) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci scb->ipa = 0x1000; 5862306a36Sopenharmony_ci scb->ipb = ((__u32) reason_code) << 16; 5962306a36Sopenharmony_ci scb->icptcode = ICPT_VALIDITY; 6062306a36Sopenharmony_ci return 1; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* mark the prefix as unmapped, this will block the VSIE */ 6462306a36Sopenharmony_cistatic void prefix_unmapped(struct vsie_page *vsie_page) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci atomic_or(PROG_REQUEST, &vsie_page->scb_s.prog20); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* mark the prefix as unmapped and wait until the VSIE has been left */ 7062306a36Sopenharmony_cistatic void prefix_unmapped_sync(struct vsie_page *vsie_page) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci prefix_unmapped(vsie_page); 7362306a36Sopenharmony_ci if (vsie_page->scb_s.prog0c & PROG_IN_SIE) 7462306a36Sopenharmony_ci atomic_or(CPUSTAT_STOP_INT, &vsie_page->scb_s.cpuflags); 7562306a36Sopenharmony_ci while (vsie_page->scb_s.prog0c & PROG_IN_SIE) 7662306a36Sopenharmony_ci cpu_relax(); 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* mark the prefix as mapped, this will allow the VSIE to run */ 8062306a36Sopenharmony_cistatic void prefix_mapped(struct vsie_page *vsie_page) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci atomic_andnot(PROG_REQUEST, &vsie_page->scb_s.prog20); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* test if the prefix is mapped into the gmap shadow */ 8662306a36Sopenharmony_cistatic int prefix_is_mapped(struct vsie_page *vsie_page) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci return !(atomic_read(&vsie_page->scb_s.prog20) & PROG_REQUEST); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* copy the updated intervention request bits into the shadow scb */ 9262306a36Sopenharmony_cistatic void update_intervention_requests(struct vsie_page *vsie_page) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci const int bits = CPUSTAT_STOP_INT | CPUSTAT_IO_INT | CPUSTAT_EXT_INT; 9562306a36Sopenharmony_ci int cpuflags; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci cpuflags = atomic_read(&vsie_page->scb_o->cpuflags); 9862306a36Sopenharmony_ci atomic_andnot(bits, &vsie_page->scb_s.cpuflags); 9962306a36Sopenharmony_ci atomic_or(cpuflags & bits, &vsie_page->scb_s.cpuflags); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* shadow (filter and validate) the cpuflags */ 10362306a36Sopenharmony_cistatic int prepare_cpuflags(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s; 10662306a36Sopenharmony_ci struct kvm_s390_sie_block *scb_o = vsie_page->scb_o; 10762306a36Sopenharmony_ci int newflags, cpuflags = atomic_read(&scb_o->cpuflags); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* we don't allow ESA/390 guests */ 11062306a36Sopenharmony_ci if (!(cpuflags & CPUSTAT_ZARCH)) 11162306a36Sopenharmony_ci return set_validity_icpt(scb_s, 0x0001U); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (cpuflags & (CPUSTAT_RRF | CPUSTAT_MCDS)) 11462306a36Sopenharmony_ci return set_validity_icpt(scb_s, 0x0001U); 11562306a36Sopenharmony_ci else if (cpuflags & (CPUSTAT_SLSV | CPUSTAT_SLSR)) 11662306a36Sopenharmony_ci return set_validity_icpt(scb_s, 0x0007U); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* intervention requests will be set later */ 11962306a36Sopenharmony_ci newflags = CPUSTAT_ZARCH; 12062306a36Sopenharmony_ci if (cpuflags & CPUSTAT_GED && test_kvm_facility(vcpu->kvm, 8)) 12162306a36Sopenharmony_ci newflags |= CPUSTAT_GED; 12262306a36Sopenharmony_ci if (cpuflags & CPUSTAT_GED2 && test_kvm_facility(vcpu->kvm, 78)) { 12362306a36Sopenharmony_ci if (cpuflags & CPUSTAT_GED) 12462306a36Sopenharmony_ci return set_validity_icpt(scb_s, 0x0001U); 12562306a36Sopenharmony_ci newflags |= CPUSTAT_GED2; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci if (test_kvm_cpu_feat(vcpu->kvm, KVM_S390_VM_CPU_FEAT_GPERE)) 12862306a36Sopenharmony_ci newflags |= cpuflags & CPUSTAT_P; 12962306a36Sopenharmony_ci if (test_kvm_cpu_feat(vcpu->kvm, KVM_S390_VM_CPU_FEAT_GSLS)) 13062306a36Sopenharmony_ci newflags |= cpuflags & CPUSTAT_SM; 13162306a36Sopenharmony_ci if (test_kvm_cpu_feat(vcpu->kvm, KVM_S390_VM_CPU_FEAT_IBS)) 13262306a36Sopenharmony_ci newflags |= cpuflags & CPUSTAT_IBS; 13362306a36Sopenharmony_ci if (test_kvm_cpu_feat(vcpu->kvm, KVM_S390_VM_CPU_FEAT_KSS)) 13462306a36Sopenharmony_ci newflags |= cpuflags & CPUSTAT_KSS; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci atomic_set(&scb_s->cpuflags, newflags); 13762306a36Sopenharmony_ci return 0; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci/* Copy to APCB FORMAT1 from APCB FORMAT0 */ 14062306a36Sopenharmony_cistatic int setup_apcb10(struct kvm_vcpu *vcpu, struct kvm_s390_apcb1 *apcb_s, 14162306a36Sopenharmony_ci unsigned long crycb_gpa, struct kvm_s390_apcb1 *apcb_h) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct kvm_s390_apcb0 tmp; 14462306a36Sopenharmony_ci unsigned long apcb_gpa; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci apcb_gpa = crycb_gpa + offsetof(struct kvm_s390_crypto_cb, apcb0); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (read_guest_real(vcpu, apcb_gpa, &tmp, 14962306a36Sopenharmony_ci sizeof(struct kvm_s390_apcb0))) 15062306a36Sopenharmony_ci return -EFAULT; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci apcb_s->apm[0] = apcb_h->apm[0] & tmp.apm[0]; 15362306a36Sopenharmony_ci apcb_s->aqm[0] = apcb_h->aqm[0] & tmp.aqm[0] & 0xffff000000000000UL; 15462306a36Sopenharmony_ci apcb_s->adm[0] = apcb_h->adm[0] & tmp.adm[0] & 0xffff000000000000UL; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return 0; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci/** 16162306a36Sopenharmony_ci * setup_apcb00 - Copy to APCB FORMAT0 from APCB FORMAT0 16262306a36Sopenharmony_ci * @vcpu: pointer to the virtual CPU 16362306a36Sopenharmony_ci * @apcb_s: pointer to start of apcb in the shadow crycb 16462306a36Sopenharmony_ci * @crycb_gpa: guest physical address to start of original guest crycb 16562306a36Sopenharmony_ci * @apcb_h: pointer to start of apcb in the guest1 16662306a36Sopenharmony_ci * 16762306a36Sopenharmony_ci * Returns 0 and -EFAULT on error reading guest apcb 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_cistatic int setup_apcb00(struct kvm_vcpu *vcpu, unsigned long *apcb_s, 17062306a36Sopenharmony_ci unsigned long crycb_gpa, unsigned long *apcb_h) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci unsigned long apcb_gpa; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci apcb_gpa = crycb_gpa + offsetof(struct kvm_s390_crypto_cb, apcb0); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (read_guest_real(vcpu, apcb_gpa, apcb_s, 17762306a36Sopenharmony_ci sizeof(struct kvm_s390_apcb0))) 17862306a36Sopenharmony_ci return -EFAULT; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci bitmap_and(apcb_s, apcb_s, apcb_h, 18162306a36Sopenharmony_ci BITS_PER_BYTE * sizeof(struct kvm_s390_apcb0)); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci return 0; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/** 18762306a36Sopenharmony_ci * setup_apcb11 - Copy the FORMAT1 APCB from the guest to the shadow CRYCB 18862306a36Sopenharmony_ci * @vcpu: pointer to the virtual CPU 18962306a36Sopenharmony_ci * @apcb_s: pointer to start of apcb in the shadow crycb 19062306a36Sopenharmony_ci * @crycb_gpa: guest physical address to start of original guest crycb 19162306a36Sopenharmony_ci * @apcb_h: pointer to start of apcb in the host 19262306a36Sopenharmony_ci * 19362306a36Sopenharmony_ci * Returns 0 and -EFAULT on error reading guest apcb 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_cistatic int setup_apcb11(struct kvm_vcpu *vcpu, unsigned long *apcb_s, 19662306a36Sopenharmony_ci unsigned long crycb_gpa, 19762306a36Sopenharmony_ci unsigned long *apcb_h) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci unsigned long apcb_gpa; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci apcb_gpa = crycb_gpa + offsetof(struct kvm_s390_crypto_cb, apcb1); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (read_guest_real(vcpu, apcb_gpa, apcb_s, 20462306a36Sopenharmony_ci sizeof(struct kvm_s390_apcb1))) 20562306a36Sopenharmony_ci return -EFAULT; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci bitmap_and(apcb_s, apcb_s, apcb_h, 20862306a36Sopenharmony_ci BITS_PER_BYTE * sizeof(struct kvm_s390_apcb1)); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return 0; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/** 21462306a36Sopenharmony_ci * setup_apcb - Create a shadow copy of the apcb. 21562306a36Sopenharmony_ci * @vcpu: pointer to the virtual CPU 21662306a36Sopenharmony_ci * @crycb_s: pointer to shadow crycb 21762306a36Sopenharmony_ci * @crycb_gpa: guest physical address of original guest crycb 21862306a36Sopenharmony_ci * @crycb_h: pointer to the host crycb 21962306a36Sopenharmony_ci * @fmt_o: format of the original guest crycb. 22062306a36Sopenharmony_ci * @fmt_h: format of the host crycb. 22162306a36Sopenharmony_ci * 22262306a36Sopenharmony_ci * Checks the compatibility between the guest and host crycb and calls the 22362306a36Sopenharmony_ci * appropriate copy function. 22462306a36Sopenharmony_ci * 22562306a36Sopenharmony_ci * Return 0 or an error number if the guest and host crycb are incompatible. 22662306a36Sopenharmony_ci */ 22762306a36Sopenharmony_cistatic int setup_apcb(struct kvm_vcpu *vcpu, struct kvm_s390_crypto_cb *crycb_s, 22862306a36Sopenharmony_ci const u32 crycb_gpa, 22962306a36Sopenharmony_ci struct kvm_s390_crypto_cb *crycb_h, 23062306a36Sopenharmony_ci int fmt_o, int fmt_h) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci switch (fmt_o) { 23362306a36Sopenharmony_ci case CRYCB_FORMAT2: 23462306a36Sopenharmony_ci if ((crycb_gpa & PAGE_MASK) != ((crycb_gpa + 256) & PAGE_MASK)) 23562306a36Sopenharmony_ci return -EACCES; 23662306a36Sopenharmony_ci if (fmt_h != CRYCB_FORMAT2) 23762306a36Sopenharmony_ci return -EINVAL; 23862306a36Sopenharmony_ci return setup_apcb11(vcpu, (unsigned long *)&crycb_s->apcb1, 23962306a36Sopenharmony_ci crycb_gpa, 24062306a36Sopenharmony_ci (unsigned long *)&crycb_h->apcb1); 24162306a36Sopenharmony_ci case CRYCB_FORMAT1: 24262306a36Sopenharmony_ci switch (fmt_h) { 24362306a36Sopenharmony_ci case CRYCB_FORMAT2: 24462306a36Sopenharmony_ci return setup_apcb10(vcpu, &crycb_s->apcb1, 24562306a36Sopenharmony_ci crycb_gpa, 24662306a36Sopenharmony_ci &crycb_h->apcb1); 24762306a36Sopenharmony_ci case CRYCB_FORMAT1: 24862306a36Sopenharmony_ci return setup_apcb00(vcpu, 24962306a36Sopenharmony_ci (unsigned long *) &crycb_s->apcb0, 25062306a36Sopenharmony_ci crycb_gpa, 25162306a36Sopenharmony_ci (unsigned long *) &crycb_h->apcb0); 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci break; 25462306a36Sopenharmony_ci case CRYCB_FORMAT0: 25562306a36Sopenharmony_ci if ((crycb_gpa & PAGE_MASK) != ((crycb_gpa + 32) & PAGE_MASK)) 25662306a36Sopenharmony_ci return -EACCES; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci switch (fmt_h) { 25962306a36Sopenharmony_ci case CRYCB_FORMAT2: 26062306a36Sopenharmony_ci return setup_apcb10(vcpu, &crycb_s->apcb1, 26162306a36Sopenharmony_ci crycb_gpa, 26262306a36Sopenharmony_ci &crycb_h->apcb1); 26362306a36Sopenharmony_ci case CRYCB_FORMAT1: 26462306a36Sopenharmony_ci case CRYCB_FORMAT0: 26562306a36Sopenharmony_ci return setup_apcb00(vcpu, 26662306a36Sopenharmony_ci (unsigned long *) &crycb_s->apcb0, 26762306a36Sopenharmony_ci crycb_gpa, 26862306a36Sopenharmony_ci (unsigned long *) &crycb_h->apcb0); 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci return -EINVAL; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci/** 27562306a36Sopenharmony_ci * shadow_crycb - Create a shadow copy of the crycb block 27662306a36Sopenharmony_ci * @vcpu: a pointer to the virtual CPU 27762306a36Sopenharmony_ci * @vsie_page: a pointer to internal date used for the vSIE 27862306a36Sopenharmony_ci * 27962306a36Sopenharmony_ci * Create a shadow copy of the crycb block and setup key wrapping, if 28062306a36Sopenharmony_ci * requested for guest 3 and enabled for guest 2. 28162306a36Sopenharmony_ci * 28262306a36Sopenharmony_ci * We accept format-1 or format-2, but we convert format-1 into format-2 28362306a36Sopenharmony_ci * in the shadow CRYCB. 28462306a36Sopenharmony_ci * Using format-2 enables the firmware to choose the right format when 28562306a36Sopenharmony_ci * scheduling the SIE. 28662306a36Sopenharmony_ci * There is nothing to do for format-0. 28762306a36Sopenharmony_ci * 28862306a36Sopenharmony_ci * This function centralize the issuing of set_validity_icpt() for all 28962306a36Sopenharmony_ci * the subfunctions working on the crycb. 29062306a36Sopenharmony_ci * 29162306a36Sopenharmony_ci * Returns: - 0 if shadowed or nothing to do 29262306a36Sopenharmony_ci * - > 0 if control has to be given to guest 2 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_cistatic int shadow_crycb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s; 29762306a36Sopenharmony_ci struct kvm_s390_sie_block *scb_o = vsie_page->scb_o; 29862306a36Sopenharmony_ci const uint32_t crycbd_o = READ_ONCE(scb_o->crycbd); 29962306a36Sopenharmony_ci const u32 crycb_addr = crycbd_o & 0x7ffffff8U; 30062306a36Sopenharmony_ci unsigned long *b1, *b2; 30162306a36Sopenharmony_ci u8 ecb3_flags; 30262306a36Sopenharmony_ci u32 ecd_flags; 30362306a36Sopenharmony_ci int apie_h; 30462306a36Sopenharmony_ci int apie_s; 30562306a36Sopenharmony_ci int key_msk = test_kvm_facility(vcpu->kvm, 76); 30662306a36Sopenharmony_ci int fmt_o = crycbd_o & CRYCB_FORMAT_MASK; 30762306a36Sopenharmony_ci int fmt_h = vcpu->arch.sie_block->crycbd & CRYCB_FORMAT_MASK; 30862306a36Sopenharmony_ci int ret = 0; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci scb_s->crycbd = 0; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci apie_h = vcpu->arch.sie_block->eca & ECA_APIE; 31362306a36Sopenharmony_ci apie_s = apie_h & scb_o->eca; 31462306a36Sopenharmony_ci if (!apie_s && (!key_msk || (fmt_o == CRYCB_FORMAT0))) 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (!crycb_addr) 31862306a36Sopenharmony_ci return set_validity_icpt(scb_s, 0x0039U); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (fmt_o == CRYCB_FORMAT1) 32162306a36Sopenharmony_ci if ((crycb_addr & PAGE_MASK) != 32262306a36Sopenharmony_ci ((crycb_addr + 128) & PAGE_MASK)) 32362306a36Sopenharmony_ci return set_validity_icpt(scb_s, 0x003CU); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (apie_s) { 32662306a36Sopenharmony_ci ret = setup_apcb(vcpu, &vsie_page->crycb, crycb_addr, 32762306a36Sopenharmony_ci vcpu->kvm->arch.crypto.crycb, 32862306a36Sopenharmony_ci fmt_o, fmt_h); 32962306a36Sopenharmony_ci if (ret) 33062306a36Sopenharmony_ci goto end; 33162306a36Sopenharmony_ci scb_s->eca |= scb_o->eca & ECA_APIE; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* we may only allow it if enabled for guest 2 */ 33562306a36Sopenharmony_ci ecb3_flags = scb_o->ecb3 & vcpu->arch.sie_block->ecb3 & 33662306a36Sopenharmony_ci (ECB3_AES | ECB3_DEA); 33762306a36Sopenharmony_ci ecd_flags = scb_o->ecd & vcpu->arch.sie_block->ecd & ECD_ECC; 33862306a36Sopenharmony_ci if (!ecb3_flags && !ecd_flags) 33962306a36Sopenharmony_ci goto end; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* copy only the wrapping keys */ 34262306a36Sopenharmony_ci if (read_guest_real(vcpu, crycb_addr + 72, 34362306a36Sopenharmony_ci vsie_page->crycb.dea_wrapping_key_mask, 56)) 34462306a36Sopenharmony_ci return set_validity_icpt(scb_s, 0x0035U); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci scb_s->ecb3 |= ecb3_flags; 34762306a36Sopenharmony_ci scb_s->ecd |= ecd_flags; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* xor both blocks in one run */ 35062306a36Sopenharmony_ci b1 = (unsigned long *) vsie_page->crycb.dea_wrapping_key_mask; 35162306a36Sopenharmony_ci b2 = (unsigned long *) 35262306a36Sopenharmony_ci vcpu->kvm->arch.crypto.crycb->dea_wrapping_key_mask; 35362306a36Sopenharmony_ci /* as 56%8 == 0, bitmap_xor won't overwrite any data */ 35462306a36Sopenharmony_ci bitmap_xor(b1, b1, b2, BITS_PER_BYTE * 56); 35562306a36Sopenharmony_ciend: 35662306a36Sopenharmony_ci switch (ret) { 35762306a36Sopenharmony_ci case -EINVAL: 35862306a36Sopenharmony_ci return set_validity_icpt(scb_s, 0x0022U); 35962306a36Sopenharmony_ci case -EFAULT: 36062306a36Sopenharmony_ci return set_validity_icpt(scb_s, 0x0035U); 36162306a36Sopenharmony_ci case -EACCES: 36262306a36Sopenharmony_ci return set_validity_icpt(scb_s, 0x003CU); 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci scb_s->crycbd = ((__u32)(__u64) &vsie_page->crycb) | CRYCB_FORMAT2; 36562306a36Sopenharmony_ci return 0; 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci/* shadow (round up/down) the ibc to avoid validity icpt */ 36962306a36Sopenharmony_cistatic void prepare_ibc(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s; 37262306a36Sopenharmony_ci struct kvm_s390_sie_block *scb_o = vsie_page->scb_o; 37362306a36Sopenharmony_ci /* READ_ONCE does not work on bitfields - use a temporary variable */ 37462306a36Sopenharmony_ci const uint32_t __new_ibc = scb_o->ibc; 37562306a36Sopenharmony_ci const uint32_t new_ibc = READ_ONCE(__new_ibc) & 0x0fffU; 37662306a36Sopenharmony_ci __u64 min_ibc = (sclp.ibc >> 16) & 0x0fffU; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci scb_s->ibc = 0; 37962306a36Sopenharmony_ci /* ibc installed in g2 and requested for g3 */ 38062306a36Sopenharmony_ci if (vcpu->kvm->arch.model.ibc && new_ibc) { 38162306a36Sopenharmony_ci scb_s->ibc = new_ibc; 38262306a36Sopenharmony_ci /* takte care of the minimum ibc level of the machine */ 38362306a36Sopenharmony_ci if (scb_s->ibc < min_ibc) 38462306a36Sopenharmony_ci scb_s->ibc = min_ibc; 38562306a36Sopenharmony_ci /* take care of the maximum ibc level set for the guest */ 38662306a36Sopenharmony_ci if (scb_s->ibc > vcpu->kvm->arch.model.ibc) 38762306a36Sopenharmony_ci scb_s->ibc = vcpu->kvm->arch.model.ibc; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci/* unshadow the scb, copying parameters back to the real scb */ 39262306a36Sopenharmony_cistatic void unshadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s; 39562306a36Sopenharmony_ci struct kvm_s390_sie_block *scb_o = vsie_page->scb_o; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* interception */ 39862306a36Sopenharmony_ci scb_o->icptcode = scb_s->icptcode; 39962306a36Sopenharmony_ci scb_o->icptstatus = scb_s->icptstatus; 40062306a36Sopenharmony_ci scb_o->ipa = scb_s->ipa; 40162306a36Sopenharmony_ci scb_o->ipb = scb_s->ipb; 40262306a36Sopenharmony_ci scb_o->gbea = scb_s->gbea; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* timer */ 40562306a36Sopenharmony_ci scb_o->cputm = scb_s->cputm; 40662306a36Sopenharmony_ci scb_o->ckc = scb_s->ckc; 40762306a36Sopenharmony_ci scb_o->todpr = scb_s->todpr; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* guest state */ 41062306a36Sopenharmony_ci scb_o->gpsw = scb_s->gpsw; 41162306a36Sopenharmony_ci scb_o->gg14 = scb_s->gg14; 41262306a36Sopenharmony_ci scb_o->gg15 = scb_s->gg15; 41362306a36Sopenharmony_ci memcpy(scb_o->gcr, scb_s->gcr, 128); 41462306a36Sopenharmony_ci scb_o->pp = scb_s->pp; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* branch prediction */ 41762306a36Sopenharmony_ci if (test_kvm_facility(vcpu->kvm, 82)) { 41862306a36Sopenharmony_ci scb_o->fpf &= ~FPF_BPBC; 41962306a36Sopenharmony_ci scb_o->fpf |= scb_s->fpf & FPF_BPBC; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* interrupt intercept */ 42362306a36Sopenharmony_ci switch (scb_s->icptcode) { 42462306a36Sopenharmony_ci case ICPT_PROGI: 42562306a36Sopenharmony_ci case ICPT_INSTPROGI: 42662306a36Sopenharmony_ci case ICPT_EXTINT: 42762306a36Sopenharmony_ci memcpy((void *)((u64)scb_o + 0xc0), 42862306a36Sopenharmony_ci (void *)((u64)scb_s + 0xc0), 0xf0 - 0xc0); 42962306a36Sopenharmony_ci break; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (scb_s->ihcpu != 0xffffU) 43362306a36Sopenharmony_ci scb_o->ihcpu = scb_s->ihcpu; 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci/* 43762306a36Sopenharmony_ci * Setup the shadow scb by copying and checking the relevant parts of the g2 43862306a36Sopenharmony_ci * provided scb. 43962306a36Sopenharmony_ci * 44062306a36Sopenharmony_ci * Returns: - 0 if the scb has been shadowed 44162306a36Sopenharmony_ci * - > 0 if control has to be given to guest 2 44262306a36Sopenharmony_ci */ 44362306a36Sopenharmony_cistatic int shadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci struct kvm_s390_sie_block *scb_o = vsie_page->scb_o; 44662306a36Sopenharmony_ci struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s; 44762306a36Sopenharmony_ci /* READ_ONCE does not work on bitfields - use a temporary variable */ 44862306a36Sopenharmony_ci const uint32_t __new_prefix = scb_o->prefix; 44962306a36Sopenharmony_ci const uint32_t new_prefix = READ_ONCE(__new_prefix); 45062306a36Sopenharmony_ci const bool wants_tx = READ_ONCE(scb_o->ecb) & ECB_TE; 45162306a36Sopenharmony_ci bool had_tx = scb_s->ecb & ECB_TE; 45262306a36Sopenharmony_ci unsigned long new_mso = 0; 45362306a36Sopenharmony_ci int rc; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* make sure we don't have any leftovers when reusing the scb */ 45662306a36Sopenharmony_ci scb_s->icptcode = 0; 45762306a36Sopenharmony_ci scb_s->eca = 0; 45862306a36Sopenharmony_ci scb_s->ecb = 0; 45962306a36Sopenharmony_ci scb_s->ecb2 = 0; 46062306a36Sopenharmony_ci scb_s->ecb3 = 0; 46162306a36Sopenharmony_ci scb_s->ecd = 0; 46262306a36Sopenharmony_ci scb_s->fac = 0; 46362306a36Sopenharmony_ci scb_s->fpf = 0; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci rc = prepare_cpuflags(vcpu, vsie_page); 46662306a36Sopenharmony_ci if (rc) 46762306a36Sopenharmony_ci goto out; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci /* timer */ 47062306a36Sopenharmony_ci scb_s->cputm = scb_o->cputm; 47162306a36Sopenharmony_ci scb_s->ckc = scb_o->ckc; 47262306a36Sopenharmony_ci scb_s->todpr = scb_o->todpr; 47362306a36Sopenharmony_ci scb_s->epoch = scb_o->epoch; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* guest state */ 47662306a36Sopenharmony_ci scb_s->gpsw = scb_o->gpsw; 47762306a36Sopenharmony_ci scb_s->gg14 = scb_o->gg14; 47862306a36Sopenharmony_ci scb_s->gg15 = scb_o->gg15; 47962306a36Sopenharmony_ci memcpy(scb_s->gcr, scb_o->gcr, 128); 48062306a36Sopenharmony_ci scb_s->pp = scb_o->pp; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* interception / execution handling */ 48362306a36Sopenharmony_ci scb_s->gbea = scb_o->gbea; 48462306a36Sopenharmony_ci scb_s->lctl = scb_o->lctl; 48562306a36Sopenharmony_ci scb_s->svcc = scb_o->svcc; 48662306a36Sopenharmony_ci scb_s->ictl = scb_o->ictl; 48762306a36Sopenharmony_ci /* 48862306a36Sopenharmony_ci * SKEY handling functions can't deal with false setting of PTE invalid 48962306a36Sopenharmony_ci * bits. Therefore we cannot provide interpretation and would later 49062306a36Sopenharmony_ci * have to provide own emulation handlers. 49162306a36Sopenharmony_ci */ 49262306a36Sopenharmony_ci if (!(atomic_read(&scb_s->cpuflags) & CPUSTAT_KSS)) 49362306a36Sopenharmony_ci scb_s->ictl |= ICTL_ISKE | ICTL_SSKE | ICTL_RRBE; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci scb_s->icpua = scb_o->icpua; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci if (!(atomic_read(&scb_s->cpuflags) & CPUSTAT_SM)) 49862306a36Sopenharmony_ci new_mso = READ_ONCE(scb_o->mso) & 0xfffffffffff00000UL; 49962306a36Sopenharmony_ci /* if the hva of the prefix changes, we have to remap the prefix */ 50062306a36Sopenharmony_ci if (scb_s->mso != new_mso || scb_s->prefix != new_prefix) 50162306a36Sopenharmony_ci prefix_unmapped(vsie_page); 50262306a36Sopenharmony_ci /* SIE will do mso/msl validity and exception checks for us */ 50362306a36Sopenharmony_ci scb_s->msl = scb_o->msl & 0xfffffffffff00000UL; 50462306a36Sopenharmony_ci scb_s->mso = new_mso; 50562306a36Sopenharmony_ci scb_s->prefix = new_prefix; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* We have to definitely flush the tlb if this scb never ran */ 50862306a36Sopenharmony_ci if (scb_s->ihcpu != 0xffffU) 50962306a36Sopenharmony_ci scb_s->ihcpu = scb_o->ihcpu; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci /* MVPG and Protection Exception Interpretation are always available */ 51262306a36Sopenharmony_ci scb_s->eca |= scb_o->eca & (ECA_MVPGI | ECA_PROTEXCI); 51362306a36Sopenharmony_ci /* Host-protection-interruption introduced with ESOP */ 51462306a36Sopenharmony_ci if (test_kvm_cpu_feat(vcpu->kvm, KVM_S390_VM_CPU_FEAT_ESOP)) 51562306a36Sopenharmony_ci scb_s->ecb |= scb_o->ecb & ECB_HOSTPROTINT; 51662306a36Sopenharmony_ci /* 51762306a36Sopenharmony_ci * CPU Topology 51862306a36Sopenharmony_ci * This facility only uses the utility field of the SCA and none of 51962306a36Sopenharmony_ci * the cpu entries that are problematic with the other interpretation 52062306a36Sopenharmony_ci * facilities so we can pass it through 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_ci if (test_kvm_facility(vcpu->kvm, 11)) 52362306a36Sopenharmony_ci scb_s->ecb |= scb_o->ecb & ECB_PTF; 52462306a36Sopenharmony_ci /* transactional execution */ 52562306a36Sopenharmony_ci if (test_kvm_facility(vcpu->kvm, 73) && wants_tx) { 52662306a36Sopenharmony_ci /* remap the prefix is tx is toggled on */ 52762306a36Sopenharmony_ci if (!had_tx) 52862306a36Sopenharmony_ci prefix_unmapped(vsie_page); 52962306a36Sopenharmony_ci scb_s->ecb |= ECB_TE; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci /* specification exception interpretation */ 53262306a36Sopenharmony_ci scb_s->ecb |= scb_o->ecb & ECB_SPECI; 53362306a36Sopenharmony_ci /* branch prediction */ 53462306a36Sopenharmony_ci if (test_kvm_facility(vcpu->kvm, 82)) 53562306a36Sopenharmony_ci scb_s->fpf |= scb_o->fpf & FPF_BPBC; 53662306a36Sopenharmony_ci /* SIMD */ 53762306a36Sopenharmony_ci if (test_kvm_facility(vcpu->kvm, 129)) { 53862306a36Sopenharmony_ci scb_s->eca |= scb_o->eca & ECA_VX; 53962306a36Sopenharmony_ci scb_s->ecd |= scb_o->ecd & ECD_HOSTREGMGMT; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci /* Run-time-Instrumentation */ 54262306a36Sopenharmony_ci if (test_kvm_facility(vcpu->kvm, 64)) 54362306a36Sopenharmony_ci scb_s->ecb3 |= scb_o->ecb3 & ECB3_RI; 54462306a36Sopenharmony_ci /* Instruction Execution Prevention */ 54562306a36Sopenharmony_ci if (test_kvm_facility(vcpu->kvm, 130)) 54662306a36Sopenharmony_ci scb_s->ecb2 |= scb_o->ecb2 & ECB2_IEP; 54762306a36Sopenharmony_ci /* Guarded Storage */ 54862306a36Sopenharmony_ci if (test_kvm_facility(vcpu->kvm, 133)) { 54962306a36Sopenharmony_ci scb_s->ecb |= scb_o->ecb & ECB_GS; 55062306a36Sopenharmony_ci scb_s->ecd |= scb_o->ecd & ECD_HOSTREGMGMT; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci if (test_kvm_cpu_feat(vcpu->kvm, KVM_S390_VM_CPU_FEAT_SIIF)) 55362306a36Sopenharmony_ci scb_s->eca |= scb_o->eca & ECA_SII; 55462306a36Sopenharmony_ci if (test_kvm_cpu_feat(vcpu->kvm, KVM_S390_VM_CPU_FEAT_IB)) 55562306a36Sopenharmony_ci scb_s->eca |= scb_o->eca & ECA_IB; 55662306a36Sopenharmony_ci if (test_kvm_cpu_feat(vcpu->kvm, KVM_S390_VM_CPU_FEAT_CEI)) 55762306a36Sopenharmony_ci scb_s->eca |= scb_o->eca & ECA_CEI; 55862306a36Sopenharmony_ci /* Epoch Extension */ 55962306a36Sopenharmony_ci if (test_kvm_facility(vcpu->kvm, 139)) { 56062306a36Sopenharmony_ci scb_s->ecd |= scb_o->ecd & ECD_MEF; 56162306a36Sopenharmony_ci scb_s->epdx = scb_o->epdx; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci /* etoken */ 56562306a36Sopenharmony_ci if (test_kvm_facility(vcpu->kvm, 156)) 56662306a36Sopenharmony_ci scb_s->ecd |= scb_o->ecd & ECD_ETOKENF; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci scb_s->hpid = HPID_VSIE; 56962306a36Sopenharmony_ci scb_s->cpnc = scb_o->cpnc; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci prepare_ibc(vcpu, vsie_page); 57262306a36Sopenharmony_ci rc = shadow_crycb(vcpu, vsie_page); 57362306a36Sopenharmony_ciout: 57462306a36Sopenharmony_ci if (rc) 57562306a36Sopenharmony_ci unshadow_scb(vcpu, vsie_page); 57662306a36Sopenharmony_ci return rc; 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_civoid kvm_s390_vsie_gmap_notifier(struct gmap *gmap, unsigned long start, 58062306a36Sopenharmony_ci unsigned long end) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci struct kvm *kvm = gmap->private; 58362306a36Sopenharmony_ci struct vsie_page *cur; 58462306a36Sopenharmony_ci unsigned long prefix; 58562306a36Sopenharmony_ci struct page *page; 58662306a36Sopenharmony_ci int i; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (!gmap_is_shadow(gmap)) 58962306a36Sopenharmony_ci return; 59062306a36Sopenharmony_ci /* 59162306a36Sopenharmony_ci * Only new shadow blocks are added to the list during runtime, 59262306a36Sopenharmony_ci * therefore we can safely reference them all the time. 59362306a36Sopenharmony_ci */ 59462306a36Sopenharmony_ci for (i = 0; i < kvm->arch.vsie.page_count; i++) { 59562306a36Sopenharmony_ci page = READ_ONCE(kvm->arch.vsie.pages[i]); 59662306a36Sopenharmony_ci if (!page) 59762306a36Sopenharmony_ci continue; 59862306a36Sopenharmony_ci cur = page_to_virt(page); 59962306a36Sopenharmony_ci if (READ_ONCE(cur->gmap) != gmap) 60062306a36Sopenharmony_ci continue; 60162306a36Sopenharmony_ci prefix = cur->scb_s.prefix << GUEST_PREFIX_SHIFT; 60262306a36Sopenharmony_ci /* with mso/msl, the prefix lies at an offset */ 60362306a36Sopenharmony_ci prefix += cur->scb_s.mso; 60462306a36Sopenharmony_ci if (prefix <= end && start <= prefix + 2 * PAGE_SIZE - 1) 60562306a36Sopenharmony_ci prefix_unmapped_sync(cur); 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci/* 61062306a36Sopenharmony_ci * Map the first prefix page and if tx is enabled also the second prefix page. 61162306a36Sopenharmony_ci * 61262306a36Sopenharmony_ci * The prefix will be protected, a gmap notifier will inform about unmaps. 61362306a36Sopenharmony_ci * The shadow scb must not be executed until the prefix is remapped, this is 61462306a36Sopenharmony_ci * guaranteed by properly handling PROG_REQUEST. 61562306a36Sopenharmony_ci * 61662306a36Sopenharmony_ci * Returns: - 0 on if successfully mapped or already mapped 61762306a36Sopenharmony_ci * - > 0 if control has to be given to guest 2 61862306a36Sopenharmony_ci * - -EAGAIN if the caller can retry immediately 61962306a36Sopenharmony_ci * - -ENOMEM if out of memory 62062306a36Sopenharmony_ci */ 62162306a36Sopenharmony_cistatic int map_prefix(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s; 62462306a36Sopenharmony_ci u64 prefix = scb_s->prefix << GUEST_PREFIX_SHIFT; 62562306a36Sopenharmony_ci int rc; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (prefix_is_mapped(vsie_page)) 62862306a36Sopenharmony_ci return 0; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci /* mark it as mapped so we can catch any concurrent unmappers */ 63162306a36Sopenharmony_ci prefix_mapped(vsie_page); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci /* with mso/msl, the prefix lies at offset *mso* */ 63462306a36Sopenharmony_ci prefix += scb_s->mso; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci rc = kvm_s390_shadow_fault(vcpu, vsie_page->gmap, prefix, NULL); 63762306a36Sopenharmony_ci if (!rc && (scb_s->ecb & ECB_TE)) 63862306a36Sopenharmony_ci rc = kvm_s390_shadow_fault(vcpu, vsie_page->gmap, 63962306a36Sopenharmony_ci prefix + PAGE_SIZE, NULL); 64062306a36Sopenharmony_ci /* 64162306a36Sopenharmony_ci * We don't have to mprotect, we will be called for all unshadows. 64262306a36Sopenharmony_ci * SIE will detect if protection applies and trigger a validity. 64362306a36Sopenharmony_ci */ 64462306a36Sopenharmony_ci if (rc) 64562306a36Sopenharmony_ci prefix_unmapped(vsie_page); 64662306a36Sopenharmony_ci if (rc > 0 || rc == -EFAULT) 64762306a36Sopenharmony_ci rc = set_validity_icpt(scb_s, 0x0037U); 64862306a36Sopenharmony_ci return rc; 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci/* 65262306a36Sopenharmony_ci * Pin the guest page given by gpa and set hpa to the pinned host address. 65362306a36Sopenharmony_ci * Will always be pinned writable. 65462306a36Sopenharmony_ci * 65562306a36Sopenharmony_ci * Returns: - 0 on success 65662306a36Sopenharmony_ci * - -EINVAL if the gpa is not valid guest storage 65762306a36Sopenharmony_ci */ 65862306a36Sopenharmony_cistatic int pin_guest_page(struct kvm *kvm, gpa_t gpa, hpa_t *hpa) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci struct page *page; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci page = gfn_to_page(kvm, gpa_to_gfn(gpa)); 66362306a36Sopenharmony_ci if (is_error_page(page)) 66462306a36Sopenharmony_ci return -EINVAL; 66562306a36Sopenharmony_ci *hpa = (hpa_t)page_to_phys(page) + (gpa & ~PAGE_MASK); 66662306a36Sopenharmony_ci return 0; 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci/* Unpins a page previously pinned via pin_guest_page, marking it as dirty. */ 67062306a36Sopenharmony_cistatic void unpin_guest_page(struct kvm *kvm, gpa_t gpa, hpa_t hpa) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci kvm_release_pfn_dirty(hpa >> PAGE_SHIFT); 67362306a36Sopenharmony_ci /* mark the page always as dirty for migration */ 67462306a36Sopenharmony_ci mark_page_dirty(kvm, gpa_to_gfn(gpa)); 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci/* unpin all blocks previously pinned by pin_blocks(), marking them dirty */ 67862306a36Sopenharmony_cistatic void unpin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s; 68162306a36Sopenharmony_ci hpa_t hpa; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci hpa = (u64) scb_s->scaoh << 32 | scb_s->scaol; 68462306a36Sopenharmony_ci if (hpa) { 68562306a36Sopenharmony_ci unpin_guest_page(vcpu->kvm, vsie_page->sca_gpa, hpa); 68662306a36Sopenharmony_ci vsie_page->sca_gpa = 0; 68762306a36Sopenharmony_ci scb_s->scaol = 0; 68862306a36Sopenharmony_ci scb_s->scaoh = 0; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci hpa = scb_s->itdba; 69262306a36Sopenharmony_ci if (hpa) { 69362306a36Sopenharmony_ci unpin_guest_page(vcpu->kvm, vsie_page->itdba_gpa, hpa); 69462306a36Sopenharmony_ci vsie_page->itdba_gpa = 0; 69562306a36Sopenharmony_ci scb_s->itdba = 0; 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci hpa = scb_s->gvrd; 69962306a36Sopenharmony_ci if (hpa) { 70062306a36Sopenharmony_ci unpin_guest_page(vcpu->kvm, vsie_page->gvrd_gpa, hpa); 70162306a36Sopenharmony_ci vsie_page->gvrd_gpa = 0; 70262306a36Sopenharmony_ci scb_s->gvrd = 0; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci hpa = scb_s->riccbd; 70662306a36Sopenharmony_ci if (hpa) { 70762306a36Sopenharmony_ci unpin_guest_page(vcpu->kvm, vsie_page->riccbd_gpa, hpa); 70862306a36Sopenharmony_ci vsie_page->riccbd_gpa = 0; 70962306a36Sopenharmony_ci scb_s->riccbd = 0; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci hpa = scb_s->sdnxo; 71362306a36Sopenharmony_ci if (hpa) { 71462306a36Sopenharmony_ci unpin_guest_page(vcpu->kvm, vsie_page->sdnx_gpa, hpa); 71562306a36Sopenharmony_ci vsie_page->sdnx_gpa = 0; 71662306a36Sopenharmony_ci scb_s->sdnxo = 0; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci/* 72162306a36Sopenharmony_ci * Instead of shadowing some blocks, we can simply forward them because the 72262306a36Sopenharmony_ci * addresses in the scb are 64 bit long. 72362306a36Sopenharmony_ci * 72462306a36Sopenharmony_ci * This works as long as the data lies in one page. If blocks ever exceed one 72562306a36Sopenharmony_ci * page, we have to fall back to shadowing. 72662306a36Sopenharmony_ci * 72762306a36Sopenharmony_ci * As we reuse the sca, the vcpu pointers contained in it are invalid. We must 72862306a36Sopenharmony_ci * therefore not enable any facilities that access these pointers (e.g. SIGPIF). 72962306a36Sopenharmony_ci * 73062306a36Sopenharmony_ci * Returns: - 0 if all blocks were pinned. 73162306a36Sopenharmony_ci * - > 0 if control has to be given to guest 2 73262306a36Sopenharmony_ci * - -ENOMEM if out of memory 73362306a36Sopenharmony_ci */ 73462306a36Sopenharmony_cistatic int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci struct kvm_s390_sie_block *scb_o = vsie_page->scb_o; 73762306a36Sopenharmony_ci struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s; 73862306a36Sopenharmony_ci hpa_t hpa; 73962306a36Sopenharmony_ci gpa_t gpa; 74062306a36Sopenharmony_ci int rc = 0; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci gpa = READ_ONCE(scb_o->scaol) & ~0xfUL; 74362306a36Sopenharmony_ci if (test_kvm_cpu_feat(vcpu->kvm, KVM_S390_VM_CPU_FEAT_64BSCAO)) 74462306a36Sopenharmony_ci gpa |= (u64) READ_ONCE(scb_o->scaoh) << 32; 74562306a36Sopenharmony_ci if (gpa) { 74662306a36Sopenharmony_ci if (gpa < 2 * PAGE_SIZE) 74762306a36Sopenharmony_ci rc = set_validity_icpt(scb_s, 0x0038U); 74862306a36Sopenharmony_ci else if ((gpa & ~0x1fffUL) == kvm_s390_get_prefix(vcpu)) 74962306a36Sopenharmony_ci rc = set_validity_icpt(scb_s, 0x0011U); 75062306a36Sopenharmony_ci else if ((gpa & PAGE_MASK) != 75162306a36Sopenharmony_ci ((gpa + sizeof(struct bsca_block) - 1) & PAGE_MASK)) 75262306a36Sopenharmony_ci rc = set_validity_icpt(scb_s, 0x003bU); 75362306a36Sopenharmony_ci if (!rc) { 75462306a36Sopenharmony_ci rc = pin_guest_page(vcpu->kvm, gpa, &hpa); 75562306a36Sopenharmony_ci if (rc) 75662306a36Sopenharmony_ci rc = set_validity_icpt(scb_s, 0x0034U); 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci if (rc) 75962306a36Sopenharmony_ci goto unpin; 76062306a36Sopenharmony_ci vsie_page->sca_gpa = gpa; 76162306a36Sopenharmony_ci scb_s->scaoh = (u32)((u64)hpa >> 32); 76262306a36Sopenharmony_ci scb_s->scaol = (u32)(u64)hpa; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci gpa = READ_ONCE(scb_o->itdba) & ~0xffUL; 76662306a36Sopenharmony_ci if (gpa && (scb_s->ecb & ECB_TE)) { 76762306a36Sopenharmony_ci if (gpa < 2 * PAGE_SIZE) { 76862306a36Sopenharmony_ci rc = set_validity_icpt(scb_s, 0x0080U); 76962306a36Sopenharmony_ci goto unpin; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci /* 256 bytes cannot cross page boundaries */ 77262306a36Sopenharmony_ci rc = pin_guest_page(vcpu->kvm, gpa, &hpa); 77362306a36Sopenharmony_ci if (rc) { 77462306a36Sopenharmony_ci rc = set_validity_icpt(scb_s, 0x0080U); 77562306a36Sopenharmony_ci goto unpin; 77662306a36Sopenharmony_ci } 77762306a36Sopenharmony_ci vsie_page->itdba_gpa = gpa; 77862306a36Sopenharmony_ci scb_s->itdba = hpa; 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci gpa = READ_ONCE(scb_o->gvrd) & ~0x1ffUL; 78262306a36Sopenharmony_ci if (gpa && (scb_s->eca & ECA_VX) && !(scb_s->ecd & ECD_HOSTREGMGMT)) { 78362306a36Sopenharmony_ci if (gpa < 2 * PAGE_SIZE) { 78462306a36Sopenharmony_ci rc = set_validity_icpt(scb_s, 0x1310U); 78562306a36Sopenharmony_ci goto unpin; 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci /* 78862306a36Sopenharmony_ci * 512 bytes vector registers cannot cross page boundaries 78962306a36Sopenharmony_ci * if this block gets bigger, we have to shadow it. 79062306a36Sopenharmony_ci */ 79162306a36Sopenharmony_ci rc = pin_guest_page(vcpu->kvm, gpa, &hpa); 79262306a36Sopenharmony_ci if (rc) { 79362306a36Sopenharmony_ci rc = set_validity_icpt(scb_s, 0x1310U); 79462306a36Sopenharmony_ci goto unpin; 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci vsie_page->gvrd_gpa = gpa; 79762306a36Sopenharmony_ci scb_s->gvrd = hpa; 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci gpa = READ_ONCE(scb_o->riccbd) & ~0x3fUL; 80162306a36Sopenharmony_ci if (gpa && (scb_s->ecb3 & ECB3_RI)) { 80262306a36Sopenharmony_ci if (gpa < 2 * PAGE_SIZE) { 80362306a36Sopenharmony_ci rc = set_validity_icpt(scb_s, 0x0043U); 80462306a36Sopenharmony_ci goto unpin; 80562306a36Sopenharmony_ci } 80662306a36Sopenharmony_ci /* 64 bytes cannot cross page boundaries */ 80762306a36Sopenharmony_ci rc = pin_guest_page(vcpu->kvm, gpa, &hpa); 80862306a36Sopenharmony_ci if (rc) { 80962306a36Sopenharmony_ci rc = set_validity_icpt(scb_s, 0x0043U); 81062306a36Sopenharmony_ci goto unpin; 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci /* Validity 0x0044 will be checked by SIE */ 81362306a36Sopenharmony_ci vsie_page->riccbd_gpa = gpa; 81462306a36Sopenharmony_ci scb_s->riccbd = hpa; 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci if (((scb_s->ecb & ECB_GS) && !(scb_s->ecd & ECD_HOSTREGMGMT)) || 81762306a36Sopenharmony_ci (scb_s->ecd & ECD_ETOKENF)) { 81862306a36Sopenharmony_ci unsigned long sdnxc; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci gpa = READ_ONCE(scb_o->sdnxo) & ~0xfUL; 82162306a36Sopenharmony_ci sdnxc = READ_ONCE(scb_o->sdnxo) & 0xfUL; 82262306a36Sopenharmony_ci if (!gpa || gpa < 2 * PAGE_SIZE) { 82362306a36Sopenharmony_ci rc = set_validity_icpt(scb_s, 0x10b0U); 82462306a36Sopenharmony_ci goto unpin; 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci if (sdnxc < 6 || sdnxc > 12) { 82762306a36Sopenharmony_ci rc = set_validity_icpt(scb_s, 0x10b1U); 82862306a36Sopenharmony_ci goto unpin; 82962306a36Sopenharmony_ci } 83062306a36Sopenharmony_ci if (gpa & ((1 << sdnxc) - 1)) { 83162306a36Sopenharmony_ci rc = set_validity_icpt(scb_s, 0x10b2U); 83262306a36Sopenharmony_ci goto unpin; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci /* Due to alignment rules (checked above) this cannot 83562306a36Sopenharmony_ci * cross page boundaries 83662306a36Sopenharmony_ci */ 83762306a36Sopenharmony_ci rc = pin_guest_page(vcpu->kvm, gpa, &hpa); 83862306a36Sopenharmony_ci if (rc) { 83962306a36Sopenharmony_ci rc = set_validity_icpt(scb_s, 0x10b0U); 84062306a36Sopenharmony_ci goto unpin; 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci vsie_page->sdnx_gpa = gpa; 84362306a36Sopenharmony_ci scb_s->sdnxo = hpa | sdnxc; 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci return 0; 84662306a36Sopenharmony_ciunpin: 84762306a36Sopenharmony_ci unpin_blocks(vcpu, vsie_page); 84862306a36Sopenharmony_ci return rc; 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci/* unpin the scb provided by guest 2, marking it as dirty */ 85262306a36Sopenharmony_cistatic void unpin_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page, 85362306a36Sopenharmony_ci gpa_t gpa) 85462306a36Sopenharmony_ci{ 85562306a36Sopenharmony_ci hpa_t hpa = (hpa_t) vsie_page->scb_o; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci if (hpa) 85862306a36Sopenharmony_ci unpin_guest_page(vcpu->kvm, gpa, hpa); 85962306a36Sopenharmony_ci vsie_page->scb_o = NULL; 86062306a36Sopenharmony_ci} 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci/* 86362306a36Sopenharmony_ci * Pin the scb at gpa provided by guest 2 at vsie_page->scb_o. 86462306a36Sopenharmony_ci * 86562306a36Sopenharmony_ci * Returns: - 0 if the scb was pinned. 86662306a36Sopenharmony_ci * - > 0 if control has to be given to guest 2 86762306a36Sopenharmony_ci */ 86862306a36Sopenharmony_cistatic int pin_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page, 86962306a36Sopenharmony_ci gpa_t gpa) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci hpa_t hpa; 87262306a36Sopenharmony_ci int rc; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci rc = pin_guest_page(vcpu->kvm, gpa, &hpa); 87562306a36Sopenharmony_ci if (rc) { 87662306a36Sopenharmony_ci rc = kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); 87762306a36Sopenharmony_ci WARN_ON_ONCE(rc); 87862306a36Sopenharmony_ci return 1; 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci vsie_page->scb_o = phys_to_virt(hpa); 88162306a36Sopenharmony_ci return 0; 88262306a36Sopenharmony_ci} 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci/* 88562306a36Sopenharmony_ci * Inject a fault into guest 2. 88662306a36Sopenharmony_ci * 88762306a36Sopenharmony_ci * Returns: - > 0 if control has to be given to guest 2 88862306a36Sopenharmony_ci * < 0 if an error occurred during injection. 88962306a36Sopenharmony_ci */ 89062306a36Sopenharmony_cistatic int inject_fault(struct kvm_vcpu *vcpu, __u16 code, __u64 vaddr, 89162306a36Sopenharmony_ci bool write_flag) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci struct kvm_s390_pgm_info pgm = { 89462306a36Sopenharmony_ci .code = code, 89562306a36Sopenharmony_ci .trans_exc_code = 89662306a36Sopenharmony_ci /* 0-51: virtual address */ 89762306a36Sopenharmony_ci (vaddr & 0xfffffffffffff000UL) | 89862306a36Sopenharmony_ci /* 52-53: store / fetch */ 89962306a36Sopenharmony_ci (((unsigned int) !write_flag) + 1) << 10, 90062306a36Sopenharmony_ci /* 62-63: asce id (always primary == 0) */ 90162306a36Sopenharmony_ci .exc_access_id = 0, /* always primary */ 90262306a36Sopenharmony_ci .op_access_id = 0, /* not MVPG */ 90362306a36Sopenharmony_ci }; 90462306a36Sopenharmony_ci int rc; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci if (code == PGM_PROTECTION) 90762306a36Sopenharmony_ci pgm.trans_exc_code |= 0x4UL; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci rc = kvm_s390_inject_prog_irq(vcpu, &pgm); 91062306a36Sopenharmony_ci return rc ? rc : 1; 91162306a36Sopenharmony_ci} 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci/* 91462306a36Sopenharmony_ci * Handle a fault during vsie execution on a gmap shadow. 91562306a36Sopenharmony_ci * 91662306a36Sopenharmony_ci * Returns: - 0 if the fault was resolved 91762306a36Sopenharmony_ci * - > 0 if control has to be given to guest 2 91862306a36Sopenharmony_ci * - < 0 if an error occurred 91962306a36Sopenharmony_ci */ 92062306a36Sopenharmony_cistatic int handle_fault(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) 92162306a36Sopenharmony_ci{ 92262306a36Sopenharmony_ci int rc; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci if (current->thread.gmap_int_code == PGM_PROTECTION) 92562306a36Sopenharmony_ci /* we can directly forward all protection exceptions */ 92662306a36Sopenharmony_ci return inject_fault(vcpu, PGM_PROTECTION, 92762306a36Sopenharmony_ci current->thread.gmap_addr, 1); 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci rc = kvm_s390_shadow_fault(vcpu, vsie_page->gmap, 93062306a36Sopenharmony_ci current->thread.gmap_addr, NULL); 93162306a36Sopenharmony_ci if (rc > 0) { 93262306a36Sopenharmony_ci rc = inject_fault(vcpu, rc, 93362306a36Sopenharmony_ci current->thread.gmap_addr, 93462306a36Sopenharmony_ci current->thread.gmap_write_flag); 93562306a36Sopenharmony_ci if (rc >= 0) 93662306a36Sopenharmony_ci vsie_page->fault_addr = current->thread.gmap_addr; 93762306a36Sopenharmony_ci } 93862306a36Sopenharmony_ci return rc; 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci/* 94262306a36Sopenharmony_ci * Retry the previous fault that required guest 2 intervention. This avoids 94362306a36Sopenharmony_ci * one superfluous SIE re-entry and direct exit. 94462306a36Sopenharmony_ci * 94562306a36Sopenharmony_ci * Will ignore any errors. The next SIE fault will do proper fault handling. 94662306a36Sopenharmony_ci */ 94762306a36Sopenharmony_cistatic void handle_last_fault(struct kvm_vcpu *vcpu, 94862306a36Sopenharmony_ci struct vsie_page *vsie_page) 94962306a36Sopenharmony_ci{ 95062306a36Sopenharmony_ci if (vsie_page->fault_addr) 95162306a36Sopenharmony_ci kvm_s390_shadow_fault(vcpu, vsie_page->gmap, 95262306a36Sopenharmony_ci vsie_page->fault_addr, NULL); 95362306a36Sopenharmony_ci vsie_page->fault_addr = 0; 95462306a36Sopenharmony_ci} 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_cistatic inline void clear_vsie_icpt(struct vsie_page *vsie_page) 95762306a36Sopenharmony_ci{ 95862306a36Sopenharmony_ci vsie_page->scb_s.icptcode = 0; 95962306a36Sopenharmony_ci} 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci/* rewind the psw and clear the vsie icpt, so we can retry execution */ 96262306a36Sopenharmony_cistatic void retry_vsie_icpt(struct vsie_page *vsie_page) 96362306a36Sopenharmony_ci{ 96462306a36Sopenharmony_ci struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s; 96562306a36Sopenharmony_ci int ilen = insn_length(scb_s->ipa >> 8); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci /* take care of EXECUTE instructions */ 96862306a36Sopenharmony_ci if (scb_s->icptstatus & 1) { 96962306a36Sopenharmony_ci ilen = (scb_s->icptstatus >> 4) & 0x6; 97062306a36Sopenharmony_ci if (!ilen) 97162306a36Sopenharmony_ci ilen = 4; 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci scb_s->gpsw.addr = __rewind_psw(scb_s->gpsw, ilen); 97462306a36Sopenharmony_ci clear_vsie_icpt(vsie_page); 97562306a36Sopenharmony_ci} 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci/* 97862306a36Sopenharmony_ci * Try to shadow + enable the guest 2 provided facility list. 97962306a36Sopenharmony_ci * Retry instruction execution if enabled for and provided by guest 2. 98062306a36Sopenharmony_ci * 98162306a36Sopenharmony_ci * Returns: - 0 if handled (retry or guest 2 icpt) 98262306a36Sopenharmony_ci * - > 0 if control has to be given to guest 2 98362306a36Sopenharmony_ci */ 98462306a36Sopenharmony_cistatic int handle_stfle(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) 98562306a36Sopenharmony_ci{ 98662306a36Sopenharmony_ci struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s; 98762306a36Sopenharmony_ci __u32 fac = READ_ONCE(vsie_page->scb_o->fac) & 0x7ffffff8U; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci if (fac && test_kvm_facility(vcpu->kvm, 7)) { 99062306a36Sopenharmony_ci retry_vsie_icpt(vsie_page); 99162306a36Sopenharmony_ci if (read_guest_real(vcpu, fac, &vsie_page->fac, 99262306a36Sopenharmony_ci sizeof(vsie_page->fac))) 99362306a36Sopenharmony_ci return set_validity_icpt(scb_s, 0x1090U); 99462306a36Sopenharmony_ci scb_s->fac = (__u32)(__u64) &vsie_page->fac; 99562306a36Sopenharmony_ci } 99662306a36Sopenharmony_ci return 0; 99762306a36Sopenharmony_ci} 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci/* 100062306a36Sopenharmony_ci * Get a register for a nested guest. 100162306a36Sopenharmony_ci * @vcpu the vcpu of the guest 100262306a36Sopenharmony_ci * @vsie_page the vsie_page for the nested guest 100362306a36Sopenharmony_ci * @reg the register number, the upper 4 bits are ignored. 100462306a36Sopenharmony_ci * returns: the value of the register. 100562306a36Sopenharmony_ci */ 100662306a36Sopenharmony_cistatic u64 vsie_get_register(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page, u8 reg) 100762306a36Sopenharmony_ci{ 100862306a36Sopenharmony_ci /* no need to validate the parameter and/or perform error handling */ 100962306a36Sopenharmony_ci reg &= 0xf; 101062306a36Sopenharmony_ci switch (reg) { 101162306a36Sopenharmony_ci case 15: 101262306a36Sopenharmony_ci return vsie_page->scb_s.gg15; 101362306a36Sopenharmony_ci case 14: 101462306a36Sopenharmony_ci return vsie_page->scb_s.gg14; 101562306a36Sopenharmony_ci default: 101662306a36Sopenharmony_ci return vcpu->run->s.regs.gprs[reg]; 101762306a36Sopenharmony_ci } 101862306a36Sopenharmony_ci} 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_cistatic int vsie_handle_mvpg(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) 102162306a36Sopenharmony_ci{ 102262306a36Sopenharmony_ci struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s; 102362306a36Sopenharmony_ci unsigned long pei_dest, pei_src, src, dest, mask, prefix; 102462306a36Sopenharmony_ci u64 *pei_block = &vsie_page->scb_o->mcic; 102562306a36Sopenharmony_ci int edat, rc_dest, rc_src; 102662306a36Sopenharmony_ci union ctlreg0 cr0; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci cr0.val = vcpu->arch.sie_block->gcr[0]; 102962306a36Sopenharmony_ci edat = cr0.edat && test_kvm_facility(vcpu->kvm, 8); 103062306a36Sopenharmony_ci mask = _kvm_s390_logical_to_effective(&scb_s->gpsw, PAGE_MASK); 103162306a36Sopenharmony_ci prefix = scb_s->prefix << GUEST_PREFIX_SHIFT; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci dest = vsie_get_register(vcpu, vsie_page, scb_s->ipb >> 20) & mask; 103462306a36Sopenharmony_ci dest = _kvm_s390_real_to_abs(prefix, dest) + scb_s->mso; 103562306a36Sopenharmony_ci src = vsie_get_register(vcpu, vsie_page, scb_s->ipb >> 16) & mask; 103662306a36Sopenharmony_ci src = _kvm_s390_real_to_abs(prefix, src) + scb_s->mso; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci rc_dest = kvm_s390_shadow_fault(vcpu, vsie_page->gmap, dest, &pei_dest); 103962306a36Sopenharmony_ci rc_src = kvm_s390_shadow_fault(vcpu, vsie_page->gmap, src, &pei_src); 104062306a36Sopenharmony_ci /* 104162306a36Sopenharmony_ci * Either everything went well, or something non-critical went wrong 104262306a36Sopenharmony_ci * e.g. because of a race. In either case, simply retry. 104362306a36Sopenharmony_ci */ 104462306a36Sopenharmony_ci if (rc_dest == -EAGAIN || rc_src == -EAGAIN || (!rc_dest && !rc_src)) { 104562306a36Sopenharmony_ci retry_vsie_icpt(vsie_page); 104662306a36Sopenharmony_ci return -EAGAIN; 104762306a36Sopenharmony_ci } 104862306a36Sopenharmony_ci /* Something more serious went wrong, propagate the error */ 104962306a36Sopenharmony_ci if (rc_dest < 0) 105062306a36Sopenharmony_ci return rc_dest; 105162306a36Sopenharmony_ci if (rc_src < 0) 105262306a36Sopenharmony_ci return rc_src; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci /* The only possible suppressing exception: just deliver it */ 105562306a36Sopenharmony_ci if (rc_dest == PGM_TRANSLATION_SPEC || rc_src == PGM_TRANSLATION_SPEC) { 105662306a36Sopenharmony_ci clear_vsie_icpt(vsie_page); 105762306a36Sopenharmony_ci rc_dest = kvm_s390_inject_program_int(vcpu, PGM_TRANSLATION_SPEC); 105862306a36Sopenharmony_ci WARN_ON_ONCE(rc_dest); 105962306a36Sopenharmony_ci return 1; 106062306a36Sopenharmony_ci } 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci /* 106362306a36Sopenharmony_ci * Forward the PEI intercept to the guest if it was a page fault, or 106462306a36Sopenharmony_ci * also for segment and region table faults if EDAT applies. 106562306a36Sopenharmony_ci */ 106662306a36Sopenharmony_ci if (edat) { 106762306a36Sopenharmony_ci rc_dest = rc_dest == PGM_ASCE_TYPE ? rc_dest : 0; 106862306a36Sopenharmony_ci rc_src = rc_src == PGM_ASCE_TYPE ? rc_src : 0; 106962306a36Sopenharmony_ci } else { 107062306a36Sopenharmony_ci rc_dest = rc_dest != PGM_PAGE_TRANSLATION ? rc_dest : 0; 107162306a36Sopenharmony_ci rc_src = rc_src != PGM_PAGE_TRANSLATION ? rc_src : 0; 107262306a36Sopenharmony_ci } 107362306a36Sopenharmony_ci if (!rc_dest && !rc_src) { 107462306a36Sopenharmony_ci pei_block[0] = pei_dest; 107562306a36Sopenharmony_ci pei_block[1] = pei_src; 107662306a36Sopenharmony_ci return 1; 107762306a36Sopenharmony_ci } 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci retry_vsie_icpt(vsie_page); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci /* 108262306a36Sopenharmony_ci * The host has edat, and the guest does not, or it was an ASCE type 108362306a36Sopenharmony_ci * exception. The host needs to inject the appropriate DAT interrupts 108462306a36Sopenharmony_ci * into the guest. 108562306a36Sopenharmony_ci */ 108662306a36Sopenharmony_ci if (rc_dest) 108762306a36Sopenharmony_ci return inject_fault(vcpu, rc_dest, dest, 1); 108862306a36Sopenharmony_ci return inject_fault(vcpu, rc_src, src, 0); 108962306a36Sopenharmony_ci} 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci/* 109262306a36Sopenharmony_ci * Run the vsie on a shadow scb and a shadow gmap, without any further 109362306a36Sopenharmony_ci * sanity checks, handling SIE faults. 109462306a36Sopenharmony_ci * 109562306a36Sopenharmony_ci * Returns: - 0 everything went fine 109662306a36Sopenharmony_ci * - > 0 if control has to be given to guest 2 109762306a36Sopenharmony_ci * - < 0 if an error occurred 109862306a36Sopenharmony_ci */ 109962306a36Sopenharmony_cistatic int do_vsie_run(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) 110062306a36Sopenharmony_ci __releases(vcpu->kvm->srcu) 110162306a36Sopenharmony_ci __acquires(vcpu->kvm->srcu) 110262306a36Sopenharmony_ci{ 110362306a36Sopenharmony_ci struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s; 110462306a36Sopenharmony_ci struct kvm_s390_sie_block *scb_o = vsie_page->scb_o; 110562306a36Sopenharmony_ci int guest_bp_isolation; 110662306a36Sopenharmony_ci int rc = 0; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci handle_last_fault(vcpu, vsie_page); 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci kvm_vcpu_srcu_read_unlock(vcpu); 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci /* save current guest state of bp isolation override */ 111362306a36Sopenharmony_ci guest_bp_isolation = test_thread_flag(TIF_ISOLATE_BP_GUEST); 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci /* 111662306a36Sopenharmony_ci * The guest is running with BPBC, so we have to force it on for our 111762306a36Sopenharmony_ci * nested guest. This is done by enabling BPBC globally, so the BPBC 111862306a36Sopenharmony_ci * control in the SCB (which the nested guest can modify) is simply 111962306a36Sopenharmony_ci * ignored. 112062306a36Sopenharmony_ci */ 112162306a36Sopenharmony_ci if (test_kvm_facility(vcpu->kvm, 82) && 112262306a36Sopenharmony_ci vcpu->arch.sie_block->fpf & FPF_BPBC) 112362306a36Sopenharmony_ci set_thread_flag(TIF_ISOLATE_BP_GUEST); 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci local_irq_disable(); 112662306a36Sopenharmony_ci guest_enter_irqoff(); 112762306a36Sopenharmony_ci local_irq_enable(); 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci /* 113062306a36Sopenharmony_ci * Simulate a SIE entry of the VCPU (see sie64a), so VCPU blocking 113162306a36Sopenharmony_ci * and VCPU requests also hinder the vSIE from running and lead 113262306a36Sopenharmony_ci * to an immediate exit. kvm_s390_vsie_kick() has to be used to 113362306a36Sopenharmony_ci * also kick the vSIE. 113462306a36Sopenharmony_ci */ 113562306a36Sopenharmony_ci vcpu->arch.sie_block->prog0c |= PROG_IN_SIE; 113662306a36Sopenharmony_ci barrier(); 113762306a36Sopenharmony_ci if (test_cpu_flag(CIF_FPU)) 113862306a36Sopenharmony_ci load_fpu_regs(); 113962306a36Sopenharmony_ci if (!kvm_s390_vcpu_sie_inhibited(vcpu)) 114062306a36Sopenharmony_ci rc = sie64a(scb_s, vcpu->run->s.regs.gprs); 114162306a36Sopenharmony_ci barrier(); 114262306a36Sopenharmony_ci vcpu->arch.sie_block->prog0c &= ~PROG_IN_SIE; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci local_irq_disable(); 114562306a36Sopenharmony_ci guest_exit_irqoff(); 114662306a36Sopenharmony_ci local_irq_enable(); 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci /* restore guest state for bp isolation override */ 114962306a36Sopenharmony_ci if (!guest_bp_isolation) 115062306a36Sopenharmony_ci clear_thread_flag(TIF_ISOLATE_BP_GUEST); 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci kvm_vcpu_srcu_read_lock(vcpu); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci if (rc == -EINTR) { 115562306a36Sopenharmony_ci VCPU_EVENT(vcpu, 3, "%s", "machine check"); 115662306a36Sopenharmony_ci kvm_s390_reinject_machine_check(vcpu, &vsie_page->mcck_info); 115762306a36Sopenharmony_ci return 0; 115862306a36Sopenharmony_ci } 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci if (rc > 0) 116162306a36Sopenharmony_ci rc = 0; /* we could still have an icpt */ 116262306a36Sopenharmony_ci else if (rc == -EFAULT) 116362306a36Sopenharmony_ci return handle_fault(vcpu, vsie_page); 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci switch (scb_s->icptcode) { 116662306a36Sopenharmony_ci case ICPT_INST: 116762306a36Sopenharmony_ci if (scb_s->ipa == 0xb2b0) 116862306a36Sopenharmony_ci rc = handle_stfle(vcpu, vsie_page); 116962306a36Sopenharmony_ci break; 117062306a36Sopenharmony_ci case ICPT_STOP: 117162306a36Sopenharmony_ci /* stop not requested by g2 - must have been a kick */ 117262306a36Sopenharmony_ci if (!(atomic_read(&scb_o->cpuflags) & CPUSTAT_STOP_INT)) 117362306a36Sopenharmony_ci clear_vsie_icpt(vsie_page); 117462306a36Sopenharmony_ci break; 117562306a36Sopenharmony_ci case ICPT_VALIDITY: 117662306a36Sopenharmony_ci if ((scb_s->ipa & 0xf000) != 0xf000) 117762306a36Sopenharmony_ci scb_s->ipa += 0x1000; 117862306a36Sopenharmony_ci break; 117962306a36Sopenharmony_ci case ICPT_PARTEXEC: 118062306a36Sopenharmony_ci if (scb_s->ipa == 0xb254) 118162306a36Sopenharmony_ci rc = vsie_handle_mvpg(vcpu, vsie_page); 118262306a36Sopenharmony_ci break; 118362306a36Sopenharmony_ci } 118462306a36Sopenharmony_ci return rc; 118562306a36Sopenharmony_ci} 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_cistatic void release_gmap_shadow(struct vsie_page *vsie_page) 118862306a36Sopenharmony_ci{ 118962306a36Sopenharmony_ci if (vsie_page->gmap) 119062306a36Sopenharmony_ci gmap_put(vsie_page->gmap); 119162306a36Sopenharmony_ci WRITE_ONCE(vsie_page->gmap, NULL); 119262306a36Sopenharmony_ci prefix_unmapped(vsie_page); 119362306a36Sopenharmony_ci} 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_cistatic int acquire_gmap_shadow(struct kvm_vcpu *vcpu, 119662306a36Sopenharmony_ci struct vsie_page *vsie_page) 119762306a36Sopenharmony_ci{ 119862306a36Sopenharmony_ci unsigned long asce; 119962306a36Sopenharmony_ci union ctlreg0 cr0; 120062306a36Sopenharmony_ci struct gmap *gmap; 120162306a36Sopenharmony_ci int edat; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci asce = vcpu->arch.sie_block->gcr[1]; 120462306a36Sopenharmony_ci cr0.val = vcpu->arch.sie_block->gcr[0]; 120562306a36Sopenharmony_ci edat = cr0.edat && test_kvm_facility(vcpu->kvm, 8); 120662306a36Sopenharmony_ci edat += edat && test_kvm_facility(vcpu->kvm, 78); 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci /* 120962306a36Sopenharmony_ci * ASCE or EDAT could have changed since last icpt, or the gmap 121062306a36Sopenharmony_ci * we're holding has been unshadowed. If the gmap is still valid, 121162306a36Sopenharmony_ci * we can safely reuse it. 121262306a36Sopenharmony_ci */ 121362306a36Sopenharmony_ci if (vsie_page->gmap && gmap_shadow_valid(vsie_page->gmap, asce, edat)) { 121462306a36Sopenharmony_ci vcpu->kvm->stat.gmap_shadow_reuse++; 121562306a36Sopenharmony_ci return 0; 121662306a36Sopenharmony_ci } 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci /* release the old shadow - if any, and mark the prefix as unmapped */ 121962306a36Sopenharmony_ci release_gmap_shadow(vsie_page); 122062306a36Sopenharmony_ci gmap = gmap_shadow(vcpu->arch.gmap, asce, edat); 122162306a36Sopenharmony_ci if (IS_ERR(gmap)) 122262306a36Sopenharmony_ci return PTR_ERR(gmap); 122362306a36Sopenharmony_ci vcpu->kvm->stat.gmap_shadow_create++; 122462306a36Sopenharmony_ci WRITE_ONCE(vsie_page->gmap, gmap); 122562306a36Sopenharmony_ci return 0; 122662306a36Sopenharmony_ci} 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci/* 122962306a36Sopenharmony_ci * Register the shadow scb at the VCPU, e.g. for kicking out of vsie. 123062306a36Sopenharmony_ci */ 123162306a36Sopenharmony_cistatic void register_shadow_scb(struct kvm_vcpu *vcpu, 123262306a36Sopenharmony_ci struct vsie_page *vsie_page) 123362306a36Sopenharmony_ci{ 123462306a36Sopenharmony_ci struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s; 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci WRITE_ONCE(vcpu->arch.vsie_block, &vsie_page->scb_s); 123762306a36Sopenharmony_ci /* 123862306a36Sopenharmony_ci * External calls have to lead to a kick of the vcpu and 123962306a36Sopenharmony_ci * therefore the vsie -> Simulate Wait state. 124062306a36Sopenharmony_ci */ 124162306a36Sopenharmony_ci kvm_s390_set_cpuflags(vcpu, CPUSTAT_WAIT); 124262306a36Sopenharmony_ci /* 124362306a36Sopenharmony_ci * We have to adjust the g3 epoch by the g2 epoch. The epoch will 124462306a36Sopenharmony_ci * automatically be adjusted on tod clock changes via kvm_sync_clock. 124562306a36Sopenharmony_ci */ 124662306a36Sopenharmony_ci preempt_disable(); 124762306a36Sopenharmony_ci scb_s->epoch += vcpu->kvm->arch.epoch; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci if (scb_s->ecd & ECD_MEF) { 125062306a36Sopenharmony_ci scb_s->epdx += vcpu->kvm->arch.epdx; 125162306a36Sopenharmony_ci if (scb_s->epoch < vcpu->kvm->arch.epoch) 125262306a36Sopenharmony_ci scb_s->epdx += 1; 125362306a36Sopenharmony_ci } 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci preempt_enable(); 125662306a36Sopenharmony_ci} 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci/* 125962306a36Sopenharmony_ci * Unregister a shadow scb from a VCPU. 126062306a36Sopenharmony_ci */ 126162306a36Sopenharmony_cistatic void unregister_shadow_scb(struct kvm_vcpu *vcpu) 126262306a36Sopenharmony_ci{ 126362306a36Sopenharmony_ci kvm_s390_clear_cpuflags(vcpu, CPUSTAT_WAIT); 126462306a36Sopenharmony_ci WRITE_ONCE(vcpu->arch.vsie_block, NULL); 126562306a36Sopenharmony_ci} 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci/* 126862306a36Sopenharmony_ci * Run the vsie on a shadowed scb, managing the gmap shadow, handling 126962306a36Sopenharmony_ci * prefix pages and faults. 127062306a36Sopenharmony_ci * 127162306a36Sopenharmony_ci * Returns: - 0 if no errors occurred 127262306a36Sopenharmony_ci * - > 0 if control has to be given to guest 2 127362306a36Sopenharmony_ci * - -ENOMEM if out of memory 127462306a36Sopenharmony_ci */ 127562306a36Sopenharmony_cistatic int vsie_run(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) 127662306a36Sopenharmony_ci{ 127762306a36Sopenharmony_ci struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s; 127862306a36Sopenharmony_ci int rc = 0; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci while (1) { 128162306a36Sopenharmony_ci rc = acquire_gmap_shadow(vcpu, vsie_page); 128262306a36Sopenharmony_ci if (!rc) 128362306a36Sopenharmony_ci rc = map_prefix(vcpu, vsie_page); 128462306a36Sopenharmony_ci if (!rc) { 128562306a36Sopenharmony_ci gmap_enable(vsie_page->gmap); 128662306a36Sopenharmony_ci update_intervention_requests(vsie_page); 128762306a36Sopenharmony_ci rc = do_vsie_run(vcpu, vsie_page); 128862306a36Sopenharmony_ci gmap_enable(vcpu->arch.gmap); 128962306a36Sopenharmony_ci } 129062306a36Sopenharmony_ci atomic_andnot(PROG_BLOCK_SIE, &scb_s->prog20); 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci if (rc == -EAGAIN) 129362306a36Sopenharmony_ci rc = 0; 129462306a36Sopenharmony_ci if (rc || scb_s->icptcode || signal_pending(current) || 129562306a36Sopenharmony_ci kvm_s390_vcpu_has_irq(vcpu, 0) || 129662306a36Sopenharmony_ci kvm_s390_vcpu_sie_inhibited(vcpu)) 129762306a36Sopenharmony_ci break; 129862306a36Sopenharmony_ci cond_resched(); 129962306a36Sopenharmony_ci } 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci if (rc == -EFAULT) { 130262306a36Sopenharmony_ci /* 130362306a36Sopenharmony_ci * Addressing exceptions are always presentes as intercepts. 130462306a36Sopenharmony_ci * As addressing exceptions are suppressing and our guest 3 PSW 130562306a36Sopenharmony_ci * points at the responsible instruction, we have to 130662306a36Sopenharmony_ci * forward the PSW and set the ilc. If we can't read guest 3 130762306a36Sopenharmony_ci * instruction, we can use an arbitrary ilc. Let's always use 130862306a36Sopenharmony_ci * ilen = 4 for now, so we can avoid reading in guest 3 virtual 130962306a36Sopenharmony_ci * memory. (we could also fake the shadow so the hardware 131062306a36Sopenharmony_ci * handles it). 131162306a36Sopenharmony_ci */ 131262306a36Sopenharmony_ci scb_s->icptcode = ICPT_PROGI; 131362306a36Sopenharmony_ci scb_s->iprcc = PGM_ADDRESSING; 131462306a36Sopenharmony_ci scb_s->pgmilc = 4; 131562306a36Sopenharmony_ci scb_s->gpsw.addr = __rewind_psw(scb_s->gpsw, 4); 131662306a36Sopenharmony_ci rc = 1; 131762306a36Sopenharmony_ci } 131862306a36Sopenharmony_ci return rc; 131962306a36Sopenharmony_ci} 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci/* 132262306a36Sopenharmony_ci * Get or create a vsie page for a scb address. 132362306a36Sopenharmony_ci * 132462306a36Sopenharmony_ci * Returns: - address of a vsie page (cached or new one) 132562306a36Sopenharmony_ci * - NULL if the same scb address is already used by another VCPU 132662306a36Sopenharmony_ci * - ERR_PTR(-ENOMEM) if out of memory 132762306a36Sopenharmony_ci */ 132862306a36Sopenharmony_cistatic struct vsie_page *get_vsie_page(struct kvm *kvm, unsigned long addr) 132962306a36Sopenharmony_ci{ 133062306a36Sopenharmony_ci struct vsie_page *vsie_page; 133162306a36Sopenharmony_ci struct page *page; 133262306a36Sopenharmony_ci int nr_vcpus; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci rcu_read_lock(); 133562306a36Sopenharmony_ci page = radix_tree_lookup(&kvm->arch.vsie.addr_to_page, addr >> 9); 133662306a36Sopenharmony_ci rcu_read_unlock(); 133762306a36Sopenharmony_ci if (page) { 133862306a36Sopenharmony_ci if (page_ref_inc_return(page) == 2) 133962306a36Sopenharmony_ci return page_to_virt(page); 134062306a36Sopenharmony_ci page_ref_dec(page); 134162306a36Sopenharmony_ci } 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci /* 134462306a36Sopenharmony_ci * We want at least #online_vcpus shadows, so every VCPU can execute 134562306a36Sopenharmony_ci * the VSIE in parallel. 134662306a36Sopenharmony_ci */ 134762306a36Sopenharmony_ci nr_vcpus = atomic_read(&kvm->online_vcpus); 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci mutex_lock(&kvm->arch.vsie.mutex); 135062306a36Sopenharmony_ci if (kvm->arch.vsie.page_count < nr_vcpus) { 135162306a36Sopenharmony_ci page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO | GFP_DMA); 135262306a36Sopenharmony_ci if (!page) { 135362306a36Sopenharmony_ci mutex_unlock(&kvm->arch.vsie.mutex); 135462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 135562306a36Sopenharmony_ci } 135662306a36Sopenharmony_ci page_ref_inc(page); 135762306a36Sopenharmony_ci kvm->arch.vsie.pages[kvm->arch.vsie.page_count] = page; 135862306a36Sopenharmony_ci kvm->arch.vsie.page_count++; 135962306a36Sopenharmony_ci } else { 136062306a36Sopenharmony_ci /* reuse an existing entry that belongs to nobody */ 136162306a36Sopenharmony_ci while (true) { 136262306a36Sopenharmony_ci page = kvm->arch.vsie.pages[kvm->arch.vsie.next]; 136362306a36Sopenharmony_ci if (page_ref_inc_return(page) == 2) 136462306a36Sopenharmony_ci break; 136562306a36Sopenharmony_ci page_ref_dec(page); 136662306a36Sopenharmony_ci kvm->arch.vsie.next++; 136762306a36Sopenharmony_ci kvm->arch.vsie.next %= nr_vcpus; 136862306a36Sopenharmony_ci } 136962306a36Sopenharmony_ci radix_tree_delete(&kvm->arch.vsie.addr_to_page, page->index >> 9); 137062306a36Sopenharmony_ci } 137162306a36Sopenharmony_ci page->index = addr; 137262306a36Sopenharmony_ci /* double use of the same address */ 137362306a36Sopenharmony_ci if (radix_tree_insert(&kvm->arch.vsie.addr_to_page, addr >> 9, page)) { 137462306a36Sopenharmony_ci page_ref_dec(page); 137562306a36Sopenharmony_ci mutex_unlock(&kvm->arch.vsie.mutex); 137662306a36Sopenharmony_ci return NULL; 137762306a36Sopenharmony_ci } 137862306a36Sopenharmony_ci mutex_unlock(&kvm->arch.vsie.mutex); 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci vsie_page = page_to_virt(page); 138162306a36Sopenharmony_ci memset(&vsie_page->scb_s, 0, sizeof(struct kvm_s390_sie_block)); 138262306a36Sopenharmony_ci release_gmap_shadow(vsie_page); 138362306a36Sopenharmony_ci vsie_page->fault_addr = 0; 138462306a36Sopenharmony_ci vsie_page->scb_s.ihcpu = 0xffffU; 138562306a36Sopenharmony_ci return vsie_page; 138662306a36Sopenharmony_ci} 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci/* put a vsie page acquired via get_vsie_page */ 138962306a36Sopenharmony_cistatic void put_vsie_page(struct kvm *kvm, struct vsie_page *vsie_page) 139062306a36Sopenharmony_ci{ 139162306a36Sopenharmony_ci struct page *page = pfn_to_page(__pa(vsie_page) >> PAGE_SHIFT); 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci page_ref_dec(page); 139462306a36Sopenharmony_ci} 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ciint kvm_s390_handle_vsie(struct kvm_vcpu *vcpu) 139762306a36Sopenharmony_ci{ 139862306a36Sopenharmony_ci struct vsie_page *vsie_page; 139962306a36Sopenharmony_ci unsigned long scb_addr; 140062306a36Sopenharmony_ci int rc; 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci vcpu->stat.instruction_sie++; 140362306a36Sopenharmony_ci if (!test_kvm_cpu_feat(vcpu->kvm, KVM_S390_VM_CPU_FEAT_SIEF2)) 140462306a36Sopenharmony_ci return -EOPNOTSUPP; 140562306a36Sopenharmony_ci if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) 140662306a36Sopenharmony_ci return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct vsie_page) != PAGE_SIZE); 140962306a36Sopenharmony_ci scb_addr = kvm_s390_get_base_disp_s(vcpu, NULL); 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci /* 512 byte alignment */ 141262306a36Sopenharmony_ci if (unlikely(scb_addr & 0x1ffUL)) 141362306a36Sopenharmony_ci return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci if (signal_pending(current) || kvm_s390_vcpu_has_irq(vcpu, 0) || 141662306a36Sopenharmony_ci kvm_s390_vcpu_sie_inhibited(vcpu)) 141762306a36Sopenharmony_ci return 0; 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci vsie_page = get_vsie_page(vcpu->kvm, scb_addr); 142062306a36Sopenharmony_ci if (IS_ERR(vsie_page)) 142162306a36Sopenharmony_ci return PTR_ERR(vsie_page); 142262306a36Sopenharmony_ci else if (!vsie_page) 142362306a36Sopenharmony_ci /* double use of sie control block - simply do nothing */ 142462306a36Sopenharmony_ci return 0; 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci rc = pin_scb(vcpu, vsie_page, scb_addr); 142762306a36Sopenharmony_ci if (rc) 142862306a36Sopenharmony_ci goto out_put; 142962306a36Sopenharmony_ci rc = shadow_scb(vcpu, vsie_page); 143062306a36Sopenharmony_ci if (rc) 143162306a36Sopenharmony_ci goto out_unpin_scb; 143262306a36Sopenharmony_ci rc = pin_blocks(vcpu, vsie_page); 143362306a36Sopenharmony_ci if (rc) 143462306a36Sopenharmony_ci goto out_unshadow; 143562306a36Sopenharmony_ci register_shadow_scb(vcpu, vsie_page); 143662306a36Sopenharmony_ci rc = vsie_run(vcpu, vsie_page); 143762306a36Sopenharmony_ci unregister_shadow_scb(vcpu); 143862306a36Sopenharmony_ci unpin_blocks(vcpu, vsie_page); 143962306a36Sopenharmony_ciout_unshadow: 144062306a36Sopenharmony_ci unshadow_scb(vcpu, vsie_page); 144162306a36Sopenharmony_ciout_unpin_scb: 144262306a36Sopenharmony_ci unpin_scb(vcpu, vsie_page, scb_addr); 144362306a36Sopenharmony_ciout_put: 144462306a36Sopenharmony_ci put_vsie_page(vcpu->kvm, vsie_page); 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci return rc < 0 ? rc : 0; 144762306a36Sopenharmony_ci} 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci/* Init the vsie data structures. To be called when a vm is initialized. */ 145062306a36Sopenharmony_civoid kvm_s390_vsie_init(struct kvm *kvm) 145162306a36Sopenharmony_ci{ 145262306a36Sopenharmony_ci mutex_init(&kvm->arch.vsie.mutex); 145362306a36Sopenharmony_ci INIT_RADIX_TREE(&kvm->arch.vsie.addr_to_page, GFP_KERNEL_ACCOUNT); 145462306a36Sopenharmony_ci} 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci/* Destroy the vsie data structures. To be called when a vm is destroyed. */ 145762306a36Sopenharmony_civoid kvm_s390_vsie_destroy(struct kvm *kvm) 145862306a36Sopenharmony_ci{ 145962306a36Sopenharmony_ci struct vsie_page *vsie_page; 146062306a36Sopenharmony_ci struct page *page; 146162306a36Sopenharmony_ci int i; 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci mutex_lock(&kvm->arch.vsie.mutex); 146462306a36Sopenharmony_ci for (i = 0; i < kvm->arch.vsie.page_count; i++) { 146562306a36Sopenharmony_ci page = kvm->arch.vsie.pages[i]; 146662306a36Sopenharmony_ci kvm->arch.vsie.pages[i] = NULL; 146762306a36Sopenharmony_ci vsie_page = page_to_virt(page); 146862306a36Sopenharmony_ci release_gmap_shadow(vsie_page); 146962306a36Sopenharmony_ci /* free the radix tree entry */ 147062306a36Sopenharmony_ci radix_tree_delete(&kvm->arch.vsie.addr_to_page, page->index >> 9); 147162306a36Sopenharmony_ci __free_page(page); 147262306a36Sopenharmony_ci } 147362306a36Sopenharmony_ci kvm->arch.vsie.page_count = 0; 147462306a36Sopenharmony_ci mutex_unlock(&kvm->arch.vsie.mutex); 147562306a36Sopenharmony_ci} 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_civoid kvm_s390_vsie_kick(struct kvm_vcpu *vcpu) 147862306a36Sopenharmony_ci{ 147962306a36Sopenharmony_ci struct kvm_s390_sie_block *scb = READ_ONCE(vcpu->arch.vsie_block); 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci /* 148262306a36Sopenharmony_ci * Even if the VCPU lets go of the shadow sie block reference, it is 148362306a36Sopenharmony_ci * still valid in the cache. So we can safely kick it. 148462306a36Sopenharmony_ci */ 148562306a36Sopenharmony_ci if (scb) { 148662306a36Sopenharmony_ci atomic_or(PROG_BLOCK_SIE, &scb->prog20); 148762306a36Sopenharmony_ci if (scb->prog0c & PROG_IN_SIE) 148862306a36Sopenharmony_ci atomic_or(CPUSTAT_STOP_INT, &scb->cpuflags); 148962306a36Sopenharmony_ci } 149062306a36Sopenharmony_ci} 1491