xref: /linux/arch/s390/kvm/intercept.c (revision 0040e7d20fe467a4bcdb8a6c284631b80efffa8b)
18f2abe6aSChristian Borntraeger /*
2a53c8fabSHeiko Carstens  * in-kernel handling for sie intercepts
38f2abe6aSChristian Borntraeger  *
4628eb9b8SChristian Ehrhardt  * Copyright IBM Corp. 2008, 2009
58f2abe6aSChristian Borntraeger  *
68f2abe6aSChristian Borntraeger  * This program is free software; you can redistribute it and/or modify
78f2abe6aSChristian Borntraeger  * it under the terms of the GNU General Public License (version 2 only)
88f2abe6aSChristian Borntraeger  * as published by the Free Software Foundation.
98f2abe6aSChristian Borntraeger  *
108f2abe6aSChristian Borntraeger  *    Author(s): Carsten Otte <cotte@de.ibm.com>
118f2abe6aSChristian Borntraeger  *               Christian Borntraeger <borntraeger@de.ibm.com>
128f2abe6aSChristian Borntraeger  */
138f2abe6aSChristian Borntraeger 
148f2abe6aSChristian Borntraeger #include <linux/kvm_host.h>
158f2abe6aSChristian Borntraeger #include <linux/errno.h>
168f2abe6aSChristian Borntraeger #include <linux/pagemap.h>
178f2abe6aSChristian Borntraeger 
188f2abe6aSChristian Borntraeger #include <asm/kvm_host.h>
198f2abe6aSChristian Borntraeger 
208f2abe6aSChristian Borntraeger #include "kvm-s390.h"
21ba5c1e9bSCarsten Otte #include "gaccess.h"
225786fffaSCornelia Huck #include "trace.h"
23ade38c31SCornelia Huck #include "trace-s390.h"
24ba5c1e9bSCarsten Otte 
25f379aae5SCornelia Huck 
2677975357SCornelia Huck static const intercept_handler_t instruction_handlers[256] = {
278c3f61e2SCornelia Huck 	[0x01] = kvm_s390_handle_01,
2848a3e950SCornelia Huck 	[0x82] = kvm_s390_handle_lpsw,
29e28acfeaSChristian Borntraeger 	[0x83] = kvm_s390_handle_diag,
305288fbf0SChristian Borntraeger 	[0xae] = kvm_s390_handle_sigp,
3170455a36SChristian Borntraeger 	[0xb2] = kvm_s390_handle_b2,
32953ed88dSThomas Huth 	[0xb7] = kvm_s390_handle_lctl,
3348a3e950SCornelia Huck 	[0xb9] = kvm_s390_handle_b9,
34bb25b9baSChristian Borntraeger 	[0xe5] = kvm_s390_handle_e5,
35953ed88dSThomas Huth 	[0xeb] = kvm_s390_handle_eb,
36ba5c1e9bSCarsten Otte };
378f2abe6aSChristian Borntraeger 
388f2abe6aSChristian Borntraeger static int handle_noop(struct kvm_vcpu *vcpu)
398f2abe6aSChristian Borntraeger {
408f2abe6aSChristian Borntraeger 	switch (vcpu->arch.sie_block->icptcode) {
410eaeafa1SChristian Borntraeger 	case 0x0:
420eaeafa1SChristian Borntraeger 		vcpu->stat.exit_null++;
430eaeafa1SChristian Borntraeger 		break;
448f2abe6aSChristian Borntraeger 	case 0x10:
458f2abe6aSChristian Borntraeger 		vcpu->stat.exit_external_request++;
468f2abe6aSChristian Borntraeger 		break;
478f2abe6aSChristian Borntraeger 	case 0x14:
488f2abe6aSChristian Borntraeger 		vcpu->stat.exit_external_interrupt++;
498f2abe6aSChristian Borntraeger 		break;
508f2abe6aSChristian Borntraeger 	default:
518f2abe6aSChristian Borntraeger 		break; /* nothing */
528f2abe6aSChristian Borntraeger 	}
538f2abe6aSChristian Borntraeger 	return 0;
548f2abe6aSChristian Borntraeger }
558f2abe6aSChristian Borntraeger 
568f2abe6aSChristian Borntraeger static int handle_stop(struct kvm_vcpu *vcpu)
578f2abe6aSChristian Borntraeger {
589ace903dSChristian Ehrhardt 	int rc = 0;
595288fbf0SChristian Borntraeger 
608f2abe6aSChristian Borntraeger 	vcpu->stat.exit_stop_request++;
615288fbf0SChristian Borntraeger 	spin_lock_bh(&vcpu->arch.local_int.lock);
625288fbf0SChristian Borntraeger 
63ade38c31SCornelia Huck 	trace_kvm_s390_stop_request(vcpu->arch.local_int.action_bits);
64ade38c31SCornelia Huck 
655288fbf0SChristian Borntraeger 	if (vcpu->arch.local_int.action_bits & ACTION_STOP_ON_STOP) {
669e6dabefSCornelia Huck 		atomic_set_mask(CPUSTAT_STOPPED,
679e6dabefSCornelia Huck 				&vcpu->arch.sie_block->cpuflags);
685288fbf0SChristian Borntraeger 		vcpu->arch.local_int.action_bits &= ~ACTION_STOP_ON_STOP;
695288fbf0SChristian Borntraeger 		VCPU_EVENT(vcpu, 3, "%s", "cpu stopped");
70b8e660b8SHeiko Carstens 		rc = -EOPNOTSUPP;
719ace903dSChristian Ehrhardt 	}
729ace903dSChristian Ehrhardt 
739e0d5473SJens Freimann 	if (vcpu->arch.local_int.action_bits & ACTION_STORE_ON_STOP) {
749e0d5473SJens Freimann 		vcpu->arch.local_int.action_bits &= ~ACTION_STORE_ON_STOP;
759e0d5473SJens Freimann 		/* store status must be called unlocked. Since local_int.lock
769e0d5473SJens Freimann 		 * only protects local_int.* and not guest memory we can give
779e0d5473SJens Freimann 		 * up the lock here */
789e0d5473SJens Freimann 		spin_unlock_bh(&vcpu->arch.local_int.lock);
799e0d5473SJens Freimann 		rc = kvm_s390_vcpu_store_status(vcpu,
809e0d5473SJens Freimann 						KVM_S390_STORE_STATUS_NOADDR);
819e0d5473SJens Freimann 		if (rc >= 0)
829e0d5473SJens Freimann 			rc = -EOPNOTSUPP;
839e0d5473SJens Freimann 	} else
845288fbf0SChristian Borntraeger 		spin_unlock_bh(&vcpu->arch.local_int.lock);
855288fbf0SChristian Borntraeger 	return rc;
868f2abe6aSChristian Borntraeger }
878f2abe6aSChristian Borntraeger 
888f2abe6aSChristian Borntraeger static int handle_validity(struct kvm_vcpu *vcpu)
898f2abe6aSChristian Borntraeger {
908f2abe6aSChristian Borntraeger 	int viwhy = vcpu->arch.sie_block->ipb >> 16;
913edbcff9SCarsten Otte 
928f2abe6aSChristian Borntraeger 	vcpu->stat.exit_validity++;
935786fffaSCornelia Huck 	trace_kvm_s390_intercept_validity(vcpu, viwhy);
942c70fe44SChristian Borntraeger 	WARN_ONCE(true, "kvm: unhandled validity intercept 0x%x\n", viwhy);
952c70fe44SChristian Borntraeger 	return -EOPNOTSUPP;
968f2abe6aSChristian Borntraeger }
978f2abe6aSChristian Borntraeger 
98ba5c1e9bSCarsten Otte static int handle_instruction(struct kvm_vcpu *vcpu)
99ba5c1e9bSCarsten Otte {
100ba5c1e9bSCarsten Otte 	intercept_handler_t handler;
101ba5c1e9bSCarsten Otte 
102ba5c1e9bSCarsten Otte 	vcpu->stat.exit_instruction++;
1035786fffaSCornelia Huck 	trace_kvm_s390_intercept_instruction(vcpu,
1045786fffaSCornelia Huck 					     vcpu->arch.sie_block->ipa,
1055786fffaSCornelia Huck 					     vcpu->arch.sie_block->ipb);
106ba5c1e9bSCarsten Otte 	handler = instruction_handlers[vcpu->arch.sie_block->ipa >> 8];
107ba5c1e9bSCarsten Otte 	if (handler)
108ba5c1e9bSCarsten Otte 		return handler(vcpu);
109b8e660b8SHeiko Carstens 	return -EOPNOTSUPP;
110ba5c1e9bSCarsten Otte }
111ba5c1e9bSCarsten Otte 
112ba5c1e9bSCarsten Otte static int handle_prog(struct kvm_vcpu *vcpu)
113ba5c1e9bSCarsten Otte {
114*0040e7d2SHeiko Carstens 	struct kvm_s390_itdb *itdb;
115*0040e7d2SHeiko Carstens 	int rc;
116*0040e7d2SHeiko Carstens 
117ba5c1e9bSCarsten Otte 	vcpu->stat.exit_program_interruption++;
1187feb6bb8SMichael Mueller 
1197feb6bb8SMichael Mueller 	/* Restore ITDB to Program-Interruption TDB in guest memory */
120*0040e7d2SHeiko Carstens 	if (!IS_TE_ENABLED(vcpu) || !IS_ITDB_VALID(vcpu))
121*0040e7d2SHeiko Carstens 		goto skip_itdb;
122*0040e7d2SHeiko Carstens 	if (current->thread.per_flags & PER_FLAG_NO_TE)
123*0040e7d2SHeiko Carstens 		goto skip_itdb;
124*0040e7d2SHeiko Carstens 	itdb = (struct kvm_s390_itdb *)vcpu->arch.sie_block->itdba;
125*0040e7d2SHeiko Carstens 	rc = write_guest_lc(vcpu, TDB_ADDR, itdb, sizeof(*itdb));
126*0040e7d2SHeiko Carstens 	if (rc)
127*0040e7d2SHeiko Carstens 		return rc;
128*0040e7d2SHeiko Carstens 	memset(itdb, 0, sizeof(*itdb));
129*0040e7d2SHeiko Carstens skip_itdb:
1305786fffaSCornelia Huck 	trace_kvm_s390_intercept_prog(vcpu, vcpu->arch.sie_block->iprcc);
131ba5c1e9bSCarsten Otte 	return kvm_s390_inject_program_int(vcpu, vcpu->arch.sie_block->iprcc);
132ba5c1e9bSCarsten Otte }
133ba5c1e9bSCarsten Otte 
134ba5c1e9bSCarsten Otte static int handle_instruction_and_prog(struct kvm_vcpu *vcpu)
135ba5c1e9bSCarsten Otte {
136ba5c1e9bSCarsten Otte 	int rc, rc2;
137ba5c1e9bSCarsten Otte 
138ba5c1e9bSCarsten Otte 	vcpu->stat.exit_instr_and_program++;
139ba5c1e9bSCarsten Otte 	rc = handle_instruction(vcpu);
140ba5c1e9bSCarsten Otte 	rc2 = handle_prog(vcpu);
141ba5c1e9bSCarsten Otte 
142b8e660b8SHeiko Carstens 	if (rc == -EOPNOTSUPP)
143ba5c1e9bSCarsten Otte 		vcpu->arch.sie_block->icptcode = 0x04;
144ba5c1e9bSCarsten Otte 	if (rc)
145ba5c1e9bSCarsten Otte 		return rc;
146ba5c1e9bSCarsten Otte 	return rc2;
147ba5c1e9bSCarsten Otte }
148ba5c1e9bSCarsten Otte 
149062d5e9bSChristian Borntraeger static const intercept_handler_t intercept_funcs[] = {
1508f2abe6aSChristian Borntraeger 	[0x00 >> 2] = handle_noop,
151ba5c1e9bSCarsten Otte 	[0x04 >> 2] = handle_instruction,
152ba5c1e9bSCarsten Otte 	[0x08 >> 2] = handle_prog,
153ba5c1e9bSCarsten Otte 	[0x0C >> 2] = handle_instruction_and_prog,
1548f2abe6aSChristian Borntraeger 	[0x10 >> 2] = handle_noop,
1558f2abe6aSChristian Borntraeger 	[0x14 >> 2] = handle_noop,
156fa6b7fe9SCornelia Huck 	[0x18 >> 2] = handle_noop,
157ba5c1e9bSCarsten Otte 	[0x1C >> 2] = kvm_s390_handle_wait,
1588f2abe6aSChristian Borntraeger 	[0x20 >> 2] = handle_validity,
1598f2abe6aSChristian Borntraeger 	[0x28 >> 2] = handle_stop,
1608f2abe6aSChristian Borntraeger };
1618f2abe6aSChristian Borntraeger 
1628f2abe6aSChristian Borntraeger int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu)
1638f2abe6aSChristian Borntraeger {
1648f2abe6aSChristian Borntraeger 	intercept_handler_t func;
1658f2abe6aSChristian Borntraeger 	u8 code = vcpu->arch.sie_block->icptcode;
1668f2abe6aSChristian Borntraeger 
167062d5e9bSChristian Borntraeger 	if (code & 3 || (code >> 2) >= ARRAY_SIZE(intercept_funcs))
168b8e660b8SHeiko Carstens 		return -EOPNOTSUPP;
1698f2abe6aSChristian Borntraeger 	func = intercept_funcs[code >> 2];
1708f2abe6aSChristian Borntraeger 	if (func)
1718f2abe6aSChristian Borntraeger 		return func(vcpu);
172b8e660b8SHeiko Carstens 	return -EOPNOTSUPP;
1738f2abe6aSChristian Borntraeger }
174