diff options
Diffstat (limited to 'arch/s390/kvm/interrupt.c')
| -rw-r--r-- | arch/s390/kvm/interrupt.c | 419 | 
1 files changed, 290 insertions, 129 deletions
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 82c481ddef7..5c948177529 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -21,11 +21,31 @@  #include "gaccess.h"  #include "trace-s390.h" +#define IOINT_SCHID_MASK 0x0000ffff +#define IOINT_SSID_MASK 0x00030000 +#define IOINT_CSSID_MASK 0x03fc0000 +#define IOINT_AI_MASK 0x04000000 + +static int is_ioint(u64 type) +{ +	return ((type & 0xfffe0000u) != 0xfffe0000u); +} +  static int psw_extint_disabled(struct kvm_vcpu *vcpu)  {  	return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_EXT);  } +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) || @@ -35,6 +55,13 @@ static int psw_interrupts_disabled(struct kvm_vcpu *vcpu)  	return 1;  } +static u64 int_word_to_isc_bits(u32 int_word) +{ +	u8 isc = (int_word & 0x38000000) >> 27; + +	return (0x80 >> isc) << 24; +} +  static int __interrupt_is_deliverable(struct kvm_vcpu *vcpu,  				      struct kvm_s390_interrupt_info *inti)  { @@ -67,7 +94,22 @@ 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; +		if (vcpu->arch.sie_block->gcr[6] & +		    int_word_to_isc_bits(inti->io.io_int_word)) +			return 1; +		return 0;  	default: +		printk(KERN_WARNING "illegal interrupt type %llx\n", +		       inti->type);  		BUG();  	}  	return 0; @@ -93,6 +135,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) @@ -116,6 +159,18 @@ 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); +		else +			vcpu->arch.sie_block->lctl |= LCTL_CR6; +		break;  	default:  		BUG();  	} @@ -125,7 +180,7 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,  				   struct kvm_s390_interrupt_info *inti)  {  	const unsigned short table[] = { 2, 4, 4, 6 }; -	int rc, exception = 0; +	int rc = 0;  	switch (inti->type) {  	case KVM_S390_INT_EMERGENCY: @@ -133,74 +188,41 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,  		vcpu->stat.deliver_emergency_signal++;  		trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type,  						 inti->emerg.code, 0); -		rc = put_guest_u16(vcpu, __LC_EXT_INT_CODE, 0x1201); -		if (rc == -EFAULT) -			exception = 1; - -		rc = put_guest_u16(vcpu, __LC_EXT_CPU_ADDR, inti->emerg.code); -		if (rc == -EFAULT) -			exception = 1; - -		rc = copy_to_guest(vcpu, __LC_EXT_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_EXT_NEW_PSW, sizeof(psw_t)); -		if (rc == -EFAULT) -			exception = 1; +		rc  = put_guest(vcpu, 0x1201, (u16 __user *)__LC_EXT_INT_CODE); +		rc |= put_guest(vcpu, inti->emerg.code, +				(u16 __user *)__LC_EXT_CPU_ADDR); +		rc |= copy_to_guest(vcpu, __LC_EXT_OLD_PSW, +				    &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); +		rc |= copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw, +				      __LC_EXT_NEW_PSW, sizeof(psw_t));  		break; -  	case KVM_S390_INT_EXTERNAL_CALL:  		VCPU_EVENT(vcpu, 4, "%s", "interrupt: sigp ext call");  		vcpu->stat.deliver_external_call++;  		trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type,  						 inti->extcall.code, 0); -		rc = put_guest_u16(vcpu, __LC_EXT_INT_CODE, 0x1202); -		if (rc == -EFAULT) -			exception = 1; - -		rc = put_guest_u16(vcpu, __LC_EXT_CPU_ADDR, inti->extcall.code); -		if (rc == -EFAULT) -			exception = 1; - -		rc = copy_to_guest(vcpu, __LC_EXT_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_EXT_NEW_PSW, sizeof(psw_t)); -		if (rc == -EFAULT) -			exception = 1; +		rc  = put_guest(vcpu, 0x1202, (u16 __user *)__LC_EXT_INT_CODE); +		rc |= put_guest(vcpu, inti->extcall.code, +				(u16 __user *)__LC_EXT_CPU_ADDR); +		rc |= copy_to_guest(vcpu, __LC_EXT_OLD_PSW, +				    &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); +		rc |= copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw, +				      __LC_EXT_NEW_PSW, sizeof(psw_t));  		break; -  	case KVM_S390_INT_SERVICE:  		VCPU_EVENT(vcpu, 4, "interrupt: sclp parm:%x",  			   inti->ext.ext_params);  		vcpu->stat.deliver_service_signal++;  		trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type,  						 inti->ext.ext_params, 0); -		rc = put_guest_u16(vcpu, __LC_EXT_INT_CODE, 0x2401); -		if (rc == -EFAULT) -			exception = 1; - -		rc = copy_to_guest(vcpu, __LC_EXT_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_EXT_NEW_PSW, sizeof(psw_t)); -		if (rc == -EFAULT) -			exception = 1; - -		rc = put_guest_u32(vcpu, __LC_EXT_PARAMS, inti->ext.ext_params); -		if (rc == -EFAULT) -			exception = 1; +		rc  = put_guest(vcpu, 0x2401, (u16 __user *)__LC_EXT_INT_CODE); +		rc |= copy_to_guest(vcpu, __LC_EXT_OLD_PSW, +				    &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); +		rc |= copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw, +				      __LC_EXT_NEW_PSW, sizeof(psw_t)); +		rc |= put_guest(vcpu, inti->ext.ext_params, +				(u32 __user *)__LC_EXT_PARAMS);  		break; -  	case KVM_S390_INT_VIRTIO:  		VCPU_EVENT(vcpu, 4, "interrupt: virtio parm:%x,parm64:%llx",  			   inti->ext.ext_params, inti->ext.ext_params2); @@ -208,34 +230,17 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,  		trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type,  						 inti->ext.ext_params,  						 inti->ext.ext_params2); -		rc = put_guest_u16(vcpu, __LC_EXT_INT_CODE, 0x2603); -		if (rc == -EFAULT) -			exception = 1; - -		rc = put_guest_u16(vcpu, __LC_EXT_CPU_ADDR, 0x0d00); -		if (rc == -EFAULT) -			exception = 1; - -		rc = copy_to_guest(vcpu, __LC_EXT_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_EXT_NEW_PSW, sizeof(psw_t)); -		if (rc == -EFAULT) -			exception = 1; - -		rc = put_guest_u32(vcpu, __LC_EXT_PARAMS, inti->ext.ext_params); -		if (rc == -EFAULT) -			exception = 1; - -		rc = put_guest_u64(vcpu, __LC_EXT_PARAMS2, -				   inti->ext.ext_params2); -		if (rc == -EFAULT) -			exception = 1; +		rc  = put_guest(vcpu, 0x2603, (u16 __user *)__LC_EXT_INT_CODE); +		rc |= put_guest(vcpu, 0x0d00, (u16 __user *)__LC_EXT_CPU_ADDR); +		rc |= copy_to_guest(vcpu, __LC_EXT_OLD_PSW, +				    &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); +		rc |= copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw, +				      __LC_EXT_NEW_PSW, sizeof(psw_t)); +		rc |= put_guest(vcpu, inti->ext.ext_params, +				(u32 __user *)__LC_EXT_PARAMS); +		rc |= put_guest(vcpu, inti->ext.ext_params2, +				(u64 __user *)__LC_EXT_PARAMS2);  		break; -  	case KVM_S390_SIGP_STOP:  		VCPU_EVENT(vcpu, 4, "%s", "interrupt: cpu stop");  		vcpu->stat.deliver_stop_signal++; @@ -258,18 +263,14 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,  		vcpu->stat.deliver_restart_signal++;  		trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type,  						 0, 0); -		rc = copy_to_guest(vcpu, offsetof(struct _lowcore, -		  restart_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, -			offsetof(struct _lowcore, restart_psw), sizeof(psw_t)); -		if (rc == -EFAULT) -			exception = 1; +		rc  = copy_to_guest(vcpu, +				    offsetof(struct _lowcore, restart_old_psw), +				    &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); +		rc |= copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw, +				      offsetof(struct _lowcore, restart_psw), +				      sizeof(psw_t));  		atomic_clear_mask(CPUSTAT_STOPPED, &vcpu->arch.sie_block->cpuflags);  		break; -  	case KVM_S390_PROGRAM_INT:  		VCPU_EVENT(vcpu, 4, "interrupt: pgm check code:%x, ilc:%x",  			   inti->pgm.code, @@ -277,56 +278,78 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,  		vcpu->stat.deliver_program_int++;  		trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type,  						 inti->pgm.code, 0); -		rc = put_guest_u16(vcpu, __LC_PGM_INT_CODE, inti->pgm.code); -		if (rc == -EFAULT) -			exception = 1; - -		rc = put_guest_u16(vcpu, __LC_PGM_ILC, -			table[vcpu->arch.sie_block->ipa >> 14]); -		if (rc == -EFAULT) -			exception = 1; - -		rc = copy_to_guest(vcpu, __LC_PGM_OLD_PSW, -			 &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); -		if (rc == -EFAULT) -			exception = 1; +		rc  = put_guest(vcpu, inti->pgm.code, (u16 __user *)__LC_PGM_INT_CODE); +		rc |= put_guest(vcpu, table[vcpu->arch.sie_block->ipa >> 14], +				(u16 __user *)__LC_PGM_ILC); +		rc |= copy_to_guest(vcpu, __LC_PGM_OLD_PSW, +				    &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); +		rc |= copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw, +				      __LC_PGM_NEW_PSW, sizeof(psw_t)); +		break; -		rc = copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw, -			__LC_PGM_NEW_PSW, sizeof(psw_t)); -		if (rc == -EFAULT) -			exception = 1; +	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); +		rc |= put_guest(vcpu, inti->mchk.mcic, (u64 __user *) __LC_MCCK_CODE); +		rc |= copy_to_guest(vcpu, __LC_MCK_OLD_PSW, +				    &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); +		rc |= copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw, +				      __LC_MCK_NEW_PSW, sizeof(psw_t));  		break; +	case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: +	{ +		__u32 param0 = ((__u32)inti->io.subchannel_id << 16) | +			inti->io.subchannel_nr; +		__u64 param1 = ((__u64)inti->io.io_int_parm << 32) | +			inti->io.io_int_word; +		VCPU_EVENT(vcpu, 4, "interrupt: I/O %llx", inti->type); +		vcpu->stat.deliver_io_int++; +		trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, +						 param0, param1); +		rc  = put_guest(vcpu, inti->io.subchannel_id, +				(u16 __user *) __LC_SUBCHANNEL_ID); +		rc |= put_guest(vcpu, inti->io.subchannel_nr, +				(u16 __user *) __LC_SUBCHANNEL_NR); +		rc |= put_guest(vcpu, inti->io.io_int_parm, +				(u32 __user *) __LC_IO_INT_PARM); +		rc |= put_guest(vcpu, inti->io.io_int_word, +				(u32 __user *) __LC_IO_INT_WORD); +		rc |= copy_to_guest(vcpu, __LC_IO_OLD_PSW, +				    &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); +		rc |= copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw, +				      __LC_IO_NEW_PSW, sizeof(psw_t)); +		break; +	}  	default:  		BUG();  	} -	if (exception) { +	if (rc) {  		printk("kvm: The guest lowcore is not mapped during interrupt " -			"delivery, killing userspace\n"); +		       "delivery, killing userspace\n");  		do_exit(SIGKILL);  	}  }  static int __try_deliver_ckc_interrupt(struct kvm_vcpu *vcpu)  { -	int rc, exception = 0; +	int rc;  	if (psw_extint_disabled(vcpu))  		return 0;  	if (!(vcpu->arch.sie_block->gcr[0] & 0x800ul))  		return 0; -	rc = put_guest_u16(vcpu, __LC_EXT_INT_CODE, 0x1004); -	if (rc == -EFAULT) -		exception = 1; -	rc = copy_to_guest(vcpu, __LC_EXT_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_EXT_NEW_PSW, sizeof(psw_t)); -	if (rc == -EFAULT) -		exception = 1; -	if (exception) { +	rc  = put_guest(vcpu, 0x1004, (u16 __user *)__LC_EXT_INT_CODE); +	rc |= copy_to_guest(vcpu, __LC_EXT_OLD_PSW, +			    &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); +	rc |= copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw, +			      __LC_EXT_NEW_PSW, sizeof(psw_t)); +	if (rc) {  		printk("kvm: The guest lowcore is not mapped during interrupt "  			"delivery, killing userspace\n");  		do_exit(SIGKILL); @@ -362,7 +385,7 @@ static int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu)  	}  	if ((!rc) && (vcpu->arch.sie_block->ckc < -		get_clock() + vcpu->arch.sie_block->epoch)) { +		get_tod_clock() + vcpu->arch.sie_block->epoch)) {  		if ((!psw_extint_disabled(vcpu)) &&  			(vcpu->arch.sie_block->gcr[0] & 0x800ul))  			rc = 1; @@ -402,7 +425,7 @@ int kvm_s390_handle_wait(struct kvm_vcpu *vcpu)  		goto no_timer;  	} -	now = get_clock() + vcpu->arch.sie_block->epoch; +	now = get_tod_clock() + vcpu->arch.sie_block->epoch;  	if (vcpu->arch.sie_block->ckc < now) {  		__unset_cpu_idle(vcpu);  		return 0; @@ -492,7 +515,7 @@ void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu)  	}  	if ((vcpu->arch.sie_block->ckc < -		get_clock() + vcpu->arch.sie_block->epoch)) +		get_tod_clock() + vcpu->arch.sie_block->epoch))  		__try_deliver_ckc_interrupt(vcpu);  	if (atomic_read(&fi->active)) { @@ -518,6 +541,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; @@ -540,12 +618,50 @@ int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code)  	return 0;  } +struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm, +						    u64 cr6, u64 schid) +{ +	struct kvm_s390_float_interrupt *fi; +	struct kvm_s390_interrupt_info *inti, *iter; + +	if ((!schid && !cr6) || (schid && cr6)) +		return NULL; +	mutex_lock(&kvm->lock); +	fi = &kvm->arch.float_int; +	spin_lock(&fi->lock); +	inti = NULL; +	list_for_each_entry(iter, &fi->list, list) { +		if (!is_ioint(iter->type)) +			continue; +		if (cr6 && +		    ((cr6 & int_word_to_isc_bits(iter->io.io_int_word)) == 0)) +			continue; +		if (schid) { +			if (((schid & 0x00000000ffff0000) >> 16) != +			    iter->io.subchannel_id) +				continue; +			if ((schid & 0x000000000000ffff) != +			    iter->io.subchannel_nr) +				continue; +		} +		inti = iter; +		break; +	} +	if (inti) +		list_del_init(&inti->list); +	if (list_empty(&fi->list)) +		atomic_set(&fi->active, 0); +	spin_unlock(&fi->lock); +	mutex_unlock(&kvm->lock); +	return inti; +} +  int kvm_s390_inject_vm(struct kvm *kvm,  		       struct kvm_s390_interrupt *s390int)  {  	struct kvm_s390_local_interrupt *li;  	struct kvm_s390_float_interrupt *fi; -	struct kvm_s390_interrupt_info *inti; +	struct kvm_s390_interrupt_info *inti, *iter;  	int sigcpu;  	inti = kzalloc(sizeof(*inti), GFP_KERNEL); @@ -569,6 +685,29 @@ int kvm_s390_inject_vm(struct kvm *kvm,  	case KVM_S390_SIGP_STOP:  	case KVM_S390_INT_EXTERNAL_CALL:  	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)"); +		else +			VM_EVENT(kvm, 5, "inject: I/O css %x ss %x schid %04x", +				 s390int->type & IOINT_CSSID_MASK, +				 s390int->type & IOINT_SSID_MASK, +				 s390int->type & IOINT_SCHID_MASK); +		inti->type = s390int->type; +		inti->io.subchannel_id = s390int->parm >> 16; +		inti->io.subchannel_nr = s390int->parm & 0x0000ffffu; +		inti->io.io_int_parm = s390int->parm64 >> 32; +		inti->io.io_int_word = s390int->parm64 & 0x00000000ffffffffull; +		break;  	default:  		kfree(inti);  		return -EINVAL; @@ -579,7 +718,22 @@ int kvm_s390_inject_vm(struct kvm *kvm,  	mutex_lock(&kvm->lock);  	fi = &kvm->arch.float_int;  	spin_lock(&fi->lock); -	list_add_tail(&inti->list, &fi->list); +	if (!is_ioint(inti->type)) +		list_add_tail(&inti->list, &fi->list); +	else { +		u64 isc_bits = int_word_to_isc_bits(inti->io.io_int_word); + +		/* Keep I/O interrupts sorted in isc order. */ +		list_for_each_entry(iter, &fi->list, list) { +			if (!is_ioint(iter->type)) +				continue; +			if (int_word_to_isc_bits(iter->io.io_int_word) +			    <= isc_bits) +				continue; +			break; +		} +		list_add_tail(&inti->list, &iter->list); +	}  	atomic_set(&fi->active, 1);  	sigcpu = find_first_bit(fi->idle_mask, KVM_MAX_VCPUS);  	if (sigcpu == KVM_MAX_VCPUS) { @@ -651,8 +805,15 @@ 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:  	default:  		kfree(inti);  		return -EINVAL;  |