xref: /openbmc/linux/arch/s390/kvm/intercept.c (revision 84744377)
1 /*
2  * in-kernel handling for sie intercepts
3  *
4  * Copyright IBM Corp. 2008, 2009
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License (version 2 only)
8  * as published by the Free Software Foundation.
9  *
10  *    Author(s): Carsten Otte <cotte@de.ibm.com>
11  *               Christian Borntraeger <borntraeger@de.ibm.com>
12  */
13 
14 #include <linux/kvm_host.h>
15 #include <linux/errno.h>
16 #include <linux/pagemap.h>
17 
18 #include <asm/kvm_host.h>
19 
20 #include "kvm-s390.h"
21 #include "gaccess.h"
22 #include "trace.h"
23 #include "trace-s390.h"
24 
25 
26 static const intercept_handler_t instruction_handlers[256] = {
27 	[0x01] = kvm_s390_handle_01,
28 	[0x82] = kvm_s390_handle_lpsw,
29 	[0x83] = kvm_s390_handle_diag,
30 	[0xae] = kvm_s390_handle_sigp,
31 	[0xb2] = kvm_s390_handle_b2,
32 	[0xb7] = kvm_s390_handle_lctl,
33 	[0xb9] = kvm_s390_handle_b9,
34 	[0xe5] = kvm_s390_handle_e5,
35 	[0xeb] = kvm_s390_handle_eb,
36 };
37 
38 static int handle_noop(struct kvm_vcpu *vcpu)
39 {
40 	switch (vcpu->arch.sie_block->icptcode) {
41 	case 0x0:
42 		vcpu->stat.exit_null++;
43 		break;
44 	case 0x10:
45 		vcpu->stat.exit_external_request++;
46 		break;
47 	case 0x14:
48 		vcpu->stat.exit_external_interrupt++;
49 		break;
50 	default:
51 		break; /* nothing */
52 	}
53 	return 0;
54 }
55 
56 static int handle_stop(struct kvm_vcpu *vcpu)
57 {
58 	int rc = 0;
59 
60 	vcpu->stat.exit_stop_request++;
61 	spin_lock_bh(&vcpu->arch.local_int.lock);
62 
63 	trace_kvm_s390_stop_request(vcpu->arch.local_int.action_bits);
64 
65 	if (vcpu->arch.local_int.action_bits & ACTION_STOP_ON_STOP) {
66 		atomic_set_mask(CPUSTAT_STOPPED,
67 				&vcpu->arch.sie_block->cpuflags);
68 		vcpu->arch.local_int.action_bits &= ~ACTION_STOP_ON_STOP;
69 		VCPU_EVENT(vcpu, 3, "%s", "cpu stopped");
70 		rc = -EOPNOTSUPP;
71 	}
72 
73 	if (vcpu->arch.local_int.action_bits & ACTION_STORE_ON_STOP) {
74 		vcpu->arch.local_int.action_bits &= ~ACTION_STORE_ON_STOP;
75 		/* store status must be called unlocked. Since local_int.lock
76 		 * only protects local_int.* and not guest memory we can give
77 		 * up the lock here */
78 		spin_unlock_bh(&vcpu->arch.local_int.lock);
79 		rc = kvm_s390_vcpu_store_status(vcpu,
80 						KVM_S390_STORE_STATUS_NOADDR);
81 		if (rc >= 0)
82 			rc = -EOPNOTSUPP;
83 	} else
84 		spin_unlock_bh(&vcpu->arch.local_int.lock);
85 	return rc;
86 }
87 
88 static int handle_validity(struct kvm_vcpu *vcpu)
89 {
90 	int viwhy = vcpu->arch.sie_block->ipb >> 16;
91 
92 	vcpu->stat.exit_validity++;
93 	trace_kvm_s390_intercept_validity(vcpu, viwhy);
94 	WARN_ONCE(true, "kvm: unhandled validity intercept 0x%x\n", viwhy);
95 	return -EOPNOTSUPP;
96 }
97 
98 static int handle_instruction(struct kvm_vcpu *vcpu)
99 {
100 	intercept_handler_t handler;
101 
102 	vcpu->stat.exit_instruction++;
103 	trace_kvm_s390_intercept_instruction(vcpu,
104 					     vcpu->arch.sie_block->ipa,
105 					     vcpu->arch.sie_block->ipb);
106 	handler = instruction_handlers[vcpu->arch.sie_block->ipa >> 8];
107 	if (handler)
108 		return handler(vcpu);
109 	return -EOPNOTSUPP;
110 }
111 
112 static int handle_prog(struct kvm_vcpu *vcpu)
113 {
114 	vcpu->stat.exit_program_interruption++;
115 
116 	/* Restore ITDB to Program-Interruption TDB in guest memory */
117 	if (IS_TE_ENABLED(vcpu) &&
118 	    !(current->thread.per_flags & PER_FLAG_NO_TE) &&
119 	    IS_ITDB_VALID(vcpu)) {
120 		copy_to_guest(vcpu, TDB_ADDR, vcpu->arch.sie_block->itdba,
121 			      sizeof(struct kvm_s390_itdb));
122 		memset((void *) vcpu->arch.sie_block->itdba, 0,
123 		       sizeof(struct kvm_s390_itdb));
124 	}
125 
126 	trace_kvm_s390_intercept_prog(vcpu, vcpu->arch.sie_block->iprcc);
127 	return kvm_s390_inject_program_int(vcpu, vcpu->arch.sie_block->iprcc);
128 }
129 
130 static int handle_instruction_and_prog(struct kvm_vcpu *vcpu)
131 {
132 	int rc, rc2;
133 
134 	vcpu->stat.exit_instr_and_program++;
135 	rc = handle_instruction(vcpu);
136 	rc2 = handle_prog(vcpu);
137 
138 	if (rc == -EOPNOTSUPP)
139 		vcpu->arch.sie_block->icptcode = 0x04;
140 	if (rc)
141 		return rc;
142 	return rc2;
143 }
144 
145 static const intercept_handler_t intercept_funcs[] = {
146 	[0x00 >> 2] = handle_noop,
147 	[0x04 >> 2] = handle_instruction,
148 	[0x08 >> 2] = handle_prog,
149 	[0x0C >> 2] = handle_instruction_and_prog,
150 	[0x10 >> 2] = handle_noop,
151 	[0x14 >> 2] = handle_noop,
152 	[0x18 >> 2] = handle_noop,
153 	[0x1C >> 2] = kvm_s390_handle_wait,
154 	[0x20 >> 2] = handle_validity,
155 	[0x28 >> 2] = handle_stop,
156 };
157 
158 int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu)
159 {
160 	intercept_handler_t func;
161 	u8 code = vcpu->arch.sie_block->icptcode;
162 
163 	if (code & 3 || (code >> 2) >= ARRAY_SIZE(intercept_funcs))
164 		return -EOPNOTSUPP;
165 	func = intercept_funcs[code >> 2];
166 	if (func)
167 		return func(vcpu);
168 	return -EOPNOTSUPP;
169 }
170