diff options
Diffstat (limited to 'arch/s390/kvm/priv.c')
| -rw-r--r-- | arch/s390/kvm/priv.c | 91 | 
1 files changed, 87 insertions, 4 deletions
diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c index 8ad776f8785..0ef9894606e 100644 --- a/arch/s390/kvm/priv.c +++ b/arch/s390/kvm/priv.c @@ -127,15 +127,98 @@ static int handle_skey(struct kvm_vcpu *vcpu)  	return 0;  } -static int handle_io_inst(struct kvm_vcpu *vcpu) +static int handle_tpi(struct kvm_vcpu *vcpu)  { -	VCPU_EVENT(vcpu, 4, "%s", "I/O instruction"); -	/* condition code 3 */ +	u64 addr; +	struct kvm_s390_interrupt_info *inti; +	int cc; + +	addr = kvm_s390_get_base_disp_s(vcpu); + +	inti = kvm_s390_get_io_int(vcpu->kvm, vcpu->run->s.regs.crs[6], 0); +	if (inti) { +		if (addr) { +			/* +			 * Store the two-word I/O interruption code into the +			 * provided area. +			 */ +			put_guest_u16(vcpu, addr, inti->io.subchannel_id); +			put_guest_u16(vcpu, addr + 2, inti->io.subchannel_nr); +			put_guest_u32(vcpu, addr + 4, inti->io.io_int_parm); +		} else { +			/* +			 * Store the three-word I/O interruption code into +			 * the appropriate lowcore area. +			 */ +			put_guest_u16(vcpu, 184, inti->io.subchannel_id); +			put_guest_u16(vcpu, 186, inti->io.subchannel_nr); +			put_guest_u32(vcpu, 188, inti->io.io_int_parm); +			put_guest_u32(vcpu, 192, inti->io.io_int_word); +		} +		cc = 1; +	} else +		cc = 0; +	kfree(inti); +	/* Set condition code and we're done. */  	vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44); -	vcpu->arch.sie_block->gpsw.mask |= (3 & 3ul) << 44; +	vcpu->arch.sie_block->gpsw.mask |= (cc & 3ul) << 44;  	return 0;  } +static int handle_tsch(struct kvm_vcpu *vcpu) +{ +	struct kvm_s390_interrupt_info *inti; + +	inti = kvm_s390_get_io_int(vcpu->kvm, 0, +				   vcpu->run->s.regs.gprs[1]); + +	/* +	 * Prepare exit to userspace. +	 * We indicate whether we dequeued a pending I/O interrupt +	 * so that userspace can re-inject it if the instruction gets +	 * a program check. While this may re-order the pending I/O +	 * interrupts, this is no problem since the priority is kept +	 * intact. +	 */ +	vcpu->run->exit_reason = KVM_EXIT_S390_TSCH; +	vcpu->run->s390_tsch.dequeued = !!inti; +	if (inti) { +		vcpu->run->s390_tsch.subchannel_id = inti->io.subchannel_id; +		vcpu->run->s390_tsch.subchannel_nr = inti->io.subchannel_nr; +		vcpu->run->s390_tsch.io_int_parm = inti->io.io_int_parm; +		vcpu->run->s390_tsch.io_int_word = inti->io.io_int_word; +	} +	vcpu->run->s390_tsch.ipb = vcpu->arch.sie_block->ipb; +	kfree(inti); +	return -EREMOTE; +} + +static int handle_io_inst(struct kvm_vcpu *vcpu) +{ +	VCPU_EVENT(vcpu, 4, "%s", "I/O instruction"); + +	if (vcpu->kvm->arch.css_support) { +		/* +		 * Most I/O instructions will be handled by userspace. +		 * Exceptions are tpi and the interrupt portion of tsch. +		 */ +		if (vcpu->arch.sie_block->ipa == 0xb236) +			return handle_tpi(vcpu); +		if (vcpu->arch.sie_block->ipa == 0xb235) +			return handle_tsch(vcpu); +		/* Handle in userspace. */ +		return -EOPNOTSUPP; +	} else { +		/* +		 * Set condition code 3 to stop the guest from issueing channel +		 * I/O instructions. +		 */ +		vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44); +		vcpu->arch.sie_block->gpsw.mask |= (3 & 3ul) << 44; +		return 0; +	} +} +  static int handle_stfl(struct kvm_vcpu *vcpu)  {  	unsigned int facility_list;  |