]>
Commit | Line | Data |
---|---|---|
5288fbf0 CB |
1 | /* |
2 | * sigp.c - handlinge interprocessor communication | |
3 | * | |
9ace903d | 4 | * Copyright IBM Corp. 2008,2009 |
5288fbf0 CB |
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> | |
9ace903d | 12 | * Christian Ehrhardt <ehrhardt@de.ibm.com> |
5288fbf0 CB |
13 | */ |
14 | ||
15 | #include <linux/kvm.h> | |
16 | #include <linux/kvm_host.h> | |
5a0e3ad6 | 17 | #include <linux/slab.h> |
a9ae32c3 | 18 | #include <asm/sigp.h> |
5288fbf0 CB |
19 | #include "gaccess.h" |
20 | #include "kvm-s390.h" | |
21 | ||
0096369d | 22 | static int __sigp_sense(struct kvm_vcpu *vcpu, u16 cpu_addr, |
5a32c1af | 23 | u64 *reg) |
5288fbf0 | 24 | { |
180c12fb | 25 | struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; |
5288fbf0 CB |
26 | int rc; |
27 | ||
28 | if (cpu_addr >= KVM_MAX_VCPUS) | |
29 | return 3; /* not operational */ | |
30 | ||
b037a4f3 | 31 | spin_lock(&fi->lock); |
5288fbf0 CB |
32 | if (fi->local_int[cpu_addr] == NULL) |
33 | rc = 3; /* not operational */ | |
9e6dabef CH |
34 | else if (!(atomic_read(fi->local_int[cpu_addr]->cpuflags) |
35 | & CPUSTAT_STOPPED)) { | |
5288fbf0 CB |
36 | *reg &= 0xffffffff00000000UL; |
37 | rc = 1; /* status stored */ | |
38 | } else { | |
39 | *reg &= 0xffffffff00000000UL; | |
a9ae32c3 | 40 | *reg |= SIGP_STATUS_STOPPED; |
5288fbf0 CB |
41 | rc = 1; /* status stored */ |
42 | } | |
b037a4f3 | 43 | spin_unlock(&fi->lock); |
5288fbf0 CB |
44 | |
45 | VCPU_EVENT(vcpu, 4, "sensed status of cpu %x rc %x", cpu_addr, rc); | |
46 | return rc; | |
47 | } | |
48 | ||
49 | static int __sigp_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr) | |
50 | { | |
180c12fb CB |
51 | struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; |
52 | struct kvm_s390_local_interrupt *li; | |
53 | struct kvm_s390_interrupt_info *inti; | |
5288fbf0 CB |
54 | int rc; |
55 | ||
56 | if (cpu_addr >= KVM_MAX_VCPUS) | |
57 | return 3; /* not operational */ | |
58 | ||
59 | inti = kzalloc(sizeof(*inti), GFP_KERNEL); | |
60 | if (!inti) | |
61 | return -ENOMEM; | |
62 | ||
63 | inti->type = KVM_S390_INT_EMERGENCY; | |
7697e71f | 64 | inti->emerg.code = vcpu->vcpu_id; |
5288fbf0 | 65 | |
b037a4f3 | 66 | spin_lock(&fi->lock); |
5288fbf0 CB |
67 | li = fi->local_int[cpu_addr]; |
68 | if (li == NULL) { | |
69 | rc = 3; /* not operational */ | |
70 | kfree(inti); | |
71 | goto unlock; | |
72 | } | |
73 | spin_lock_bh(&li->lock); | |
74 | list_add_tail(&inti->list, &li->list); | |
75 | atomic_set(&li->active, 1); | |
76 | atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags); | |
77 | if (waitqueue_active(&li->wq)) | |
78 | wake_up_interruptible(&li->wq); | |
79 | spin_unlock_bh(&li->lock); | |
80 | rc = 0; /* order accepted */ | |
7697e71f CE |
81 | VCPU_EVENT(vcpu, 4, "sent sigp emerg to cpu %x", cpu_addr); |
82 | unlock: | |
83 | spin_unlock(&fi->lock); | |
84 | return rc; | |
85 | } | |
86 | ||
87 | static int __sigp_external_call(struct kvm_vcpu *vcpu, u16 cpu_addr) | |
88 | { | |
89 | struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; | |
90 | struct kvm_s390_local_interrupt *li; | |
91 | struct kvm_s390_interrupt_info *inti; | |
92 | int rc; | |
93 | ||
94 | if (cpu_addr >= KVM_MAX_VCPUS) | |
95 | return 3; /* not operational */ | |
96 | ||
97 | inti = kzalloc(sizeof(*inti), GFP_KERNEL); | |
98 | if (!inti) | |
99 | return -ENOMEM; | |
100 | ||
101 | inti->type = KVM_S390_INT_EXTERNAL_CALL; | |
102 | inti->extcall.code = vcpu->vcpu_id; | |
103 | ||
104 | spin_lock(&fi->lock); | |
105 | li = fi->local_int[cpu_addr]; | |
106 | if (li == NULL) { | |
107 | rc = 3; /* not operational */ | |
108 | kfree(inti); | |
109 | goto unlock; | |
110 | } | |
111 | spin_lock_bh(&li->lock); | |
112 | list_add_tail(&inti->list, &li->list); | |
113 | atomic_set(&li->active, 1); | |
114 | atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags); | |
115 | if (waitqueue_active(&li->wq)) | |
116 | wake_up_interruptible(&li->wq); | |
117 | spin_unlock_bh(&li->lock); | |
118 | rc = 0; /* order accepted */ | |
119 | VCPU_EVENT(vcpu, 4, "sent sigp ext call to cpu %x", cpu_addr); | |
5288fbf0 | 120 | unlock: |
b037a4f3 | 121 | spin_unlock(&fi->lock); |
5288fbf0 CB |
122 | return rc; |
123 | } | |
124 | ||
9ace903d | 125 | static int __inject_sigp_stop(struct kvm_s390_local_interrupt *li, int action) |
5288fbf0 | 126 | { |
180c12fb | 127 | struct kvm_s390_interrupt_info *inti; |
5288fbf0 | 128 | |
9940fa80 | 129 | inti = kzalloc(sizeof(*inti), GFP_ATOMIC); |
5288fbf0 CB |
130 | if (!inti) |
131 | return -ENOMEM; | |
5288fbf0 CB |
132 | inti->type = KVM_S390_SIGP_STOP; |
133 | ||
5288fbf0 | 134 | spin_lock_bh(&li->lock); |
24a13044 JF |
135 | if ((atomic_read(li->cpuflags) & CPUSTAT_STOPPED)) |
136 | goto out; | |
5288fbf0 CB |
137 | list_add_tail(&inti->list, &li->list); |
138 | atomic_set(&li->active, 1); | |
139 | atomic_set_mask(CPUSTAT_STOP_INT, li->cpuflags); | |
9ace903d | 140 | li->action_bits |= action; |
5288fbf0 CB |
141 | if (waitqueue_active(&li->wq)) |
142 | wake_up_interruptible(&li->wq); | |
24a13044 | 143 | out: |
5288fbf0 | 144 | spin_unlock_bh(&li->lock); |
9ace903d CE |
145 | |
146 | return 0; /* order accepted */ | |
147 | } | |
148 | ||
149 | static int __sigp_stop(struct kvm_vcpu *vcpu, u16 cpu_addr, int action) | |
150 | { | |
151 | struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; | |
152 | struct kvm_s390_local_interrupt *li; | |
153 | int rc; | |
154 | ||
155 | if (cpu_addr >= KVM_MAX_VCPUS) | |
156 | return 3; /* not operational */ | |
157 | ||
158 | spin_lock(&fi->lock); | |
159 | li = fi->local_int[cpu_addr]; | |
160 | if (li == NULL) { | |
161 | rc = 3; /* not operational */ | |
162 | goto unlock; | |
163 | } | |
164 | ||
165 | rc = __inject_sigp_stop(li, action); | |
166 | ||
5288fbf0 | 167 | unlock: |
b037a4f3 | 168 | spin_unlock(&fi->lock); |
5288fbf0 CB |
169 | VCPU_EVENT(vcpu, 4, "sent sigp stop to cpu %x", cpu_addr); |
170 | return rc; | |
171 | } | |
172 | ||
9ace903d CE |
173 | int kvm_s390_inject_sigp_stop(struct kvm_vcpu *vcpu, int action) |
174 | { | |
175 | struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; | |
176 | return __inject_sigp_stop(li, action); | |
177 | } | |
178 | ||
5288fbf0 CB |
179 | static int __sigp_set_arch(struct kvm_vcpu *vcpu, u32 parameter) |
180 | { | |
181 | int rc; | |
182 | ||
183 | switch (parameter & 0xff) { | |
184 | case 0: | |
5288fbf0 CB |
185 | rc = 3; /* not operational */ |
186 | break; | |
187 | case 1: | |
188 | case 2: | |
189 | rc = 0; /* order accepted */ | |
190 | break; | |
191 | default: | |
b8e660b8 | 192 | rc = -EOPNOTSUPP; |
5288fbf0 CB |
193 | } |
194 | return rc; | |
195 | } | |
196 | ||
197 | static int __sigp_set_prefix(struct kvm_vcpu *vcpu, u16 cpu_addr, u32 address, | |
5a32c1af | 198 | u64 *reg) |
5288fbf0 | 199 | { |
180c12fb | 200 | struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; |
53cb780a | 201 | struct kvm_s390_local_interrupt *li = NULL; |
180c12fb | 202 | struct kvm_s390_interrupt_info *inti; |
5288fbf0 CB |
203 | int rc; |
204 | u8 tmp; | |
205 | ||
206 | /* make sure that the new value is valid memory */ | |
207 | address = address & 0x7fffe000u; | |
092670cd CO |
208 | if (copy_from_guest_absolute(vcpu, &tmp, address, 1) || |
209 | copy_from_guest_absolute(vcpu, &tmp, address + PAGE_SIZE, 1)) { | |
a9ae32c3 | 210 | *reg |= SIGP_STATUS_INVALID_PARAMETER; |
5288fbf0 CB |
211 | return 1; /* invalid parameter */ |
212 | } | |
213 | ||
214 | inti = kzalloc(sizeof(*inti), GFP_KERNEL); | |
215 | if (!inti) | |
216 | return 2; /* busy */ | |
217 | ||
b037a4f3 | 218 | spin_lock(&fi->lock); |
53cb780a RK |
219 | if (cpu_addr < KVM_MAX_VCPUS) |
220 | li = fi->local_int[cpu_addr]; | |
5288fbf0 | 221 | |
53cb780a | 222 | if (li == NULL) { |
5288fbf0 | 223 | rc = 1; /* incorrect state */ |
a9ae32c3 | 224 | *reg &= SIGP_STATUS_INCORRECT_STATE; |
5288fbf0 CB |
225 | kfree(inti); |
226 | goto out_fi; | |
227 | } | |
228 | ||
229 | spin_lock_bh(&li->lock); | |
230 | /* cpu must be in stopped state */ | |
9e6dabef | 231 | if (!(atomic_read(li->cpuflags) & CPUSTAT_STOPPED)) { |
5288fbf0 | 232 | rc = 1; /* incorrect state */ |
a9ae32c3 | 233 | *reg &= SIGP_STATUS_INCORRECT_STATE; |
5288fbf0 CB |
234 | kfree(inti); |
235 | goto out_li; | |
236 | } | |
237 | ||
238 | inti->type = KVM_S390_SIGP_SET_PREFIX; | |
239 | inti->prefix.address = address; | |
240 | ||
241 | list_add_tail(&inti->list, &li->list); | |
242 | atomic_set(&li->active, 1); | |
243 | if (waitqueue_active(&li->wq)) | |
244 | wake_up_interruptible(&li->wq); | |
245 | rc = 0; /* order accepted */ | |
246 | ||
247 | VCPU_EVENT(vcpu, 4, "set prefix of cpu %02x to %x", cpu_addr, address); | |
248 | out_li: | |
249 | spin_unlock_bh(&li->lock); | |
250 | out_fi: | |
b037a4f3 | 251 | spin_unlock(&fi->lock); |
5288fbf0 CB |
252 | return rc; |
253 | } | |
254 | ||
bd59d3a4 | 255 | static int __sigp_sense_running(struct kvm_vcpu *vcpu, u16 cpu_addr, |
5a32c1af | 256 | u64 *reg) |
bd59d3a4 CH |
257 | { |
258 | int rc; | |
259 | struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; | |
260 | ||
261 | if (cpu_addr >= KVM_MAX_VCPUS) | |
262 | return 3; /* not operational */ | |
263 | ||
264 | spin_lock(&fi->lock); | |
265 | if (fi->local_int[cpu_addr] == NULL) | |
266 | rc = 3; /* not operational */ | |
267 | else { | |
268 | if (atomic_read(fi->local_int[cpu_addr]->cpuflags) | |
269 | & CPUSTAT_RUNNING) { | |
270 | /* running */ | |
271 | rc = 1; | |
272 | } else { | |
273 | /* not running */ | |
274 | *reg &= 0xffffffff00000000UL; | |
a9ae32c3 | 275 | *reg |= SIGP_STATUS_NOT_RUNNING; |
bd59d3a4 CH |
276 | rc = 0; |
277 | } | |
278 | } | |
279 | spin_unlock(&fi->lock); | |
280 | ||
281 | VCPU_EVENT(vcpu, 4, "sensed running status of cpu %x rc %x", cpu_addr, | |
282 | rc); | |
283 | ||
284 | return rc; | |
285 | } | |
286 | ||
151104a7 JF |
287 | static int __sigp_restart(struct kvm_vcpu *vcpu, u16 cpu_addr) |
288 | { | |
289 | int rc = 0; | |
290 | struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; | |
291 | struct kvm_s390_local_interrupt *li; | |
292 | ||
293 | if (cpu_addr >= KVM_MAX_VCPUS) | |
294 | return 3; /* not operational */ | |
295 | ||
296 | spin_lock(&fi->lock); | |
297 | li = fi->local_int[cpu_addr]; | |
298 | if (li == NULL) { | |
299 | rc = 3; /* not operational */ | |
300 | goto out; | |
301 | } | |
302 | ||
303 | spin_lock_bh(&li->lock); | |
304 | if (li->action_bits & ACTION_STOP_ON_STOP) | |
305 | rc = 2; /* busy */ | |
306 | else | |
307 | VCPU_EVENT(vcpu, 4, "sigp restart %x to handle userspace", | |
308 | cpu_addr); | |
309 | spin_unlock_bh(&li->lock); | |
310 | out: | |
311 | spin_unlock(&fi->lock); | |
312 | return rc; | |
313 | } | |
314 | ||
5288fbf0 CB |
315 | int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu) |
316 | { | |
317 | int r1 = (vcpu->arch.sie_block->ipa & 0x00f0) >> 4; | |
318 | int r3 = vcpu->arch.sie_block->ipa & 0x000f; | |
319 | int base2 = vcpu->arch.sie_block->ipb >> 28; | |
320 | int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16); | |
321 | u32 parameter; | |
5a32c1af | 322 | u16 cpu_addr = vcpu->run->s.regs.gprs[r3]; |
5288fbf0 CB |
323 | u8 order_code; |
324 | int rc; | |
325 | ||
3eb77d51 CB |
326 | /* sigp in userspace can exit */ |
327 | if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) | |
328 | return kvm_s390_inject_program_int(vcpu, | |
329 | PGM_PRIVILEGED_OPERATION); | |
330 | ||
5288fbf0 CB |
331 | order_code = disp2; |
332 | if (base2) | |
5a32c1af | 333 | order_code += vcpu->run->s.regs.gprs[base2]; |
5288fbf0 CB |
334 | |
335 | if (r1 % 2) | |
5a32c1af | 336 | parameter = vcpu->run->s.regs.gprs[r1]; |
5288fbf0 | 337 | else |
5a32c1af | 338 | parameter = vcpu->run->s.regs.gprs[r1 + 1]; |
5288fbf0 CB |
339 | |
340 | switch (order_code) { | |
341 | case SIGP_SENSE: | |
342 | vcpu->stat.instruction_sigp_sense++; | |
343 | rc = __sigp_sense(vcpu, cpu_addr, | |
5a32c1af | 344 | &vcpu->run->s.regs.gprs[r1]); |
5288fbf0 | 345 | break; |
7697e71f CE |
346 | case SIGP_EXTERNAL_CALL: |
347 | vcpu->stat.instruction_sigp_external_call++; | |
348 | rc = __sigp_external_call(vcpu, cpu_addr); | |
349 | break; | |
a9ae32c3 | 350 | case SIGP_EMERGENCY_SIGNAL: |
5288fbf0 CB |
351 | vcpu->stat.instruction_sigp_emergency++; |
352 | rc = __sigp_emergency(vcpu, cpu_addr); | |
353 | break; | |
354 | case SIGP_STOP: | |
355 | vcpu->stat.instruction_sigp_stop++; | |
9ace903d | 356 | rc = __sigp_stop(vcpu, cpu_addr, ACTION_STOP_ON_STOP); |
5288fbf0 | 357 | break; |
a9ae32c3 | 358 | case SIGP_STOP_AND_STORE_STATUS: |
5288fbf0 | 359 | vcpu->stat.instruction_sigp_stop++; |
9ec2d6dc JF |
360 | rc = __sigp_stop(vcpu, cpu_addr, ACTION_STORE_ON_STOP | |
361 | ACTION_STOP_ON_STOP); | |
5288fbf0 | 362 | break; |
a9ae32c3 | 363 | case SIGP_SET_ARCHITECTURE: |
5288fbf0 CB |
364 | vcpu->stat.instruction_sigp_arch++; |
365 | rc = __sigp_set_arch(vcpu, parameter); | |
366 | break; | |
367 | case SIGP_SET_PREFIX: | |
368 | vcpu->stat.instruction_sigp_prefix++; | |
369 | rc = __sigp_set_prefix(vcpu, cpu_addr, parameter, | |
5a32c1af | 370 | &vcpu->run->s.regs.gprs[r1]); |
5288fbf0 | 371 | break; |
bd59d3a4 CH |
372 | case SIGP_SENSE_RUNNING: |
373 | vcpu->stat.instruction_sigp_sense_running++; | |
374 | rc = __sigp_sense_running(vcpu, cpu_addr, | |
5a32c1af | 375 | &vcpu->run->s.regs.gprs[r1]); |
bd59d3a4 | 376 | break; |
5288fbf0 CB |
377 | case SIGP_RESTART: |
378 | vcpu->stat.instruction_sigp_restart++; | |
151104a7 JF |
379 | rc = __sigp_restart(vcpu, cpu_addr); |
380 | if (rc == 2) /* busy */ | |
381 | break; | |
5288fbf0 CB |
382 | /* user space must know about restart */ |
383 | default: | |
b8e660b8 | 384 | return -EOPNOTSUPP; |
5288fbf0 CB |
385 | } |
386 | ||
387 | if (rc < 0) | |
388 | return rc; | |
389 | ||
390 | vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44); | |
391 | vcpu->arch.sie_block->gpsw.mask |= (rc & 3ul) << 44; | |
392 | return 0; | |
393 | } |