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;
29 int base2
= vcpu
->arch
.sie_block
->ipb
>> 28;
30 int disp2
= ((vcpu
->arch
.sie_block
->ipb
& 0x0fff0000) >> 16) +
31 ((vcpu
->arch
.sie_block
->ipb
& 0xff00) << 4);
35 vcpu
->stat
.instruction_lctlg
++;
36 if ((vcpu
->arch
.sie_block
->ipb
& 0xff) != 0x2f)
41 useraddr
+= vcpu
->run
->s
.regs
.gprs
[base2
];
44 return kvm_s390_inject_program_int(vcpu
, PGM_SPECIFICATION
);
48 VCPU_EVENT(vcpu
, 5, "lctlg r1:%x, r3:%x,b2:%x,d2:%x", reg1
, reg3
, base2
,
50 trace_kvm_s390_handle_lctl(vcpu
, 1, reg1
, reg3
, useraddr
);
53 rc
= get_guest_u64(vcpu
, useraddr
,
54 &vcpu
->arch
.sie_block
->gcr
[reg
]);
56 kvm_s390_inject_program_int(vcpu
, PGM_ADDRESSING
);
67 static int handle_lctl(struct kvm_vcpu
*vcpu
)
69 int reg1
= (vcpu
->arch
.sie_block
->ipa
& 0x00f0) >> 4;
70 int reg3
= vcpu
->arch
.sie_block
->ipa
& 0x000f;
71 int base2
= vcpu
->arch
.sie_block
->ipb
>> 28;
72 int disp2
= ((vcpu
->arch
.sie_block
->ipb
& 0x0fff0000) >> 16);
77 vcpu
->stat
.instruction_lctl
++;
81 useraddr
+= vcpu
->run
->s
.regs
.gprs
[base2
];
84 return kvm_s390_inject_program_int(vcpu
, PGM_SPECIFICATION
);
86 VCPU_EVENT(vcpu
, 5, "lctl r1:%x, r3:%x,b2:%x,d2:%x", reg1
, reg3
, base2
,
88 trace_kvm_s390_handle_lctl(vcpu
, 0, reg1
, reg3
, useraddr
);
92 rc
= get_guest_u32(vcpu
, useraddr
, &val
);
94 kvm_s390_inject_program_int(vcpu
, PGM_ADDRESSING
);
97 vcpu
->arch
.sie_block
->gcr
[reg
] &= 0xffffffff00000000ul
;
98 vcpu
->arch
.sie_block
->gcr
[reg
] |= val
;
102 reg
= (reg
+ 1) % 16;
107 static intercept_handler_t instruction_handlers
[256] = {
108 [0x01] = kvm_s390_handle_01
,
109 [0x83] = kvm_s390_handle_diag
,
110 [0xae] = kvm_s390_handle_sigp
,
111 [0xb2] = kvm_s390_handle_b2
,
112 [0xb7] = handle_lctl
,
113 [0xe5] = kvm_s390_handle_e5
,
114 [0xeb] = handle_lctlg
,
117 static int handle_noop(struct kvm_vcpu
*vcpu
)
119 switch (vcpu
->arch
.sie_block
->icptcode
) {
121 vcpu
->stat
.exit_null
++;
124 vcpu
->stat
.exit_external_request
++;
127 vcpu
->stat
.exit_external_interrupt
++;
135 static int handle_stop(struct kvm_vcpu
*vcpu
)
139 vcpu
->stat
.exit_stop_request
++;
140 spin_lock_bh(&vcpu
->arch
.local_int
.lock
);
142 trace_kvm_s390_stop_request(vcpu
->arch
.local_int
.action_bits
);
144 if (vcpu
->arch
.local_int
.action_bits
& ACTION_RELOADVCPU_ON_STOP
) {
145 vcpu
->arch
.local_int
.action_bits
&= ~ACTION_RELOADVCPU_ON_STOP
;
146 rc
= SIE_INTERCEPT_RERUNVCPU
;
147 vcpu
->run
->exit_reason
= KVM_EXIT_INTR
;
150 if (vcpu
->arch
.local_int
.action_bits
& ACTION_STOP_ON_STOP
) {
151 atomic_set_mask(CPUSTAT_STOPPED
,
152 &vcpu
->arch
.sie_block
->cpuflags
);
153 vcpu
->arch
.local_int
.action_bits
&= ~ACTION_STOP_ON_STOP
;
154 VCPU_EVENT(vcpu
, 3, "%s", "cpu stopped");
158 if (vcpu
->arch
.local_int
.action_bits
& ACTION_STORE_ON_STOP
) {
159 vcpu
->arch
.local_int
.action_bits
&= ~ACTION_STORE_ON_STOP
;
160 /* store status must be called unlocked. Since local_int.lock
161 * only protects local_int.* and not guest memory we can give
162 * up the lock here */
163 spin_unlock_bh(&vcpu
->arch
.local_int
.lock
);
164 rc
= kvm_s390_vcpu_store_status(vcpu
,
165 KVM_S390_STORE_STATUS_NOADDR
);
169 spin_unlock_bh(&vcpu
->arch
.local_int
.lock
);
173 static int handle_validity(struct kvm_vcpu
*vcpu
)
175 unsigned long vmaddr
;
176 int viwhy
= vcpu
->arch
.sie_block
->ipb
>> 16;
179 vcpu
->stat
.exit_validity
++;
180 trace_kvm_s390_intercept_validity(vcpu
, viwhy
);
182 vmaddr
= gmap_fault(vcpu
->arch
.sie_block
->prefix
,
184 if (IS_ERR_VALUE(vmaddr
)) {
188 rc
= fault_in_pages_writeable((char __user
*) vmaddr
,
191 /* user will receive sigsegv, exit to user */
195 vmaddr
= gmap_fault(vcpu
->arch
.sie_block
->prefix
+ PAGE_SIZE
,
197 if (IS_ERR_VALUE(vmaddr
)) {
201 rc
= fault_in_pages_writeable((char __user
*) vmaddr
,
204 /* user will receive sigsegv, exit to user */
213 VCPU_EVENT(vcpu
, 2, "unhandled validity intercept code %d",
218 static int handle_instruction(struct kvm_vcpu
*vcpu
)
220 intercept_handler_t handler
;
222 vcpu
->stat
.exit_instruction
++;
223 trace_kvm_s390_intercept_instruction(vcpu
,
224 vcpu
->arch
.sie_block
->ipa
,
225 vcpu
->arch
.sie_block
->ipb
);
226 handler
= instruction_handlers
[vcpu
->arch
.sie_block
->ipa
>> 8];
228 return handler(vcpu
);
232 static int handle_prog(struct kvm_vcpu
*vcpu
)
234 vcpu
->stat
.exit_program_interruption
++;
235 trace_kvm_s390_intercept_prog(vcpu
, vcpu
->arch
.sie_block
->iprcc
);
236 return kvm_s390_inject_program_int(vcpu
, vcpu
->arch
.sie_block
->iprcc
);
239 static int handle_instruction_and_prog(struct kvm_vcpu
*vcpu
)
243 vcpu
->stat
.exit_instr_and_program
++;
244 rc
= handle_instruction(vcpu
);
245 rc2
= handle_prog(vcpu
);
247 if (rc
== -EOPNOTSUPP
)
248 vcpu
->arch
.sie_block
->icptcode
= 0x04;
254 static const intercept_handler_t intercept_funcs
[] = {
255 [0x00 >> 2] = handle_noop
,
256 [0x04 >> 2] = handle_instruction
,
257 [0x08 >> 2] = handle_prog
,
258 [0x0C >> 2] = handle_instruction_and_prog
,
259 [0x10 >> 2] = handle_noop
,
260 [0x14 >> 2] = handle_noop
,
261 [0x1C >> 2] = kvm_s390_handle_wait
,
262 [0x20 >> 2] = handle_validity
,
263 [0x28 >> 2] = handle_stop
,
266 int kvm_handle_sie_intercept(struct kvm_vcpu
*vcpu
)
268 intercept_handler_t func
;
269 u8 code
= vcpu
->arch
.sie_block
->icptcode
;
271 if (code
& 3 || (code
>> 2) >= ARRAY_SIZE(intercept_funcs
))
273 func
= intercept_funcs
[code
>> 2];