2 * in-kernel handling for sie intercepts
4 * Copyright IBM Corp. 2008, 2009
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.
10 * Author(s): Carsten Otte <cotte@de.ibm.com>
11 * Christian Borntraeger <borntraeger@de.ibm.com>
14 #include <linux/kvm_host.h>
15 #include <linux/errno.h>
16 #include <linux/pagemap.h>
18 #include <asm/kvm_host.h>
23 #include "trace-s390.h"
25 static int handle_lctlg(struct kvm_vcpu
*vcpu
)
27 int reg1
= (vcpu
->arch
.sie_block
->ipa
& 0x00f0) >> 4;
28 int reg3
= vcpu
->arch
.sie_block
->ipa
& 0x000f;
32 vcpu
->stat
.instruction_lctlg
++;
34 useraddr
= kvm_s390_get_base_disp_rsy(vcpu
);
37 return kvm_s390_inject_program_int(vcpu
, PGM_SPECIFICATION
);
41 VCPU_EVENT(vcpu
, 5, "lctlg r1:%x, r3:%x, addr:%llx", reg1
, reg3
,
43 trace_kvm_s390_handle_lctl(vcpu
, 1, reg1
, reg3
, useraddr
);
46 rc
= get_guest(vcpu
, vcpu
->arch
.sie_block
->gcr
[reg
],
47 (u64 __user
*) useraddr
);
49 return kvm_s390_inject_program_int(vcpu
, PGM_ADDRESSING
);
58 static int handle_lctl(struct kvm_vcpu
*vcpu
)
60 int reg1
= (vcpu
->arch
.sie_block
->ipa
& 0x00f0) >> 4;
61 int reg3
= vcpu
->arch
.sie_block
->ipa
& 0x000f;
66 vcpu
->stat
.instruction_lctl
++;
68 useraddr
= kvm_s390_get_base_disp_rs(vcpu
);
71 return kvm_s390_inject_program_int(vcpu
, PGM_SPECIFICATION
);
73 VCPU_EVENT(vcpu
, 5, "lctl r1:%x, r3:%x, addr:%llx", reg1
, reg3
,
75 trace_kvm_s390_handle_lctl(vcpu
, 0, reg1
, reg3
, useraddr
);
79 rc
= get_guest(vcpu
, val
, (u32 __user
*) useraddr
);
81 return kvm_s390_inject_program_int(vcpu
, PGM_ADDRESSING
);
82 vcpu
->arch
.sie_block
->gcr
[reg
] &= 0xffffffff00000000ul
;
83 vcpu
->arch
.sie_block
->gcr
[reg
] |= val
;
92 static const intercept_handler_t eb_handlers
[256] = {
93 [0x2f] = handle_lctlg
,
94 [0x8a] = kvm_s390_handle_priv_eb
,
97 static int handle_eb(struct kvm_vcpu
*vcpu
)
99 intercept_handler_t handler
;
101 handler
= eb_handlers
[vcpu
->arch
.sie_block
->ipb
& 0xff];
103 return handler(vcpu
);
107 static const intercept_handler_t instruction_handlers
[256] = {
108 [0x01] = kvm_s390_handle_01
,
109 [0x82] = kvm_s390_handle_lpsw
,
110 [0x83] = kvm_s390_handle_diag
,
111 [0xae] = kvm_s390_handle_sigp
,
112 [0xb2] = kvm_s390_handle_b2
,
113 [0xb7] = handle_lctl
,
114 [0xb9] = kvm_s390_handle_b9
,
115 [0xe5] = kvm_s390_handle_e5
,
119 static int handle_noop(struct kvm_vcpu
*vcpu
)
121 switch (vcpu
->arch
.sie_block
->icptcode
) {
123 vcpu
->stat
.exit_null
++;
126 vcpu
->stat
.exit_external_request
++;
129 vcpu
->stat
.exit_external_interrupt
++;
137 static int handle_stop(struct kvm_vcpu
*vcpu
)
141 vcpu
->stat
.exit_stop_request
++;
142 spin_lock_bh(&vcpu
->arch
.local_int
.lock
);
144 trace_kvm_s390_stop_request(vcpu
->arch
.local_int
.action_bits
);
146 if (vcpu
->arch
.local_int
.action_bits
& ACTION_RELOADVCPU_ON_STOP
) {
147 vcpu
->arch
.local_int
.action_bits
&= ~ACTION_RELOADVCPU_ON_STOP
;
148 rc
= SIE_INTERCEPT_RERUNVCPU
;
149 vcpu
->run
->exit_reason
= KVM_EXIT_INTR
;
152 if (vcpu
->arch
.local_int
.action_bits
& ACTION_STOP_ON_STOP
) {
153 atomic_set_mask(CPUSTAT_STOPPED
,
154 &vcpu
->arch
.sie_block
->cpuflags
);
155 vcpu
->arch
.local_int
.action_bits
&= ~ACTION_STOP_ON_STOP
;
156 VCPU_EVENT(vcpu
, 3, "%s", "cpu stopped");
160 if (vcpu
->arch
.local_int
.action_bits
& ACTION_STORE_ON_STOP
) {
161 vcpu
->arch
.local_int
.action_bits
&= ~ACTION_STORE_ON_STOP
;
162 /* store status must be called unlocked. Since local_int.lock
163 * only protects local_int.* and not guest memory we can give
164 * up the lock here */
165 spin_unlock_bh(&vcpu
->arch
.local_int
.lock
);
166 rc
= kvm_s390_vcpu_store_status(vcpu
,
167 KVM_S390_STORE_STATUS_NOADDR
);
171 spin_unlock_bh(&vcpu
->arch
.local_int
.lock
);
175 static int handle_validity(struct kvm_vcpu
*vcpu
)
177 unsigned long vmaddr
;
178 int viwhy
= vcpu
->arch
.sie_block
->ipb
>> 16;
181 vcpu
->stat
.exit_validity
++;
182 trace_kvm_s390_intercept_validity(vcpu
, viwhy
);
184 vmaddr
= gmap_fault(vcpu
->arch
.sie_block
->prefix
,
186 if (IS_ERR_VALUE(vmaddr
)) {
190 rc
= fault_in_pages_writeable((char __user
*) vmaddr
,
193 /* user will receive sigsegv, exit to user */
197 vmaddr
= gmap_fault(vcpu
->arch
.sie_block
->prefix
+ PAGE_SIZE
,
199 if (IS_ERR_VALUE(vmaddr
)) {
203 rc
= fault_in_pages_writeable((char __user
*) vmaddr
,
206 /* user will receive sigsegv, exit to user */
215 VCPU_EVENT(vcpu
, 2, "unhandled validity intercept code %d",
220 static int handle_instruction(struct kvm_vcpu
*vcpu
)
222 intercept_handler_t handler
;
224 vcpu
->stat
.exit_instruction
++;
225 trace_kvm_s390_intercept_instruction(vcpu
,
226 vcpu
->arch
.sie_block
->ipa
,
227 vcpu
->arch
.sie_block
->ipb
);
228 handler
= instruction_handlers
[vcpu
->arch
.sie_block
->ipa
>> 8];
230 return handler(vcpu
);
234 static int handle_prog(struct kvm_vcpu
*vcpu
)
236 vcpu
->stat
.exit_program_interruption
++;
237 trace_kvm_s390_intercept_prog(vcpu
, vcpu
->arch
.sie_block
->iprcc
);
238 return kvm_s390_inject_program_int(vcpu
, vcpu
->arch
.sie_block
->iprcc
);
241 static int handle_instruction_and_prog(struct kvm_vcpu
*vcpu
)
245 vcpu
->stat
.exit_instr_and_program
++;
246 rc
= handle_instruction(vcpu
);
247 rc2
= handle_prog(vcpu
);
249 if (rc
== -EOPNOTSUPP
)
250 vcpu
->arch
.sie_block
->icptcode
= 0x04;
256 static const intercept_handler_t intercept_funcs
[] = {
257 [0x00 >> 2] = handle_noop
,
258 [0x04 >> 2] = handle_instruction
,
259 [0x08 >> 2] = handle_prog
,
260 [0x0C >> 2] = handle_instruction_and_prog
,
261 [0x10 >> 2] = handle_noop
,
262 [0x14 >> 2] = handle_noop
,
263 [0x18 >> 2] = handle_noop
,
264 [0x1C >> 2] = kvm_s390_handle_wait
,
265 [0x20 >> 2] = handle_validity
,
266 [0x28 >> 2] = handle_stop
,
269 int kvm_handle_sie_intercept(struct kvm_vcpu
*vcpu
)
271 intercept_handler_t func
;
272 u8 code
= vcpu
->arch
.sie_block
->icptcode
;
274 if (code
& 3 || (code
>> 2) >= ARRAY_SIZE(intercept_funcs
))
276 func
= intercept_funcs
[code
>> 2];