1 /* 2 * sigp.c - handlinge interprocessor communication 3 * 4 * Copyright IBM Corp. 2008 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> 12 */ 13 14 #include <linux/kvm.h> 15 #include <linux/kvm_host.h> 16 #include "gaccess.h" 17 #include "kvm-s390.h" 18 19 /* sigp order codes */ 20 #define SIGP_SENSE 0x01 21 #define SIGP_EXTERNAL_CALL 0x02 22 #define SIGP_EMERGENCY 0x03 23 #define SIGP_START 0x04 24 #define SIGP_STOP 0x05 25 #define SIGP_RESTART 0x06 26 #define SIGP_STOP_STORE_STATUS 0x09 27 #define SIGP_INITIAL_CPU_RESET 0x0b 28 #define SIGP_CPU_RESET 0x0c 29 #define SIGP_SET_PREFIX 0x0d 30 #define SIGP_STORE_STATUS_ADDR 0x0e 31 #define SIGP_SET_ARCH 0x12 32 33 /* cpu status bits */ 34 #define SIGP_STAT_EQUIPMENT_CHECK 0x80000000UL 35 #define SIGP_STAT_INCORRECT_STATE 0x00000200UL 36 #define SIGP_STAT_INVALID_PARAMETER 0x00000100UL 37 #define SIGP_STAT_EXT_CALL_PENDING 0x00000080UL 38 #define SIGP_STAT_STOPPED 0x00000040UL 39 #define SIGP_STAT_OPERATOR_INTERV 0x00000020UL 40 #define SIGP_STAT_CHECK_STOP 0x00000010UL 41 #define SIGP_STAT_INOPERATIVE 0x00000004UL 42 #define SIGP_STAT_INVALID_ORDER 0x00000002UL 43 #define SIGP_STAT_RECEIVER_CHECK 0x00000001UL 44 45 46 static int __sigp_sense(struct kvm_vcpu *vcpu, u16 cpu_addr, 47 unsigned long *reg) 48 { 49 struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; 50 int rc; 51 52 if (cpu_addr >= KVM_MAX_VCPUS) 53 return 3; /* not operational */ 54 55 spin_lock(&fi->lock); 56 if (fi->local_int[cpu_addr] == NULL) 57 rc = 3; /* not operational */ 58 else if (atomic_read(fi->local_int[cpu_addr]->cpuflags) 59 & CPUSTAT_RUNNING) { 60 *reg &= 0xffffffff00000000UL; 61 rc = 1; /* status stored */ 62 } else { 63 *reg &= 0xffffffff00000000UL; 64 *reg |= SIGP_STAT_STOPPED; 65 rc = 1; /* status stored */ 66 } 67 spin_unlock(&fi->lock); 68 69 VCPU_EVENT(vcpu, 4, "sensed status of cpu %x rc %x", cpu_addr, rc); 70 return rc; 71 } 72 73 static int __sigp_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr) 74 { 75 struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; 76 struct kvm_s390_local_interrupt *li; 77 struct kvm_s390_interrupt_info *inti; 78 int rc; 79 80 if (cpu_addr >= KVM_MAX_VCPUS) 81 return 3; /* not operational */ 82 83 inti = kzalloc(sizeof(*inti), GFP_KERNEL); 84 if (!inti) 85 return -ENOMEM; 86 87 inti->type = KVM_S390_INT_EMERGENCY; 88 89 spin_lock(&fi->lock); 90 li = fi->local_int[cpu_addr]; 91 if (li == NULL) { 92 rc = 3; /* not operational */ 93 kfree(inti); 94 goto unlock; 95 } 96 spin_lock_bh(&li->lock); 97 list_add_tail(&inti->list, &li->list); 98 atomic_set(&li->active, 1); 99 atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags); 100 if (waitqueue_active(&li->wq)) 101 wake_up_interruptible(&li->wq); 102 spin_unlock_bh(&li->lock); 103 rc = 0; /* order accepted */ 104 unlock: 105 spin_unlock(&fi->lock); 106 VCPU_EVENT(vcpu, 4, "sent sigp emerg to cpu %x", cpu_addr); 107 return rc; 108 } 109 110 static int __sigp_stop(struct kvm_vcpu *vcpu, u16 cpu_addr, int store) 111 { 112 struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; 113 struct kvm_s390_local_interrupt *li; 114 struct kvm_s390_interrupt_info *inti; 115 int rc; 116 117 if (cpu_addr >= KVM_MAX_VCPUS) 118 return 3; /* not operational */ 119 120 inti = kzalloc(sizeof(*inti), GFP_KERNEL); 121 if (!inti) 122 return -ENOMEM; 123 124 inti->type = KVM_S390_SIGP_STOP; 125 126 spin_lock(&fi->lock); 127 li = fi->local_int[cpu_addr]; 128 if (li == NULL) { 129 rc = 3; /* not operational */ 130 kfree(inti); 131 goto unlock; 132 } 133 spin_lock_bh(&li->lock); 134 list_add_tail(&inti->list, &li->list); 135 atomic_set(&li->active, 1); 136 atomic_set_mask(CPUSTAT_STOP_INT, li->cpuflags); 137 if (store) 138 li->action_bits |= ACTION_STORE_ON_STOP; 139 li->action_bits |= ACTION_STOP_ON_STOP; 140 if (waitqueue_active(&li->wq)) 141 wake_up_interruptible(&li->wq); 142 spin_unlock_bh(&li->lock); 143 rc = 0; /* order accepted */ 144 unlock: 145 spin_unlock(&fi->lock); 146 VCPU_EVENT(vcpu, 4, "sent sigp stop to cpu %x", cpu_addr); 147 return rc; 148 } 149 150 static int __sigp_set_arch(struct kvm_vcpu *vcpu, u32 parameter) 151 { 152 int rc; 153 154 switch (parameter & 0xff) { 155 case 0: 156 rc = 3; /* not operational */ 157 break; 158 case 1: 159 case 2: 160 rc = 0; /* order accepted */ 161 break; 162 default: 163 rc = -ENOTSUPP; 164 } 165 return rc; 166 } 167 168 static int __sigp_set_prefix(struct kvm_vcpu *vcpu, u16 cpu_addr, u32 address, 169 unsigned long *reg) 170 { 171 struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; 172 struct kvm_s390_local_interrupt *li = NULL; 173 struct kvm_s390_interrupt_info *inti; 174 int rc; 175 u8 tmp; 176 177 /* make sure that the new value is valid memory */ 178 address = address & 0x7fffe000u; 179 if ((copy_from_guest(vcpu, &tmp, 180 (u64) (address + vcpu->kvm->arch.guest_origin) , 1)) || 181 (copy_from_guest(vcpu, &tmp, (u64) (address + 182 vcpu->kvm->arch.guest_origin + PAGE_SIZE), 1))) { 183 *reg |= SIGP_STAT_INVALID_PARAMETER; 184 return 1; /* invalid parameter */ 185 } 186 187 inti = kzalloc(sizeof(*inti), GFP_KERNEL); 188 if (!inti) 189 return 2; /* busy */ 190 191 spin_lock(&fi->lock); 192 if (cpu_addr < KVM_MAX_VCPUS) 193 li = fi->local_int[cpu_addr]; 194 195 if (li == NULL) { 196 rc = 1; /* incorrect state */ 197 *reg &= SIGP_STAT_INCORRECT_STATE; 198 kfree(inti); 199 goto out_fi; 200 } 201 202 spin_lock_bh(&li->lock); 203 /* cpu must be in stopped state */ 204 if (atomic_read(li->cpuflags) & CPUSTAT_RUNNING) { 205 rc = 1; /* incorrect state */ 206 *reg &= SIGP_STAT_INCORRECT_STATE; 207 kfree(inti); 208 goto out_li; 209 } 210 211 inti->type = KVM_S390_SIGP_SET_PREFIX; 212 inti->prefix.address = address; 213 214 list_add_tail(&inti->list, &li->list); 215 atomic_set(&li->active, 1); 216 if (waitqueue_active(&li->wq)) 217 wake_up_interruptible(&li->wq); 218 rc = 0; /* order accepted */ 219 220 VCPU_EVENT(vcpu, 4, "set prefix of cpu %02x to %x", cpu_addr, address); 221 out_li: 222 spin_unlock_bh(&li->lock); 223 out_fi: 224 spin_unlock(&fi->lock); 225 return rc; 226 } 227 228 int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu) 229 { 230 int r1 = (vcpu->arch.sie_block->ipa & 0x00f0) >> 4; 231 int r3 = vcpu->arch.sie_block->ipa & 0x000f; 232 int base2 = vcpu->arch.sie_block->ipb >> 28; 233 int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16); 234 u32 parameter; 235 u16 cpu_addr = vcpu->arch.guest_gprs[r3]; 236 u8 order_code; 237 int rc; 238 239 /* sigp in userspace can exit */ 240 if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) 241 return kvm_s390_inject_program_int(vcpu, 242 PGM_PRIVILEGED_OPERATION); 243 244 order_code = disp2; 245 if (base2) 246 order_code += vcpu->arch.guest_gprs[base2]; 247 248 if (r1 % 2) 249 parameter = vcpu->arch.guest_gprs[r1]; 250 else 251 parameter = vcpu->arch.guest_gprs[r1 + 1]; 252 253 switch (order_code) { 254 case SIGP_SENSE: 255 vcpu->stat.instruction_sigp_sense++; 256 rc = __sigp_sense(vcpu, cpu_addr, 257 &vcpu->arch.guest_gprs[r1]); 258 break; 259 case SIGP_EMERGENCY: 260 vcpu->stat.instruction_sigp_emergency++; 261 rc = __sigp_emergency(vcpu, cpu_addr); 262 break; 263 case SIGP_STOP: 264 vcpu->stat.instruction_sigp_stop++; 265 rc = __sigp_stop(vcpu, cpu_addr, 0); 266 break; 267 case SIGP_STOP_STORE_STATUS: 268 vcpu->stat.instruction_sigp_stop++; 269 rc = __sigp_stop(vcpu, cpu_addr, 1); 270 break; 271 case SIGP_SET_ARCH: 272 vcpu->stat.instruction_sigp_arch++; 273 rc = __sigp_set_arch(vcpu, parameter); 274 break; 275 case SIGP_SET_PREFIX: 276 vcpu->stat.instruction_sigp_prefix++; 277 rc = __sigp_set_prefix(vcpu, cpu_addr, parameter, 278 &vcpu->arch.guest_gprs[r1]); 279 break; 280 case SIGP_RESTART: 281 vcpu->stat.instruction_sigp_restart++; 282 /* user space must know about restart */ 283 default: 284 return -ENOTSUPP; 285 } 286 287 if (rc < 0) 288 return rc; 289 290 vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44); 291 vcpu->arch.sie_block->gpsw.mask |= (rc & 3ul) << 44; 292 return 0; 293 } 294