@@ -33,6 +33,7 @@ u8 kvm_s390_get_ilen(struct kvm_vcpu *vcpu)
case ICPT_OPEREXC:
case ICPT_PARTEXEC:
case ICPT_IOINST:
+ case ICPT_KSS:
/* instruction only stored for these icptcodes */
ilen = insn_length(vcpu->arch.sie_block->ipa >> 8);
/* Use the length of the EXECUTE instruction if necessary */
@@ -565,7 +566,44 @@ int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu)
rc = handle_partial_execution(vcpu);
break;
case ICPT_KSS:
- rc = kvm_s390_skey_check_enable(vcpu);
+ if (likely(!test_kvm_facility(vcpu->kvm, 169))) {
+ rc = kvm_s390_skey_check_enable(vcpu);
+ } else {
+ /*
+ * Storage key removal facility emulation.
+ *
+ * KSS is the same priority as instruction
+ * interception. Hence we need handling here
+ * and in the instruction emulation code.
+ *
+ * lpsw(e) needs to store the problematic psw
+ * as the program old psw. Calling the
+ * handlers directly does that without falsely
+ * increasing stat counters.
+ */
+ switch (vcpu->arch.sie_block->ipa) {
+ case 0xb2b2:
+ kvm_s390_forward_psw(vcpu, kvm_s390_get_ilen(vcpu));
+ rc = kvm_s390_handle_b2(vcpu);
+ break;
+ case 0x8200:
+ kvm_s390_forward_psw(vcpu, kvm_s390_get_ilen(vcpu));
+ rc = kvm_s390_handle_lpsw(vcpu);
+ break;
+ case 0:
+ /* Interception caused by exception new PSW key */
+ rc = kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+ break;
+ default:
+ /*
+ * KSS is nullifying (no psw forward),
+ * SKRF issues suppressing SPECIAL
+ * OPS, so we need to forward by hand.
+ */
+ kvm_s390_forward_psw(vcpu, kvm_s390_get_ilen(vcpu));
+ rc = kvm_s390_inject_program_int(vcpu, PGM_SPECIAL_OPERATION);
+ }
+ }
break;
case ICPT_MCHKREQ:
case ICPT_INT_ENABLE:
@@ -2692,6 +2692,11 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
/* we emulate STHYI in kvm */
set_kvm_facility(kvm->arch.model.fac_mask, 74);
set_kvm_facility(kvm->arch.model.fac_list, 74);
+ /* we emulate the storage key removal facility only with kss */
+ if (sclp.has_kss) {
+ set_kvm_facility(kvm->arch.model.fac_mask, 169);
+ set_kvm_facility(kvm->arch.model.fac_list, 169);
+ }
if (MACHINE_HAS_TLB_GUEST) {
set_kvm_facility(kvm->arch.model.fac_mask, 147);
set_kvm_facility(kvm->arch.model.fac_list, 147);
@@ -207,6 +207,13 @@ int kvm_s390_skey_check_enable(struct kvm_vcpu *vcpu)
int rc;
trace_kvm_s390_skey_related_inst(vcpu);
+
+ if (test_kvm_facility(vcpu->kvm, 169)) {
+ rc = kvm_s390_inject_program_int(vcpu, PGM_SPECIAL_OPERATION);
+ if (!rc)
+ return -EOPNOTSUPP;
+ }
+
/* Already enabled? */
if (vcpu->arch.skey_enabled)
return 0;
@@ -257,7 +264,7 @@ static int handle_iske(struct kvm_vcpu *vcpu)
rc = try_handle_skey(vcpu);
if (rc)
- return rc != -EAGAIN ? rc : 0;
+ return rc != (-EAGAIN || -EOPNOTSUPP) ? rc : 0;
kvm_s390_get_regs_rre(vcpu, ®1, ®2);
@@ -304,7 +311,7 @@ static int handle_rrbe(struct kvm_vcpu *vcpu)
rc = try_handle_skey(vcpu);
if (rc)
- return rc != -EAGAIN ? rc : 0;
+ return rc != (-EAGAIN || -EOPNOTSUPP) ? rc : 0;
kvm_s390_get_regs_rre(vcpu, ®1, ®2);
@@ -355,7 +362,7 @@ static int handle_sske(struct kvm_vcpu *vcpu)
rc = try_handle_skey(vcpu);
if (rc)
- return rc != -EAGAIN ? rc : 0;
+ return rc != (-EAGAIN || -EOPNOTSUPP) ? rc : 0;
if (!test_kvm_facility(vcpu->kvm, 8))
m3 &= ~SSKE_MB;
@@ -745,6 +752,8 @@ int kvm_s390_handle_lpsw(struct kvm_vcpu *vcpu)
return kvm_s390_inject_prog_cond(vcpu, rc);
if (!(new_psw.mask & PSW32_MASK_BASE))
return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+ if (new_psw.mask & PSW32_MASK_KEY && test_kvm_facility(vcpu->kvm, 169))
+ return kvm_s390_inject_program_int(vcpu, PGM_SPECIAL_OPERATION);
gpsw->mask = (new_psw.mask & ~PSW32_MASK_BASE) << 32;
gpsw->mask |= new_psw.addr & PSW32_ADDR_AMODE;
gpsw->addr = new_psw.addr & ~PSW32_ADDR_AMODE;
@@ -771,6 +780,8 @@ static int handle_lpswe(struct kvm_vcpu *vcpu)
rc = read_guest(vcpu, addr, ar, &new_psw, sizeof(new_psw));
if (rc)
return kvm_s390_inject_prog_cond(vcpu, rc);
+ if ((new_psw.mask & PSW_MASK_KEY) && test_kvm_facility(vcpu->kvm, 169))
+ return kvm_s390_inject_program_int(vcpu, PGM_SPECIAL_OPERATION);
vcpu->arch.sie_block->gpsw = new_psw;
if (!is_valid_psw(&vcpu->arch.sie_block->gpsw))
return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
@@ -1025,6 +1036,10 @@ static int handle_pfmf(struct kvm_vcpu *vcpu)
if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
+ if (vcpu->run->s.regs.gprs[reg1] & PFMF_SK &&
+ test_kvm_facility(vcpu->kvm, 169))
+ return kvm_s390_inject_program_int(vcpu, PGM_SPECIAL_OPERATION);
+
if (vcpu->run->s.regs.gprs[reg1] & PFMF_RESERVED)
return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
@@ -1203,6 +1218,8 @@ static int handle_essa(struct kvm_vcpu *vcpu)
return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
/* Check for invalid operation request code */
orc = (vcpu->arch.sie_block->ipb & 0xf0000000) >> 28;
+ if (orc == ESSA_SET_POT_VOLATILE && test_kvm_facility(vcpu->kvm, 169))
+ return kvm_s390_inject_program_int(vcpu, PGM_SPECIAL_OPERATION);
/* ORCs 0-6 are always valid */
if (orc > (test_kvm_facility(vcpu->kvm, 147) ? ESSA_SET_STABLE_NODAT
: ESSA_SET_STABLE_IF_RESIDENT))
@@ -1451,6 +1468,9 @@ static int handle_tprot(struct kvm_vcpu *vcpu)
kvm_s390_get_base_disp_sse(vcpu, &address1, &address2, &ar, NULL);
+ if ((address2 & 0xf0) && test_kvm_facility(vcpu->kvm, 169))
+ return kvm_s390_inject_program_int(vcpu, PGM_SPECIAL_OPERATION);
+
/* we only handle the Linux memory detection case:
* access key == 0
* everything else goes to userspace. */
The storage key removal facility makes skey related instructions result in special operation program exceptions. It is based on the Keyless Subset Facility. The usual suspects are iske, sske, rrbe and their respective variants. lpsw(e), pfmf and tprot can also specify a key and essa with an ORC of 4 will consult the change bit, hence they all result in exceptions. Unfortunately storage keys were so essential to the architecture, that there is no facility bit that we could deactivate. That's why the removal facility (bit 169) was introduced which makes it necessary, that, if active, the skey related facilities 10, 14, 66, 145 and 149 are zero. Managing this requirement and migratability has to be done in userspace, as KVM does not check the facilities it receives to be able to easily implement userspace emulation. Removing storage key support allows us to circumvent complicated emulation code and makes huge page support tremendously easier. Signed-off-by: Janosch Frank <frankja@linux.ibm.com> --- arch/s390/kvm/intercept.c | 40 ++++++++++++++++++++++++++++++++++++++- arch/s390/kvm/kvm-s390.c | 5 +++++ arch/s390/kvm/priv.c | 26 ++++++++++++++++++++++--- 3 files changed, 67 insertions(+), 4 deletions(-)