|
@@ -473,10 +473,45 @@ static void filter_guest_per_event(struct kvm_vcpu *vcpu)
|
|
|
vcpu->arch.sie_block->iprcc &= ~PGM_PER;
|
|
|
}
|
|
|
|
|
|
+#define pssec(vcpu) (vcpu->arch.sie_block->gcr[1] & _ASCE_SPACE_SWITCH)
|
|
|
+#define hssec(vcpu) (vcpu->arch.sie_block->gcr[13] & _ASCE_SPACE_SWITCH)
|
|
|
+#define old_ssec(vcpu) ((vcpu->arch.sie_block->tecmc >> 31) & 0x1)
|
|
|
+#define old_as_is_home(vcpu) !(vcpu->arch.sie_block->tecmc & 0xffff)
|
|
|
+
|
|
|
void kvm_s390_handle_per_event(struct kvm_vcpu *vcpu)
|
|
|
{
|
|
|
+ int new_as;
|
|
|
+
|
|
|
if (debug_exit_required(vcpu))
|
|
|
vcpu->guest_debug |= KVM_GUESTDBG_EXIT_PENDING;
|
|
|
|
|
|
filter_guest_per_event(vcpu);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Only RP, SAC, SACF, PT, PTI, PR, PC instructions can trigger
|
|
|
+ * a space-switch event. PER events enforce space-switch events
|
|
|
+ * for these instructions. So if no PER event for the guest is left,
|
|
|
+ * we might have to filter the space-switch element out, too.
|
|
|
+ */
|
|
|
+ if (vcpu->arch.sie_block->iprcc == PGM_SPACE_SWITCH) {
|
|
|
+ vcpu->arch.sie_block->iprcc = 0;
|
|
|
+ new_as = psw_bits(vcpu->arch.sie_block->gpsw).as;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If the AS changed from / to home, we had RP, SAC or SACF
|
|
|
+ * instruction. Check primary and home space-switch-event
|
|
|
+ * controls. (theoretically home -> home produced no event)
|
|
|
+ */
|
|
|
+ if (((new_as == PSW_AS_HOME) ^ old_as_is_home(vcpu)) &&
|
|
|
+ (pssec(vcpu) || hssec(vcpu)))
|
|
|
+ vcpu->arch.sie_block->iprcc = PGM_SPACE_SWITCH;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * PT, PTI, PR, PC instruction operate on primary AS only. Check
|
|
|
+ * if the primary-space-switch-event control was or got set.
|
|
|
+ */
|
|
|
+ if (new_as == PSW_AS_PRIMARY && !old_as_is_home(vcpu) &&
|
|
|
+ (pssec(vcpu) || old_ssec(vcpu)))
|
|
|
+ vcpu->arch.sie_block->iprcc = PGM_SPACE_SWITCH;
|
|
|
+ }
|
|
|
}
|