|
@@ -41,6 +41,11 @@ static int psw_ioint_disabled(struct kvm_vcpu *vcpu)
|
|
|
return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_IO);
|
|
|
}
|
|
|
|
|
|
+static int psw_mchk_disabled(struct kvm_vcpu *vcpu)
|
|
|
+{
|
|
|
+ return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_MCHECK);
|
|
|
+}
|
|
|
+
|
|
|
static int psw_interrupts_disabled(struct kvm_vcpu *vcpu)
|
|
|
{
|
|
|
if ((vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PER) ||
|
|
@@ -82,6 +87,12 @@ static int __interrupt_is_deliverable(struct kvm_vcpu *vcpu,
|
|
|
case KVM_S390_SIGP_SET_PREFIX:
|
|
|
case KVM_S390_RESTART:
|
|
|
return 1;
|
|
|
+ case KVM_S390_MCHK:
|
|
|
+ if (psw_mchk_disabled(vcpu))
|
|
|
+ return 0;
|
|
|
+ if (vcpu->arch.sie_block->gcr[14] & inti->mchk.cr14)
|
|
|
+ return 1;
|
|
|
+ return 0;
|
|
|
case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
|
|
|
if (psw_ioint_disabled(vcpu))
|
|
|
return 0;
|
|
@@ -116,6 +127,7 @@ static void __reset_intercept_indicators(struct kvm_vcpu *vcpu)
|
|
|
CPUSTAT_IO_INT | CPUSTAT_EXT_INT | CPUSTAT_STOP_INT,
|
|
|
&vcpu->arch.sie_block->cpuflags);
|
|
|
vcpu->arch.sie_block->lctl = 0x0000;
|
|
|
+ vcpu->arch.sie_block->ictl &= ~ICTL_LPSW;
|
|
|
}
|
|
|
|
|
|
static void __set_cpuflag(struct kvm_vcpu *vcpu, u32 flag)
|
|
@@ -139,6 +151,12 @@ static void __set_intercept_indicator(struct kvm_vcpu *vcpu,
|
|
|
case KVM_S390_SIGP_STOP:
|
|
|
__set_cpuflag(vcpu, CPUSTAT_STOP_INT);
|
|
|
break;
|
|
|
+ case KVM_S390_MCHK:
|
|
|
+ if (psw_mchk_disabled(vcpu))
|
|
|
+ vcpu->arch.sie_block->ictl |= ICTL_LPSW;
|
|
|
+ else
|
|
|
+ vcpu->arch.sie_block->lctl |= LCTL_CR14;
|
|
|
+ break;
|
|
|
case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
|
|
|
if (psw_ioint_disabled(vcpu))
|
|
|
__set_cpuflag(vcpu, CPUSTAT_IO_INT);
|
|
@@ -326,6 +344,32 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,
|
|
|
exception = 1;
|
|
|
break;
|
|
|
|
|
|
+ case KVM_S390_MCHK:
|
|
|
+ VCPU_EVENT(vcpu, 4, "interrupt: machine check mcic=%llx",
|
|
|
+ inti->mchk.mcic);
|
|
|
+ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type,
|
|
|
+ inti->mchk.cr14,
|
|
|
+ inti->mchk.mcic);
|
|
|
+ rc = kvm_s390_vcpu_store_status(vcpu,
|
|
|
+ KVM_S390_STORE_STATUS_PREFIXED);
|
|
|
+ if (rc == -EFAULT)
|
|
|
+ exception = 1;
|
|
|
+
|
|
|
+ rc = put_guest_u64(vcpu, __LC_MCCK_CODE, inti->mchk.mcic);
|
|
|
+ if (rc == -EFAULT)
|
|
|
+ exception = 1;
|
|
|
+
|
|
|
+ rc = copy_to_guest(vcpu, __LC_MCK_OLD_PSW,
|
|
|
+ &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
|
|
|
+ if (rc == -EFAULT)
|
|
|
+ exception = 1;
|
|
|
+
|
|
|
+ rc = copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw,
|
|
|
+ __LC_MCK_NEW_PSW, sizeof(psw_t));
|
|
|
+ if (rc == -EFAULT)
|
|
|
+ exception = 1;
|
|
|
+ break;
|
|
|
+
|
|
|
case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
|
|
|
{
|
|
|
__u32 param0 = ((__u32)inti->io.subchannel_id << 16) |
|
|
@@ -588,6 +632,61 @@ void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+void kvm_s390_deliver_pending_machine_checks(struct kvm_vcpu *vcpu)
|
|
|
+{
|
|
|
+ struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
|
|
|
+ struct kvm_s390_float_interrupt *fi = vcpu->arch.local_int.float_int;
|
|
|
+ struct kvm_s390_interrupt_info *n, *inti = NULL;
|
|
|
+ int deliver;
|
|
|
+
|
|
|
+ __reset_intercept_indicators(vcpu);
|
|
|
+ if (atomic_read(&li->active)) {
|
|
|
+ do {
|
|
|
+ deliver = 0;
|
|
|
+ spin_lock_bh(&li->lock);
|
|
|
+ list_for_each_entry_safe(inti, n, &li->list, list) {
|
|
|
+ if ((inti->type == KVM_S390_MCHK) &&
|
|
|
+ __interrupt_is_deliverable(vcpu, inti)) {
|
|
|
+ list_del(&inti->list);
|
|
|
+ deliver = 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ __set_intercept_indicator(vcpu, inti);
|
|
|
+ }
|
|
|
+ if (list_empty(&li->list))
|
|
|
+ atomic_set(&li->active, 0);
|
|
|
+ spin_unlock_bh(&li->lock);
|
|
|
+ if (deliver) {
|
|
|
+ __do_deliver_interrupt(vcpu, inti);
|
|
|
+ kfree(inti);
|
|
|
+ }
|
|
|
+ } while (deliver);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (atomic_read(&fi->active)) {
|
|
|
+ do {
|
|
|
+ deliver = 0;
|
|
|
+ spin_lock(&fi->lock);
|
|
|
+ list_for_each_entry_safe(inti, n, &fi->list, list) {
|
|
|
+ if ((inti->type == KVM_S390_MCHK) &&
|
|
|
+ __interrupt_is_deliverable(vcpu, inti)) {
|
|
|
+ list_del(&inti->list);
|
|
|
+ deliver = 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ __set_intercept_indicator(vcpu, inti);
|
|
|
+ }
|
|
|
+ if (list_empty(&fi->list))
|
|
|
+ atomic_set(&fi->active, 0);
|
|
|
+ spin_unlock(&fi->lock);
|
|
|
+ if (deliver) {
|
|
|
+ __do_deliver_interrupt(vcpu, inti);
|
|
|
+ kfree(inti);
|
|
|
+ }
|
|
|
+ } while (deliver);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code)
|
|
|
{
|
|
|
struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
|
|
@@ -641,6 +740,13 @@ int kvm_s390_inject_vm(struct kvm *kvm,
|
|
|
case KVM_S390_INT_EMERGENCY:
|
|
|
kfree(inti);
|
|
|
return -EINVAL;
|
|
|
+ case KVM_S390_MCHK:
|
|
|
+ VM_EVENT(kvm, 5, "inject: machine check parm64:%llx",
|
|
|
+ s390int->parm64);
|
|
|
+ inti->type = s390int->type;
|
|
|
+ inti->mchk.cr14 = s390int->parm; /* upper bits are not used */
|
|
|
+ inti->mchk.mcic = s390int->parm64;
|
|
|
+ break;
|
|
|
case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
|
|
|
if (s390int->type & IOINT_AI_MASK)
|
|
|
VM_EVENT(kvm, 5, "%s", "inject: I/O (AI)");
|
|
@@ -749,6 +855,12 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
|
|
|
inti->type = s390int->type;
|
|
|
inti->emerg.code = s390int->parm;
|
|
|
break;
|
|
|
+ case KVM_S390_MCHK:
|
|
|
+ VCPU_EVENT(vcpu, 5, "inject: machine check parm64:%llx",
|
|
|
+ s390int->parm64);
|
|
|
+ inti->type = s390int->type;
|
|
|
+ inti->mchk.mcic = s390int->parm64;
|
|
|
+ break;
|
|
|
case KVM_S390_INT_VIRTIO:
|
|
|
case KVM_S390_INT_SERVICE:
|
|
|
case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
|