diff options
Diffstat (limited to 'arch/s390/kvm/sigp.c')
-rw-r--r-- | arch/s390/kvm/sigp.c | 120 |
1 files changed, 102 insertions, 18 deletions
diff --git a/arch/s390/kvm/sigp.c b/arch/s390/kvm/sigp.c index bec398c57acf..87c2b3a3bd3e 100644 --- a/arch/s390/kvm/sigp.c +++ b/arch/s390/kvm/sigp.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * handling interprocessor communication | 2 | * handling interprocessor communication |
3 | * | 3 | * |
4 | * Copyright IBM Corp. 2008, 2009 | 4 | * Copyright IBM Corp. 2008, 2013 |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 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) | 7 | * it under the terms of the GNU General Public License (version 2 only) |
@@ -89,6 +89,37 @@ unlock: | |||
89 | return rc; | 89 | return rc; |
90 | } | 90 | } |
91 | 91 | ||
92 | static int __sigp_conditional_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr, | ||
93 | u16 asn, u64 *reg) | ||
94 | { | ||
95 | struct kvm_vcpu *dst_vcpu = NULL; | ||
96 | const u64 psw_int_mask = PSW_MASK_IO | PSW_MASK_EXT; | ||
97 | u16 p_asn, s_asn; | ||
98 | psw_t *psw; | ||
99 | u32 flags; | ||
100 | |||
101 | if (cpu_addr < KVM_MAX_VCPUS) | ||
102 | dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr); | ||
103 | if (!dst_vcpu) | ||
104 | return SIGP_CC_NOT_OPERATIONAL; | ||
105 | flags = atomic_read(&dst_vcpu->arch.sie_block->cpuflags); | ||
106 | psw = &dst_vcpu->arch.sie_block->gpsw; | ||
107 | p_asn = dst_vcpu->arch.sie_block->gcr[4] & 0xffff; /* Primary ASN */ | ||
108 | s_asn = dst_vcpu->arch.sie_block->gcr[3] & 0xffff; /* Secondary ASN */ | ||
109 | |||
110 | /* Deliver the emergency signal? */ | ||
111 | if (!(flags & CPUSTAT_STOPPED) | ||
112 | || (psw->mask & psw_int_mask) != psw_int_mask | ||
113 | || ((flags & CPUSTAT_WAIT) && psw->addr != 0) | ||
114 | || (!(flags & CPUSTAT_WAIT) && (asn == p_asn || asn == s_asn))) { | ||
115 | return __sigp_emergency(vcpu, cpu_addr); | ||
116 | } else { | ||
117 | *reg &= 0xffffffff00000000UL; | ||
118 | *reg |= SIGP_STATUS_INCORRECT_STATE; | ||
119 | return SIGP_CC_STATUS_STORED; | ||
120 | } | ||
121 | } | ||
122 | |||
92 | static int __sigp_external_call(struct kvm_vcpu *vcpu, u16 cpu_addr) | 123 | static int __sigp_external_call(struct kvm_vcpu *vcpu, u16 cpu_addr) |
93 | { | 124 | { |
94 | struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; | 125 | struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; |
@@ -130,6 +161,7 @@ unlock: | |||
130 | static int __inject_sigp_stop(struct kvm_s390_local_interrupt *li, int action) | 161 | static int __inject_sigp_stop(struct kvm_s390_local_interrupt *li, int action) |
131 | { | 162 | { |
132 | struct kvm_s390_interrupt_info *inti; | 163 | struct kvm_s390_interrupt_info *inti; |
164 | int rc = SIGP_CC_ORDER_CODE_ACCEPTED; | ||
133 | 165 | ||
134 | inti = kzalloc(sizeof(*inti), GFP_ATOMIC); | 166 | inti = kzalloc(sizeof(*inti), GFP_ATOMIC); |
135 | if (!inti) | 167 | if (!inti) |
@@ -139,6 +171,8 @@ static int __inject_sigp_stop(struct kvm_s390_local_interrupt *li, int action) | |||
139 | spin_lock_bh(&li->lock); | 171 | spin_lock_bh(&li->lock); |
140 | if ((atomic_read(li->cpuflags) & CPUSTAT_STOPPED)) { | 172 | if ((atomic_read(li->cpuflags) & CPUSTAT_STOPPED)) { |
141 | kfree(inti); | 173 | kfree(inti); |
174 | if ((action & ACTION_STORE_ON_STOP) != 0) | ||
175 | rc = -ESHUTDOWN; | ||
142 | goto out; | 176 | goto out; |
143 | } | 177 | } |
144 | list_add_tail(&inti->list, &li->list); | 178 | list_add_tail(&inti->list, &li->list); |
@@ -150,7 +184,7 @@ static int __inject_sigp_stop(struct kvm_s390_local_interrupt *li, int action) | |||
150 | out: | 184 | out: |
151 | spin_unlock_bh(&li->lock); | 185 | spin_unlock_bh(&li->lock); |
152 | 186 | ||
153 | return SIGP_CC_ORDER_CODE_ACCEPTED; | 187 | return rc; |
154 | } | 188 | } |
155 | 189 | ||
156 | static int __sigp_stop(struct kvm_vcpu *vcpu, u16 cpu_addr, int action) | 190 | static int __sigp_stop(struct kvm_vcpu *vcpu, u16 cpu_addr, int action) |
@@ -174,13 +208,17 @@ static int __sigp_stop(struct kvm_vcpu *vcpu, u16 cpu_addr, int action) | |||
174 | unlock: | 208 | unlock: |
175 | spin_unlock(&fi->lock); | 209 | spin_unlock(&fi->lock); |
176 | VCPU_EVENT(vcpu, 4, "sent sigp stop to cpu %x", cpu_addr); | 210 | VCPU_EVENT(vcpu, 4, "sent sigp stop to cpu %x", cpu_addr); |
177 | return rc; | ||
178 | } | ||
179 | 211 | ||
180 | int kvm_s390_inject_sigp_stop(struct kvm_vcpu *vcpu, int action) | 212 | if ((action & ACTION_STORE_ON_STOP) != 0 && rc == -ESHUTDOWN) { |
181 | { | 213 | /* If the CPU has already been stopped, we still have |
182 | struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; | 214 | * to save the status when doing stop-and-store. This |
183 | return __inject_sigp_stop(li, action); | 215 | * has to be done after unlocking all spinlocks. */ |
216 | struct kvm_vcpu *dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr); | ||
217 | rc = kvm_s390_store_status_unloaded(dst_vcpu, | ||
218 | KVM_S390_STORE_STATUS_NOADDR); | ||
219 | } | ||
220 | |||
221 | return rc; | ||
184 | } | 222 | } |
185 | 223 | ||
186 | static int __sigp_set_arch(struct kvm_vcpu *vcpu, u32 parameter) | 224 | static int __sigp_set_arch(struct kvm_vcpu *vcpu, u32 parameter) |
@@ -262,6 +300,37 @@ out_fi: | |||
262 | return rc; | 300 | return rc; |
263 | } | 301 | } |
264 | 302 | ||
303 | static int __sigp_store_status_at_addr(struct kvm_vcpu *vcpu, u16 cpu_id, | ||
304 | u32 addr, u64 *reg) | ||
305 | { | ||
306 | struct kvm_vcpu *dst_vcpu = NULL; | ||
307 | int flags; | ||
308 | int rc; | ||
309 | |||
310 | if (cpu_id < KVM_MAX_VCPUS) | ||
311 | dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_id); | ||
312 | if (!dst_vcpu) | ||
313 | return SIGP_CC_NOT_OPERATIONAL; | ||
314 | |||
315 | spin_lock_bh(&dst_vcpu->arch.local_int.lock); | ||
316 | flags = atomic_read(dst_vcpu->arch.local_int.cpuflags); | ||
317 | spin_unlock_bh(&dst_vcpu->arch.local_int.lock); | ||
318 | if (!(flags & CPUSTAT_STOPPED)) { | ||
319 | *reg &= 0xffffffff00000000UL; | ||
320 | *reg |= SIGP_STATUS_INCORRECT_STATE; | ||
321 | return SIGP_CC_STATUS_STORED; | ||
322 | } | ||
323 | |||
324 | addr &= 0x7ffffe00; | ||
325 | rc = kvm_s390_store_status_unloaded(dst_vcpu, addr); | ||
326 | if (rc == -EFAULT) { | ||
327 | *reg &= 0xffffffff00000000UL; | ||
328 | *reg |= SIGP_STATUS_INVALID_PARAMETER; | ||
329 | rc = SIGP_CC_STATUS_STORED; | ||
330 | } | ||
331 | return rc; | ||
332 | } | ||
333 | |||
265 | static int __sigp_sense_running(struct kvm_vcpu *vcpu, u16 cpu_addr, | 334 | static int __sigp_sense_running(struct kvm_vcpu *vcpu, u16 cpu_addr, |
266 | u64 *reg) | 335 | u64 *reg) |
267 | { | 336 | { |
@@ -294,7 +363,8 @@ static int __sigp_sense_running(struct kvm_vcpu *vcpu, u16 cpu_addr, | |||
294 | return rc; | 363 | return rc; |
295 | } | 364 | } |
296 | 365 | ||
297 | static int __sigp_restart(struct kvm_vcpu *vcpu, u16 cpu_addr) | 366 | /* Test whether the destination CPU is available and not busy */ |
367 | static int sigp_check_callable(struct kvm_vcpu *vcpu, u16 cpu_addr) | ||
298 | { | 368 | { |
299 | struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; | 369 | struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; |
300 | struct kvm_s390_local_interrupt *li; | 370 | struct kvm_s390_local_interrupt *li; |
@@ -313,9 +383,6 @@ static int __sigp_restart(struct kvm_vcpu *vcpu, u16 cpu_addr) | |||
313 | spin_lock_bh(&li->lock); | 383 | spin_lock_bh(&li->lock); |
314 | if (li->action_bits & ACTION_STOP_ON_STOP) | 384 | if (li->action_bits & ACTION_STOP_ON_STOP) |
315 | rc = SIGP_CC_BUSY; | 385 | rc = SIGP_CC_BUSY; |
316 | else | ||
317 | VCPU_EVENT(vcpu, 4, "sigp restart %x to handle userspace", | ||
318 | cpu_addr); | ||
319 | spin_unlock_bh(&li->lock); | 386 | spin_unlock_bh(&li->lock); |
320 | out: | 387 | out: |
321 | spin_unlock(&fi->lock); | 388 | spin_unlock(&fi->lock); |
@@ -366,6 +433,10 @@ int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu) | |||
366 | rc = __sigp_stop(vcpu, cpu_addr, ACTION_STORE_ON_STOP | | 433 | rc = __sigp_stop(vcpu, cpu_addr, ACTION_STORE_ON_STOP | |
367 | ACTION_STOP_ON_STOP); | 434 | ACTION_STOP_ON_STOP); |
368 | break; | 435 | break; |
436 | case SIGP_STORE_STATUS_AT_ADDRESS: | ||
437 | rc = __sigp_store_status_at_addr(vcpu, cpu_addr, parameter, | ||
438 | &vcpu->run->s.regs.gprs[r1]); | ||
439 | break; | ||
369 | case SIGP_SET_ARCHITECTURE: | 440 | case SIGP_SET_ARCHITECTURE: |
370 | vcpu->stat.instruction_sigp_arch++; | 441 | vcpu->stat.instruction_sigp_arch++; |
371 | rc = __sigp_set_arch(vcpu, parameter); | 442 | rc = __sigp_set_arch(vcpu, parameter); |
@@ -375,17 +446,31 @@ int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu) | |||
375 | rc = __sigp_set_prefix(vcpu, cpu_addr, parameter, | 446 | rc = __sigp_set_prefix(vcpu, cpu_addr, parameter, |
376 | &vcpu->run->s.regs.gprs[r1]); | 447 | &vcpu->run->s.regs.gprs[r1]); |
377 | break; | 448 | break; |
449 | case SIGP_COND_EMERGENCY_SIGNAL: | ||
450 | rc = __sigp_conditional_emergency(vcpu, cpu_addr, parameter, | ||
451 | &vcpu->run->s.regs.gprs[r1]); | ||
452 | break; | ||
378 | case SIGP_SENSE_RUNNING: | 453 | case SIGP_SENSE_RUNNING: |
379 | vcpu->stat.instruction_sigp_sense_running++; | 454 | vcpu->stat.instruction_sigp_sense_running++; |
380 | rc = __sigp_sense_running(vcpu, cpu_addr, | 455 | rc = __sigp_sense_running(vcpu, cpu_addr, |
381 | &vcpu->run->s.regs.gprs[r1]); | 456 | &vcpu->run->s.regs.gprs[r1]); |
382 | break; | 457 | break; |
458 | case SIGP_START: | ||
459 | rc = sigp_check_callable(vcpu, cpu_addr); | ||
460 | if (rc == SIGP_CC_ORDER_CODE_ACCEPTED) | ||
461 | rc = -EOPNOTSUPP; /* Handle START in user space */ | ||
462 | break; | ||
383 | case SIGP_RESTART: | 463 | case SIGP_RESTART: |
384 | vcpu->stat.instruction_sigp_restart++; | 464 | vcpu->stat.instruction_sigp_restart++; |
385 | rc = __sigp_restart(vcpu, cpu_addr); | 465 | rc = sigp_check_callable(vcpu, cpu_addr); |
386 | if (rc == SIGP_CC_BUSY) | 466 | if (rc == SIGP_CC_ORDER_CODE_ACCEPTED) { |
387 | break; | 467 | VCPU_EVENT(vcpu, 4, |
388 | /* user space must know about restart */ | 468 | "sigp restart %x to handle userspace", |
469 | cpu_addr); | ||
470 | /* user space must know about restart */ | ||
471 | rc = -EOPNOTSUPP; | ||
472 | } | ||
473 | break; | ||
389 | default: | 474 | default: |
390 | return -EOPNOTSUPP; | 475 | return -EOPNOTSUPP; |
391 | } | 476 | } |
@@ -393,7 +478,6 @@ int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu) | |||
393 | if (rc < 0) | 478 | if (rc < 0) |
394 | return rc; | 479 | return rc; |
395 | 480 | ||
396 | vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44); | 481 | kvm_s390_set_psw_cc(vcpu, rc); |
397 | vcpu->arch.sie_block->gpsw.mask |= (rc & 3ul) << 44; | ||
398 | return 0; | 482 | return 0; |
399 | } | 483 | } |