xref: /linux/arch/s390/kvm/intercept.c (revision aba0750889d012f84a719112997abb7be11bba4b)
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,
32*aba07508SDavid Hildenbrand 	[0xb6] = kvm_s390_handle_stctl,
33953ed88dSThomas Huth 	[0xb7] = kvm_s390_handle_lctl,
3448a3e950SCornelia Huck 	[0xb9] = kvm_s390_handle_b9,
35bb25b9baSChristian Borntraeger 	[0xe5] = kvm_s390_handle_e5,
36953ed88dSThomas Huth 	[0xeb] = kvm_s390_handle_eb,
37ba5c1e9bSCarsten Otte };
388f2abe6aSChristian Borntraeger 
398f2abe6aSChristian Borntraeger static int handle_noop(struct kvm_vcpu *vcpu)
408f2abe6aSChristian Borntraeger {
418f2abe6aSChristian Borntraeger 	switch (vcpu->arch.sie_block->icptcode) {
420eaeafa1SChristian Borntraeger 	case 0x0:
430eaeafa1SChristian Borntraeger 		vcpu->stat.exit_null++;
440eaeafa1SChristian Borntraeger 		break;
458f2abe6aSChristian Borntraeger 	case 0x10:
468f2abe6aSChristian Borntraeger 		vcpu->stat.exit_external_request++;
478f2abe6aSChristian Borntraeger 		break;
488f2abe6aSChristian Borntraeger 	case 0x14:
498f2abe6aSChristian Borntraeger 		vcpu->stat.exit_external_interrupt++;
508f2abe6aSChristian Borntraeger 		break;
518f2abe6aSChristian Borntraeger 	default:
528f2abe6aSChristian Borntraeger 		break; /* nothing */
538f2abe6aSChristian Borntraeger 	}
548f2abe6aSChristian Borntraeger 	return 0;
558f2abe6aSChristian Borntraeger }
568f2abe6aSChristian Borntraeger 
578f2abe6aSChristian Borntraeger static int handle_stop(struct kvm_vcpu *vcpu)
588f2abe6aSChristian Borntraeger {
599ace903dSChristian Ehrhardt 	int rc = 0;
605288fbf0SChristian Borntraeger 
618f2abe6aSChristian Borntraeger 	vcpu->stat.exit_stop_request++;
625288fbf0SChristian Borntraeger 	spin_lock_bh(&vcpu->arch.local_int.lock);
635288fbf0SChristian Borntraeger 
64ade38c31SCornelia Huck 	trace_kvm_s390_stop_request(vcpu->arch.local_int.action_bits);
65ade38c31SCornelia Huck 
665288fbf0SChristian Borntraeger 	if (vcpu->arch.local_int.action_bits & ACTION_STOP_ON_STOP) {
679e6dabefSCornelia Huck 		atomic_set_mask(CPUSTAT_STOPPED,
689e6dabefSCornelia Huck 				&vcpu->arch.sie_block->cpuflags);
695288fbf0SChristian Borntraeger 		vcpu->arch.local_int.action_bits &= ~ACTION_STOP_ON_STOP;
705288fbf0SChristian Borntraeger 		VCPU_EVENT(vcpu, 3, "%s", "cpu stopped");
71b8e660b8SHeiko Carstens 		rc = -EOPNOTSUPP;
729ace903dSChristian Ehrhardt 	}
739ace903dSChristian Ehrhardt 
749e0d5473SJens Freimann 	if (vcpu->arch.local_int.action_bits & ACTION_STORE_ON_STOP) {
759e0d5473SJens Freimann 		vcpu->arch.local_int.action_bits &= ~ACTION_STORE_ON_STOP;
769e0d5473SJens Freimann 		/* store status must be called unlocked. Since local_int.lock
779e0d5473SJens Freimann 		 * only protects local_int.* and not guest memory we can give
789e0d5473SJens Freimann 		 * up the lock here */
799e0d5473SJens Freimann 		spin_unlock_bh(&vcpu->arch.local_int.lock);
809e0d5473SJens Freimann 		rc = kvm_s390_vcpu_store_status(vcpu,
819e0d5473SJens Freimann 						KVM_S390_STORE_STATUS_NOADDR);
829e0d5473SJens Freimann 		if (rc >= 0)
839e0d5473SJens Freimann 			rc = -EOPNOTSUPP;
849e0d5473SJens Freimann 	} else
855288fbf0SChristian Borntraeger 		spin_unlock_bh(&vcpu->arch.local_int.lock);
865288fbf0SChristian Borntraeger 	return rc;
878f2abe6aSChristian Borntraeger }
888f2abe6aSChristian Borntraeger 
898f2abe6aSChristian Borntraeger static int handle_validity(struct kvm_vcpu *vcpu)
908f2abe6aSChristian Borntraeger {
918f2abe6aSChristian Borntraeger 	int viwhy = vcpu->arch.sie_block->ipb >> 16;
923edbcff9SCarsten Otte 
938f2abe6aSChristian Borntraeger 	vcpu->stat.exit_validity++;
945786fffaSCornelia Huck 	trace_kvm_s390_intercept_validity(vcpu, viwhy);
952c70fe44SChristian Borntraeger 	WARN_ONCE(true, "kvm: unhandled validity intercept 0x%x\n", viwhy);
962c70fe44SChristian Borntraeger 	return -EOPNOTSUPP;
978f2abe6aSChristian Borntraeger }
988f2abe6aSChristian Borntraeger 
99ba5c1e9bSCarsten Otte static int handle_instruction(struct kvm_vcpu *vcpu)
100ba5c1e9bSCarsten Otte {
101ba5c1e9bSCarsten Otte 	intercept_handler_t handler;
102ba5c1e9bSCarsten Otte 
103ba5c1e9bSCarsten Otte 	vcpu->stat.exit_instruction++;
1045786fffaSCornelia Huck 	trace_kvm_s390_intercept_instruction(vcpu,
1055786fffaSCornelia Huck 					     vcpu->arch.sie_block->ipa,
1065786fffaSCornelia Huck 					     vcpu->arch.sie_block->ipb);
107ba5c1e9bSCarsten Otte 	handler = instruction_handlers[vcpu->arch.sie_block->ipa >> 8];
108ba5c1e9bSCarsten Otte 	if (handler)
109ba5c1e9bSCarsten Otte 		return handler(vcpu);
110b8e660b8SHeiko Carstens 	return -EOPNOTSUPP;
111ba5c1e9bSCarsten Otte }
112ba5c1e9bSCarsten Otte 
113439716a5SDavid Hildenbrand static void __extract_prog_irq(struct kvm_vcpu *vcpu,
114439716a5SDavid Hildenbrand 			       struct kvm_s390_pgm_info *pgm_info)
115439716a5SDavid Hildenbrand {
116439716a5SDavid Hildenbrand 	memset(pgm_info, 0, sizeof(struct kvm_s390_pgm_info));
117439716a5SDavid Hildenbrand 	pgm_info->code = vcpu->arch.sie_block->iprcc;
118439716a5SDavid Hildenbrand 
119439716a5SDavid Hildenbrand 	switch (vcpu->arch.sie_block->iprcc & ~PGM_PER) {
120439716a5SDavid Hildenbrand 	case PGM_AFX_TRANSLATION:
121439716a5SDavid Hildenbrand 	case PGM_ASX_TRANSLATION:
122439716a5SDavid Hildenbrand 	case PGM_EX_TRANSLATION:
123439716a5SDavid Hildenbrand 	case PGM_LFX_TRANSLATION:
124439716a5SDavid Hildenbrand 	case PGM_LSTE_SEQUENCE:
125439716a5SDavid Hildenbrand 	case PGM_LSX_TRANSLATION:
126439716a5SDavid Hildenbrand 	case PGM_LX_TRANSLATION:
127439716a5SDavid Hildenbrand 	case PGM_PRIMARY_AUTHORITY:
128439716a5SDavid Hildenbrand 	case PGM_SECONDARY_AUTHORITY:
129439716a5SDavid Hildenbrand 	case PGM_SPACE_SWITCH:
130439716a5SDavid Hildenbrand 		pgm_info->trans_exc_code = vcpu->arch.sie_block->tecmc;
131439716a5SDavid Hildenbrand 		break;
132439716a5SDavid Hildenbrand 	case PGM_ALEN_TRANSLATION:
133439716a5SDavid Hildenbrand 	case PGM_ALE_SEQUENCE:
134439716a5SDavid Hildenbrand 	case PGM_ASTE_INSTANCE:
135439716a5SDavid Hildenbrand 	case PGM_ASTE_SEQUENCE:
136439716a5SDavid Hildenbrand 	case PGM_ASTE_VALIDITY:
137439716a5SDavid Hildenbrand 	case PGM_EXTENDED_AUTHORITY:
138439716a5SDavid Hildenbrand 		pgm_info->exc_access_id = vcpu->arch.sie_block->eai;
139439716a5SDavid Hildenbrand 		break;
140439716a5SDavid Hildenbrand 	case PGM_ASCE_TYPE:
141439716a5SDavid Hildenbrand 	case PGM_PAGE_TRANSLATION:
142439716a5SDavid Hildenbrand 	case PGM_REGION_FIRST_TRANS:
143439716a5SDavid Hildenbrand 	case PGM_REGION_SECOND_TRANS:
144439716a5SDavid Hildenbrand 	case PGM_REGION_THIRD_TRANS:
145439716a5SDavid Hildenbrand 	case PGM_SEGMENT_TRANSLATION:
146439716a5SDavid Hildenbrand 		pgm_info->trans_exc_code = vcpu->arch.sie_block->tecmc;
147439716a5SDavid Hildenbrand 		pgm_info->exc_access_id  = vcpu->arch.sie_block->eai;
148439716a5SDavid Hildenbrand 		pgm_info->op_access_id  = vcpu->arch.sie_block->oai;
149439716a5SDavid Hildenbrand 		break;
150439716a5SDavid Hildenbrand 	case PGM_MONITOR:
151439716a5SDavid Hildenbrand 		pgm_info->mon_class_nr = vcpu->arch.sie_block->mcn;
152439716a5SDavid Hildenbrand 		pgm_info->mon_code = vcpu->arch.sie_block->tecmc;
153439716a5SDavid Hildenbrand 		break;
154439716a5SDavid Hildenbrand 	case PGM_DATA:
155439716a5SDavid Hildenbrand 		pgm_info->data_exc_code = vcpu->arch.sie_block->dxc;
156439716a5SDavid Hildenbrand 		break;
157439716a5SDavid Hildenbrand 	case PGM_PROTECTION:
158439716a5SDavid Hildenbrand 		pgm_info->trans_exc_code = vcpu->arch.sie_block->tecmc;
159439716a5SDavid Hildenbrand 		pgm_info->exc_access_id  = vcpu->arch.sie_block->eai;
160439716a5SDavid Hildenbrand 		break;
161439716a5SDavid Hildenbrand 	default:
162439716a5SDavid Hildenbrand 		break;
163439716a5SDavid Hildenbrand 	}
164439716a5SDavid Hildenbrand 
165439716a5SDavid Hildenbrand 	if (vcpu->arch.sie_block->iprcc & PGM_PER) {
166439716a5SDavid Hildenbrand 		pgm_info->per_code = vcpu->arch.sie_block->perc;
167439716a5SDavid Hildenbrand 		pgm_info->per_atmid = vcpu->arch.sie_block->peratmid;
168439716a5SDavid Hildenbrand 		pgm_info->per_address = vcpu->arch.sie_block->peraddr;
169439716a5SDavid Hildenbrand 		pgm_info->per_access_id = vcpu->arch.sie_block->peraid;
170439716a5SDavid Hildenbrand 	}
171439716a5SDavid Hildenbrand }
172439716a5SDavid Hildenbrand 
173ba5c1e9bSCarsten Otte static int handle_prog(struct kvm_vcpu *vcpu)
174ba5c1e9bSCarsten Otte {
175439716a5SDavid Hildenbrand 	struct kvm_s390_pgm_info pgm_info;
1760040e7d2SHeiko Carstens 	struct kvm_s390_itdb *itdb;
1770040e7d2SHeiko Carstens 	int rc;
1780040e7d2SHeiko Carstens 
179ba5c1e9bSCarsten Otte 	vcpu->stat.exit_program_interruption++;
1807feb6bb8SMichael Mueller 
1817feb6bb8SMichael Mueller 	/* Restore ITDB to Program-Interruption TDB in guest memory */
1820040e7d2SHeiko Carstens 	if (!IS_TE_ENABLED(vcpu) || !IS_ITDB_VALID(vcpu))
1830040e7d2SHeiko Carstens 		goto skip_itdb;
1840040e7d2SHeiko Carstens 	if (current->thread.per_flags & PER_FLAG_NO_TE)
1850040e7d2SHeiko Carstens 		goto skip_itdb;
1860040e7d2SHeiko Carstens 	itdb = (struct kvm_s390_itdb *)vcpu->arch.sie_block->itdba;
1870040e7d2SHeiko Carstens 	rc = write_guest_lc(vcpu, TDB_ADDR, itdb, sizeof(*itdb));
1880040e7d2SHeiko Carstens 	if (rc)
1890040e7d2SHeiko Carstens 		return rc;
1900040e7d2SHeiko Carstens 	memset(itdb, 0, sizeof(*itdb));
1910040e7d2SHeiko Carstens skip_itdb:
1925786fffaSCornelia Huck 	trace_kvm_s390_intercept_prog(vcpu, vcpu->arch.sie_block->iprcc);
193439716a5SDavid Hildenbrand 	__extract_prog_irq(vcpu, &pgm_info);
194439716a5SDavid Hildenbrand 
195439716a5SDavid Hildenbrand 	return kvm_s390_inject_prog_irq(vcpu, &pgm_info);
196ba5c1e9bSCarsten Otte }
197ba5c1e9bSCarsten Otte 
198ba5c1e9bSCarsten Otte static int handle_instruction_and_prog(struct kvm_vcpu *vcpu)
199ba5c1e9bSCarsten Otte {
200ba5c1e9bSCarsten Otte 	int rc, rc2;
201ba5c1e9bSCarsten Otte 
202ba5c1e9bSCarsten Otte 	vcpu->stat.exit_instr_and_program++;
203ba5c1e9bSCarsten Otte 	rc = handle_instruction(vcpu);
204ba5c1e9bSCarsten Otte 	rc2 = handle_prog(vcpu);
205ba5c1e9bSCarsten Otte 
206b8e660b8SHeiko Carstens 	if (rc == -EOPNOTSUPP)
207ba5c1e9bSCarsten Otte 		vcpu->arch.sie_block->icptcode = 0x04;
208ba5c1e9bSCarsten Otte 	if (rc)
209ba5c1e9bSCarsten Otte 		return rc;
210ba5c1e9bSCarsten Otte 	return rc2;
211ba5c1e9bSCarsten Otte }
212ba5c1e9bSCarsten Otte 
213062d5e9bSChristian Borntraeger static const intercept_handler_t intercept_funcs[] = {
2148f2abe6aSChristian Borntraeger 	[0x00 >> 2] = handle_noop,
215ba5c1e9bSCarsten Otte 	[0x04 >> 2] = handle_instruction,
216ba5c1e9bSCarsten Otte 	[0x08 >> 2] = handle_prog,
217ba5c1e9bSCarsten Otte 	[0x0C >> 2] = handle_instruction_and_prog,
2188f2abe6aSChristian Borntraeger 	[0x10 >> 2] = handle_noop,
2198f2abe6aSChristian Borntraeger 	[0x14 >> 2] = handle_noop,
220fa6b7fe9SCornelia Huck 	[0x18 >> 2] = handle_noop,
221ba5c1e9bSCarsten Otte 	[0x1C >> 2] = kvm_s390_handle_wait,
2228f2abe6aSChristian Borntraeger 	[0x20 >> 2] = handle_validity,
2238f2abe6aSChristian Borntraeger 	[0x28 >> 2] = handle_stop,
2248f2abe6aSChristian Borntraeger };
2258f2abe6aSChristian Borntraeger 
2268f2abe6aSChristian Borntraeger int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu)
2278f2abe6aSChristian Borntraeger {
2288f2abe6aSChristian Borntraeger 	intercept_handler_t func;
2298f2abe6aSChristian Borntraeger 	u8 code = vcpu->arch.sie_block->icptcode;
2308f2abe6aSChristian Borntraeger 
231062d5e9bSChristian Borntraeger 	if (code & 3 || (code >> 2) >= ARRAY_SIZE(intercept_funcs))
232b8e660b8SHeiko Carstens 		return -EOPNOTSUPP;
2338f2abe6aSChristian Borntraeger 	func = intercept_funcs[code >> 2];
2348f2abe6aSChristian Borntraeger 	if (func)
2358f2abe6aSChristian Borntraeger 		return func(vcpu);
236b8e660b8SHeiko Carstens 	return -EOPNOTSUPP;
2378f2abe6aSChristian Borntraeger }
238