Blame view
arch/s390/kvm/sigp.c
9.74 KB
5288fbf0e KVM: s390: interp... |
1 2 3 |
/* * sigp.c - handlinge interprocessor communication * |
9ace903d1 KVM: s390: infras... |
4 |
* Copyright IBM Corp. 2008,2009 |
5288fbf0e KVM: s390: interp... |
5 6 7 8 9 10 11 |
* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License (version 2 only) * as published by the Free Software Foundation. * * Author(s): Carsten Otte <cotte@de.ibm.com> * Christian Borntraeger <borntraeger@de.ibm.com> |
9ace903d1 KVM: s390: infras... |
12 |
* Christian Ehrhardt <ehrhardt@de.ibm.com> |
5288fbf0e KVM: s390: interp... |
13 14 15 16 |
*/ #include <linux/kvm.h> #include <linux/kvm_host.h> |
5a0e3ad6a include cleanup: ... |
17 |
#include <linux/slab.h> |
5288fbf0e KVM: s390: interp... |
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#include "gaccess.h" #include "kvm-s390.h" /* sigp order codes */ #define SIGP_SENSE 0x01 #define SIGP_EXTERNAL_CALL 0x02 #define SIGP_EMERGENCY 0x03 #define SIGP_START 0x04 #define SIGP_STOP 0x05 #define SIGP_RESTART 0x06 #define SIGP_STOP_STORE_STATUS 0x09 #define SIGP_INITIAL_CPU_RESET 0x0b #define SIGP_CPU_RESET 0x0c #define SIGP_SET_PREFIX 0x0d #define SIGP_STORE_STATUS_ADDR 0x0e #define SIGP_SET_ARCH 0x12 |
bd59d3a44 KVM: s390: handle... |
34 |
#define SIGP_SENSE_RUNNING 0x15 |
5288fbf0e KVM: s390: interp... |
35 36 37 |
/* cpu status bits */ #define SIGP_STAT_EQUIPMENT_CHECK 0x80000000UL |
bd59d3a44 KVM: s390: handle... |
38 |
#define SIGP_STAT_NOT_RUNNING 0x00000400UL |
5288fbf0e KVM: s390: interp... |
39 40 41 42 43 44 45 46 47 |
#define SIGP_STAT_INCORRECT_STATE 0x00000200UL #define SIGP_STAT_INVALID_PARAMETER 0x00000100UL #define SIGP_STAT_EXT_CALL_PENDING 0x00000080UL #define SIGP_STAT_STOPPED 0x00000040UL #define SIGP_STAT_OPERATOR_INTERV 0x00000020UL #define SIGP_STAT_CHECK_STOP 0x00000010UL #define SIGP_STAT_INOPERATIVE 0x00000004UL #define SIGP_STAT_INVALID_ORDER 0x00000002UL #define SIGP_STAT_RECEIVER_CHECK 0x00000001UL |
0096369da KVM: s390: Change... |
48 49 |
static int __sigp_sense(struct kvm_vcpu *vcpu, u16 cpu_addr, unsigned long *reg) |
5288fbf0e KVM: s390: interp... |
50 |
{ |
180c12fb2 KVM: s390: rename... |
51 |
struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; |
5288fbf0e KVM: s390: interp... |
52 53 54 55 |
int rc; if (cpu_addr >= KVM_MAX_VCPUS) return 3; /* not operational */ |
b037a4f34 KVM: s390: optimi... |
56 |
spin_lock(&fi->lock); |
5288fbf0e KVM: s390: interp... |
57 58 |
if (fi->local_int[cpu_addr] == NULL) rc = 3; /* not operational */ |
9e6dabeff KVM: s390: Fix RU... |
59 60 |
else if (!(atomic_read(fi->local_int[cpu_addr]->cpuflags) & CPUSTAT_STOPPED)) { |
5288fbf0e KVM: s390: interp... |
61 62 63 64 65 66 67 |
*reg &= 0xffffffff00000000UL; rc = 1; /* status stored */ } else { *reg &= 0xffffffff00000000UL; *reg |= SIGP_STAT_STOPPED; rc = 1; /* status stored */ } |
b037a4f34 KVM: s390: optimi... |
68 |
spin_unlock(&fi->lock); |
5288fbf0e KVM: s390: interp... |
69 70 71 72 73 74 75 |
VCPU_EVENT(vcpu, 4, "sensed status of cpu %x rc %x", cpu_addr, rc); return rc; } static int __sigp_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr) { |
180c12fb2 KVM: s390: rename... |
76 77 78 |
struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; struct kvm_s390_local_interrupt *li; struct kvm_s390_interrupt_info *inti; |
5288fbf0e KVM: s390: interp... |
79 80 81 82 83 84 85 86 87 88 |
int rc; if (cpu_addr >= KVM_MAX_VCPUS) return 3; /* not operational */ inti = kzalloc(sizeof(*inti), GFP_KERNEL); if (!inti) return -ENOMEM; inti->type = KVM_S390_INT_EMERGENCY; |
7697e71f7 KVM: s390: implem... |
89 |
inti->emerg.code = vcpu->vcpu_id; |
5288fbf0e KVM: s390: interp... |
90 |
|
b037a4f34 KVM: s390: optimi... |
91 |
spin_lock(&fi->lock); |
5288fbf0e KVM: s390: interp... |
92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
li = fi->local_int[cpu_addr]; if (li == NULL) { rc = 3; /* not operational */ kfree(inti); goto unlock; } spin_lock_bh(&li->lock); list_add_tail(&inti->list, &li->list); atomic_set(&li->active, 1); atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags); if (waitqueue_active(&li->wq)) wake_up_interruptible(&li->wq); spin_unlock_bh(&li->lock); rc = 0; /* order accepted */ |
7697e71f7 KVM: s390: implem... |
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
VCPU_EVENT(vcpu, 4, "sent sigp emerg to cpu %x", cpu_addr); unlock: spin_unlock(&fi->lock); return rc; } static int __sigp_external_call(struct kvm_vcpu *vcpu, u16 cpu_addr) { struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; struct kvm_s390_local_interrupt *li; struct kvm_s390_interrupt_info *inti; int rc; if (cpu_addr >= KVM_MAX_VCPUS) return 3; /* not operational */ inti = kzalloc(sizeof(*inti), GFP_KERNEL); if (!inti) return -ENOMEM; inti->type = KVM_S390_INT_EXTERNAL_CALL; inti->extcall.code = vcpu->vcpu_id; spin_lock(&fi->lock); li = fi->local_int[cpu_addr]; if (li == NULL) { rc = 3; /* not operational */ kfree(inti); goto unlock; } spin_lock_bh(&li->lock); list_add_tail(&inti->list, &li->list); atomic_set(&li->active, 1); atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags); if (waitqueue_active(&li->wq)) wake_up_interruptible(&li->wq); spin_unlock_bh(&li->lock); rc = 0; /* order accepted */ VCPU_EVENT(vcpu, 4, "sent sigp ext call to cpu %x", cpu_addr); |
5288fbf0e KVM: s390: interp... |
145 |
unlock: |
b037a4f34 KVM: s390: optimi... |
146 |
spin_unlock(&fi->lock); |
5288fbf0e KVM: s390: interp... |
147 148 |
return rc; } |
9ace903d1 KVM: s390: infras... |
149 |
static int __inject_sigp_stop(struct kvm_s390_local_interrupt *li, int action) |
5288fbf0e KVM: s390: interp... |
150 |
{ |
180c12fb2 KVM: s390: rename... |
151 |
struct kvm_s390_interrupt_info *inti; |
5288fbf0e KVM: s390: interp... |
152 |
|
9940fa80c [S390] arch/s390/... |
153 |
inti = kzalloc(sizeof(*inti), GFP_ATOMIC); |
5288fbf0e KVM: s390: interp... |
154 155 |
if (!inti) return -ENOMEM; |
5288fbf0e KVM: s390: interp... |
156 |
inti->type = KVM_S390_SIGP_STOP; |
5288fbf0e KVM: s390: interp... |
157 158 159 160 |
spin_lock_bh(&li->lock); list_add_tail(&inti->list, &li->list); atomic_set(&li->active, 1); atomic_set_mask(CPUSTAT_STOP_INT, li->cpuflags); |
9ace903d1 KVM: s390: infras... |
161 |
li->action_bits |= action; |
5288fbf0e KVM: s390: interp... |
162 163 164 |
if (waitqueue_active(&li->wq)) wake_up_interruptible(&li->wq); spin_unlock_bh(&li->lock); |
9ace903d1 KVM: s390: infras... |
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
return 0; /* order accepted */ } static int __sigp_stop(struct kvm_vcpu *vcpu, u16 cpu_addr, int action) { struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; struct kvm_s390_local_interrupt *li; int rc; if (cpu_addr >= KVM_MAX_VCPUS) return 3; /* not operational */ spin_lock(&fi->lock); li = fi->local_int[cpu_addr]; if (li == NULL) { rc = 3; /* not operational */ goto unlock; } rc = __inject_sigp_stop(li, action); |
5288fbf0e KVM: s390: interp... |
186 |
unlock: |
b037a4f34 KVM: s390: optimi... |
187 |
spin_unlock(&fi->lock); |
5288fbf0e KVM: s390: interp... |
188 189 190 |
VCPU_EVENT(vcpu, 4, "sent sigp stop to cpu %x", cpu_addr); return rc; } |
9ace903d1 KVM: s390: infras... |
191 192 193 194 195 |
int kvm_s390_inject_sigp_stop(struct kvm_vcpu *vcpu, int action) { struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; return __inject_sigp_stop(li, action); } |
5288fbf0e KVM: s390: interp... |
196 197 198 199 200 201 |
static int __sigp_set_arch(struct kvm_vcpu *vcpu, u32 parameter) { int rc; switch (parameter & 0xff) { case 0: |
5288fbf0e KVM: s390: interp... |
202 203 204 205 206 207 208 |
rc = 3; /* not operational */ break; case 1: case 2: rc = 0; /* order accepted */ break; default: |
b8e660b83 [S390] Replace EN... |
209 |
rc = -EOPNOTSUPP; |
5288fbf0e KVM: s390: interp... |
210 211 212 213 214 |
} return rc; } static int __sigp_set_prefix(struct kvm_vcpu *vcpu, u16 cpu_addr, u32 address, |
0096369da KVM: s390: Change... |
215 |
unsigned long *reg) |
5288fbf0e KVM: s390: interp... |
216 |
{ |
180c12fb2 KVM: s390: rename... |
217 |
struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; |
53cb780ad [S390] KVM: Read ... |
218 |
struct kvm_s390_local_interrupt *li = NULL; |
180c12fb2 KVM: s390: rename... |
219 |
struct kvm_s390_interrupt_info *inti; |
5288fbf0e KVM: s390: interp... |
220 221 222 223 224 |
int rc; u8 tmp; /* make sure that the new value is valid memory */ address = address & 0x7fffe000u; |
092670cd9 [S390] Use gmap t... |
225 226 |
if (copy_from_guest_absolute(vcpu, &tmp, address, 1) || copy_from_guest_absolute(vcpu, &tmp, address + PAGE_SIZE, 1)) { |
5288fbf0e KVM: s390: interp... |
227 228 229 230 231 232 233 |
*reg |= SIGP_STAT_INVALID_PARAMETER; return 1; /* invalid parameter */ } inti = kzalloc(sizeof(*inti), GFP_KERNEL); if (!inti) return 2; /* busy */ |
b037a4f34 KVM: s390: optimi... |
234 |
spin_lock(&fi->lock); |
53cb780ad [S390] KVM: Read ... |
235 236 |
if (cpu_addr < KVM_MAX_VCPUS) li = fi->local_int[cpu_addr]; |
5288fbf0e KVM: s390: interp... |
237 |
|
53cb780ad [S390] KVM: Read ... |
238 |
if (li == NULL) { |
5288fbf0e KVM: s390: interp... |
239 240 241 242 243 244 245 246 |
rc = 1; /* incorrect state */ *reg &= SIGP_STAT_INCORRECT_STATE; kfree(inti); goto out_fi; } spin_lock_bh(&li->lock); /* cpu must be in stopped state */ |
9e6dabeff KVM: s390: Fix RU... |
247 |
if (!(atomic_read(li->cpuflags) & CPUSTAT_STOPPED)) { |
5288fbf0e KVM: s390: interp... |
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 |
rc = 1; /* incorrect state */ *reg &= SIGP_STAT_INCORRECT_STATE; kfree(inti); goto out_li; } inti->type = KVM_S390_SIGP_SET_PREFIX; inti->prefix.address = address; list_add_tail(&inti->list, &li->list); atomic_set(&li->active, 1); if (waitqueue_active(&li->wq)) wake_up_interruptible(&li->wq); rc = 0; /* order accepted */ VCPU_EVENT(vcpu, 4, "set prefix of cpu %02x to %x", cpu_addr, address); out_li: spin_unlock_bh(&li->lock); out_fi: |
b037a4f34 KVM: s390: optimi... |
267 |
spin_unlock(&fi->lock); |
5288fbf0e KVM: s390: interp... |
268 269 |
return rc; } |
bd59d3a44 KVM: s390: handle... |
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
static int __sigp_sense_running(struct kvm_vcpu *vcpu, u16 cpu_addr, unsigned long *reg) { int rc; struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; if (cpu_addr >= KVM_MAX_VCPUS) return 3; /* not operational */ spin_lock(&fi->lock); if (fi->local_int[cpu_addr] == NULL) rc = 3; /* not operational */ else { if (atomic_read(fi->local_int[cpu_addr]->cpuflags) & CPUSTAT_RUNNING) { /* running */ rc = 1; } else { /* not running */ *reg &= 0xffffffff00000000UL; *reg |= SIGP_STAT_NOT_RUNNING; rc = 0; } } spin_unlock(&fi->lock); VCPU_EVENT(vcpu, 4, "sensed running status of cpu %x rc %x", cpu_addr, rc); return rc; } |
5288fbf0e KVM: s390: interp... |
301 302 303 304 305 306 307 308 309 310 |
int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu) { int r1 = (vcpu->arch.sie_block->ipa & 0x00f0) >> 4; int r3 = vcpu->arch.sie_block->ipa & 0x000f; int base2 = vcpu->arch.sie_block->ipb >> 28; int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16); u32 parameter; u16 cpu_addr = vcpu->arch.guest_gprs[r3]; u8 order_code; int rc; |
3eb77d511 KVM: s390: Fix pr... |
311 312 313 314 |
/* sigp in userspace can exit */ if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OPERATION); |
5288fbf0e KVM: s390: interp... |
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 |
order_code = disp2; if (base2) order_code += vcpu->arch.guest_gprs[base2]; if (r1 % 2) parameter = vcpu->arch.guest_gprs[r1]; else parameter = vcpu->arch.guest_gprs[r1 + 1]; switch (order_code) { case SIGP_SENSE: vcpu->stat.instruction_sigp_sense++; rc = __sigp_sense(vcpu, cpu_addr, &vcpu->arch.guest_gprs[r1]); break; |
7697e71f7 KVM: s390: implem... |
330 331 332 333 |
case SIGP_EXTERNAL_CALL: vcpu->stat.instruction_sigp_external_call++; rc = __sigp_external_call(vcpu, cpu_addr); break; |
5288fbf0e KVM: s390: interp... |
334 335 336 337 338 339 |
case SIGP_EMERGENCY: vcpu->stat.instruction_sigp_emergency++; rc = __sigp_emergency(vcpu, cpu_addr); break; case SIGP_STOP: vcpu->stat.instruction_sigp_stop++; |
9ace903d1 KVM: s390: infras... |
340 |
rc = __sigp_stop(vcpu, cpu_addr, ACTION_STOP_ON_STOP); |
5288fbf0e KVM: s390: interp... |
341 342 343 |
break; case SIGP_STOP_STORE_STATUS: vcpu->stat.instruction_sigp_stop++; |
9ace903d1 KVM: s390: infras... |
344 |
rc = __sigp_stop(vcpu, cpu_addr, ACTION_STORE_ON_STOP); |
5288fbf0e KVM: s390: interp... |
345 346 347 348 349 350 351 352 353 354 |
break; case SIGP_SET_ARCH: vcpu->stat.instruction_sigp_arch++; rc = __sigp_set_arch(vcpu, parameter); break; case SIGP_SET_PREFIX: vcpu->stat.instruction_sigp_prefix++; rc = __sigp_set_prefix(vcpu, cpu_addr, parameter, &vcpu->arch.guest_gprs[r1]); break; |
bd59d3a44 KVM: s390: handle... |
355 356 357 358 359 |
case SIGP_SENSE_RUNNING: vcpu->stat.instruction_sigp_sense_running++; rc = __sigp_sense_running(vcpu, cpu_addr, &vcpu->arch.guest_gprs[r1]); break; |
5288fbf0e KVM: s390: interp... |
360 361 362 363 |
case SIGP_RESTART: vcpu->stat.instruction_sigp_restart++; /* user space must know about restart */ default: |
b8e660b83 [S390] Replace EN... |
364 |
return -EOPNOTSUPP; |
5288fbf0e KVM: s390: interp... |
365 366 367 368 369 370 371 372 373 |
} if (rc < 0) return rc; vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44); vcpu->arch.sie_block->gpsw.mask |= (rc & 3ul) << 44; return 0; } |