| Christian Borntraeger | 453423d | 2008-03-25 18:47:29 +0100 | [diff] [blame] | 1 | /* | 
|  | 2 | * priv.c - handling privileged instructions | 
|  | 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> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 15 | #include <linux/gfp.h> | 
| Christian Borntraeger | 453423d | 2008-03-25 18:47:29 +0100 | [diff] [blame] | 16 | #include <linux/errno.h> | 
|  | 17 | #include <asm/current.h> | 
|  | 18 | #include <asm/debug.h> | 
|  | 19 | #include <asm/ebcdic.h> | 
|  | 20 | #include <asm/sysinfo.h> | 
|  | 21 | #include "gaccess.h" | 
|  | 22 | #include "kvm-s390.h" | 
|  | 23 |  | 
|  | 24 | static int handle_set_prefix(struct kvm_vcpu *vcpu) | 
|  | 25 | { | 
|  | 26 | int base2 = vcpu->arch.sie_block->ipb >> 28; | 
|  | 27 | int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16); | 
|  | 28 | u64 operand2; | 
|  | 29 | u32 address = 0; | 
|  | 30 | u8 tmp; | 
|  | 31 |  | 
|  | 32 | vcpu->stat.instruction_spx++; | 
|  | 33 |  | 
|  | 34 | operand2 = disp2; | 
|  | 35 | if (base2) | 
|  | 36 | operand2 += vcpu->arch.guest_gprs[base2]; | 
|  | 37 |  | 
|  | 38 | /* must be word boundary */ | 
|  | 39 | if (operand2 & 3) { | 
|  | 40 | kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); | 
|  | 41 | goto out; | 
|  | 42 | } | 
|  | 43 |  | 
|  | 44 | /* get the value */ | 
|  | 45 | if (get_guest_u32(vcpu, operand2, &address)) { | 
|  | 46 | kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); | 
|  | 47 | goto out; | 
|  | 48 | } | 
|  | 49 |  | 
|  | 50 | address = address & 0x7fffe000u; | 
|  | 51 |  | 
|  | 52 | /* make sure that the new value is valid memory */ | 
|  | 53 | if (copy_from_guest_absolute(vcpu, &tmp, address, 1) || | 
|  | 54 | (copy_from_guest_absolute(vcpu, &tmp, address + PAGE_SIZE, 1))) { | 
|  | 55 | kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); | 
|  | 56 | goto out; | 
|  | 57 | } | 
|  | 58 |  | 
|  | 59 | vcpu->arch.sie_block->prefix = address; | 
|  | 60 | vcpu->arch.sie_block->ihcpu = 0xffff; | 
|  | 61 |  | 
|  | 62 | VCPU_EVENT(vcpu, 5, "setting prefix to %x", address); | 
|  | 63 | out: | 
|  | 64 | return 0; | 
|  | 65 | } | 
|  | 66 |  | 
|  | 67 | static int handle_store_prefix(struct kvm_vcpu *vcpu) | 
|  | 68 | { | 
|  | 69 | int base2 = vcpu->arch.sie_block->ipb >> 28; | 
|  | 70 | int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16); | 
|  | 71 | u64 operand2; | 
|  | 72 | u32 address; | 
|  | 73 |  | 
|  | 74 | vcpu->stat.instruction_stpx++; | 
|  | 75 | operand2 = disp2; | 
|  | 76 | if (base2) | 
|  | 77 | operand2 += vcpu->arch.guest_gprs[base2]; | 
|  | 78 |  | 
|  | 79 | /* must be word boundary */ | 
|  | 80 | if (operand2 & 3) { | 
|  | 81 | kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); | 
|  | 82 | goto out; | 
|  | 83 | } | 
|  | 84 |  | 
|  | 85 | address = vcpu->arch.sie_block->prefix; | 
|  | 86 | address = address & 0x7fffe000u; | 
|  | 87 |  | 
|  | 88 | /* get the value */ | 
|  | 89 | if (put_guest_u32(vcpu, operand2, address)) { | 
|  | 90 | kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); | 
|  | 91 | goto out; | 
|  | 92 | } | 
|  | 93 |  | 
|  | 94 | VCPU_EVENT(vcpu, 5, "storing prefix to %x", address); | 
|  | 95 | out: | 
|  | 96 | return 0; | 
|  | 97 | } | 
|  | 98 |  | 
|  | 99 | static int handle_store_cpu_address(struct kvm_vcpu *vcpu) | 
|  | 100 | { | 
|  | 101 | int base2 = vcpu->arch.sie_block->ipb >> 28; | 
|  | 102 | int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16); | 
|  | 103 | u64 useraddr; | 
|  | 104 | int rc; | 
|  | 105 |  | 
|  | 106 | vcpu->stat.instruction_stap++; | 
|  | 107 | useraddr = disp2; | 
|  | 108 | if (base2) | 
|  | 109 | useraddr += vcpu->arch.guest_gprs[base2]; | 
|  | 110 |  | 
|  | 111 | if (useraddr & 1) { | 
|  | 112 | kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); | 
|  | 113 | goto out; | 
|  | 114 | } | 
|  | 115 |  | 
|  | 116 | rc = put_guest_u16(vcpu, useraddr, vcpu->vcpu_id); | 
|  | 117 | if (rc == -EFAULT) { | 
|  | 118 | kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); | 
|  | 119 | goto out; | 
|  | 120 | } | 
|  | 121 |  | 
| Heiko Carstens | 33e1911 | 2009-01-09 12:14:56 +0100 | [diff] [blame] | 122 | VCPU_EVENT(vcpu, 5, "storing cpu address to %llx", useraddr); | 
| Christian Borntraeger | 453423d | 2008-03-25 18:47:29 +0100 | [diff] [blame] | 123 | out: | 
|  | 124 | return 0; | 
|  | 125 | } | 
|  | 126 |  | 
|  | 127 | static int handle_skey(struct kvm_vcpu *vcpu) | 
|  | 128 | { | 
|  | 129 | vcpu->stat.instruction_storage_key++; | 
|  | 130 | vcpu->arch.sie_block->gpsw.addr -= 4; | 
|  | 131 | VCPU_EVENT(vcpu, 4, "%s", "retrying storage key operation"); | 
|  | 132 | return 0; | 
|  | 133 | } | 
|  | 134 |  | 
|  | 135 | static int handle_stsch(struct kvm_vcpu *vcpu) | 
|  | 136 | { | 
|  | 137 | vcpu->stat.instruction_stsch++; | 
|  | 138 | VCPU_EVENT(vcpu, 4, "%s", "store subchannel - CC3"); | 
|  | 139 | /* condition code 3 */ | 
|  | 140 | vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44); | 
|  | 141 | vcpu->arch.sie_block->gpsw.mask |= (3 & 3ul) << 44; | 
|  | 142 | return 0; | 
|  | 143 | } | 
|  | 144 |  | 
|  | 145 | static int handle_chsc(struct kvm_vcpu *vcpu) | 
|  | 146 | { | 
|  | 147 | vcpu->stat.instruction_chsc++; | 
|  | 148 | VCPU_EVENT(vcpu, 4, "%s", "channel subsystem call - CC3"); | 
|  | 149 | /* condition code 3 */ | 
|  | 150 | vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44); | 
|  | 151 | vcpu->arch.sie_block->gpsw.mask |= (3 & 3ul) << 44; | 
|  | 152 | return 0; | 
|  | 153 | } | 
|  | 154 |  | 
| Christian Borntraeger | 453423d | 2008-03-25 18:47:29 +0100 | [diff] [blame] | 155 | static int handle_stfl(struct kvm_vcpu *vcpu) | 
|  | 156 | { | 
| Martin Schwidefsky | 14375bc | 2010-10-25 16:10:51 +0200 | [diff] [blame] | 157 | unsigned int facility_list; | 
| Christian Borntraeger | 453423d | 2008-03-25 18:47:29 +0100 | [diff] [blame] | 158 | int rc; | 
|  | 159 |  | 
|  | 160 | vcpu->stat.instruction_stfl++; | 
| Christian Borntraeger | a0046b6 | 2008-08-29 13:29:45 +0200 | [diff] [blame] | 161 | /* only pass the facility bits, which we can handle */ | 
| Martin Schwidefsky | 14375bc | 2010-10-25 16:10:51 +0200 | [diff] [blame] | 162 | facility_list = S390_lowcore.stfl_fac_list & 0xff00fff3; | 
| Christian Borntraeger | 453423d | 2008-03-25 18:47:29 +0100 | [diff] [blame] | 163 |  | 
|  | 164 | rc = copy_to_guest(vcpu, offsetof(struct _lowcore, stfl_fac_list), | 
|  | 165 | &facility_list, sizeof(facility_list)); | 
|  | 166 | if (rc == -EFAULT) | 
|  | 167 | kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); | 
|  | 168 | else | 
|  | 169 | VCPU_EVENT(vcpu, 5, "store facility list value %x", | 
|  | 170 | facility_list); | 
|  | 171 | return 0; | 
|  | 172 | } | 
|  | 173 |  | 
|  | 174 | static int handle_stidp(struct kvm_vcpu *vcpu) | 
|  | 175 | { | 
|  | 176 | int base2 = vcpu->arch.sie_block->ipb >> 28; | 
|  | 177 | int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16); | 
|  | 178 | u64 operand2; | 
|  | 179 | int rc; | 
|  | 180 |  | 
|  | 181 | vcpu->stat.instruction_stidp++; | 
|  | 182 | operand2 = disp2; | 
|  | 183 | if (base2) | 
|  | 184 | operand2 += vcpu->arch.guest_gprs[base2]; | 
|  | 185 |  | 
|  | 186 | if (operand2 & 7) { | 
|  | 187 | kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); | 
|  | 188 | goto out; | 
|  | 189 | } | 
|  | 190 |  | 
|  | 191 | rc = put_guest_u64(vcpu, operand2, vcpu->arch.stidp_data); | 
|  | 192 | if (rc == -EFAULT) { | 
|  | 193 | kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); | 
|  | 194 | goto out; | 
|  | 195 | } | 
|  | 196 |  | 
|  | 197 | VCPU_EVENT(vcpu, 5, "%s", "store cpu id"); | 
|  | 198 | out: | 
|  | 199 | return 0; | 
|  | 200 | } | 
|  | 201 |  | 
|  | 202 | static void handle_stsi_3_2_2(struct kvm_vcpu *vcpu, struct sysinfo_3_2_2 *mem) | 
|  | 203 | { | 
| Christian Borntraeger | 180c12f | 2008-06-27 15:05:40 +0200 | [diff] [blame] | 204 | struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; | 
| Christian Borntraeger | 453423d | 2008-03-25 18:47:29 +0100 | [diff] [blame] | 205 | int cpus = 0; | 
|  | 206 | int n; | 
|  | 207 |  | 
| Christian Borntraeger | b037a4f | 2009-05-12 17:21:50 +0200 | [diff] [blame] | 208 | spin_lock(&fi->lock); | 
| Christian Borntraeger | 453423d | 2008-03-25 18:47:29 +0100 | [diff] [blame] | 209 | for (n = 0; n < KVM_MAX_VCPUS; n++) | 
|  | 210 | if (fi->local_int[n]) | 
|  | 211 | cpus++; | 
| Christian Borntraeger | b037a4f | 2009-05-12 17:21:50 +0200 | [diff] [blame] | 212 | spin_unlock(&fi->lock); | 
| Christian Borntraeger | 453423d | 2008-03-25 18:47:29 +0100 | [diff] [blame] | 213 |  | 
|  | 214 | /* deal with other level 3 hypervisors */ | 
|  | 215 | if (stsi(mem, 3, 2, 2) == -ENOSYS) | 
|  | 216 | mem->count = 0; | 
|  | 217 | if (mem->count < 8) | 
|  | 218 | mem->count++; | 
|  | 219 | for (n = mem->count - 1; n > 0 ; n--) | 
|  | 220 | memcpy(&mem->vm[n], &mem->vm[n - 1], sizeof(mem->vm[0])); | 
|  | 221 |  | 
|  | 222 | mem->vm[0].cpus_total = cpus; | 
|  | 223 | mem->vm[0].cpus_configured = cpus; | 
|  | 224 | mem->vm[0].cpus_standby = 0; | 
|  | 225 | mem->vm[0].cpus_reserved = 0; | 
|  | 226 | mem->vm[0].caf = 1000; | 
|  | 227 | memcpy(mem->vm[0].name, "KVMguest", 8); | 
|  | 228 | ASCEBC(mem->vm[0].name, 8); | 
|  | 229 | memcpy(mem->vm[0].cpi, "KVM/Linux       ", 16); | 
|  | 230 | ASCEBC(mem->vm[0].cpi, 16); | 
|  | 231 | } | 
|  | 232 |  | 
|  | 233 | static int handle_stsi(struct kvm_vcpu *vcpu) | 
|  | 234 | { | 
|  | 235 | int fc = (vcpu->arch.guest_gprs[0] & 0xf0000000) >> 28; | 
|  | 236 | int sel1 = vcpu->arch.guest_gprs[0] & 0xff; | 
|  | 237 | int sel2 = vcpu->arch.guest_gprs[1] & 0xffff; | 
|  | 238 | int base2 = vcpu->arch.sie_block->ipb >> 28; | 
|  | 239 | int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16); | 
|  | 240 | u64 operand2; | 
|  | 241 | unsigned long mem; | 
|  | 242 |  | 
|  | 243 | vcpu->stat.instruction_stsi++; | 
|  | 244 | VCPU_EVENT(vcpu, 4, "stsi: fc: %x sel1: %x sel2: %x", fc, sel1, sel2); | 
|  | 245 |  | 
|  | 246 | operand2 = disp2; | 
|  | 247 | if (base2) | 
|  | 248 | operand2 += vcpu->arch.guest_gprs[base2]; | 
|  | 249 |  | 
|  | 250 | if (operand2 & 0xfff && fc > 0) | 
|  | 251 | return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); | 
|  | 252 |  | 
|  | 253 | switch (fc) { | 
|  | 254 | case 0: | 
|  | 255 | vcpu->arch.guest_gprs[0] = 3 << 28; | 
|  | 256 | vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44); | 
|  | 257 | return 0; | 
|  | 258 | case 1: /* same handling for 1 and 2 */ | 
|  | 259 | case 2: | 
|  | 260 | mem = get_zeroed_page(GFP_KERNEL); | 
|  | 261 | if (!mem) | 
|  | 262 | goto out_fail; | 
|  | 263 | if (stsi((void *) mem, fc, sel1, sel2) == -ENOSYS) | 
|  | 264 | goto out_mem; | 
|  | 265 | break; | 
|  | 266 | case 3: | 
|  | 267 | if (sel1 != 2 || sel2 != 2) | 
|  | 268 | goto out_fail; | 
|  | 269 | mem = get_zeroed_page(GFP_KERNEL); | 
|  | 270 | if (!mem) | 
|  | 271 | goto out_fail; | 
|  | 272 | handle_stsi_3_2_2(vcpu, (void *) mem); | 
|  | 273 | break; | 
|  | 274 | default: | 
|  | 275 | goto out_fail; | 
|  | 276 | } | 
|  | 277 |  | 
|  | 278 | if (copy_to_guest_absolute(vcpu, operand2, (void *) mem, PAGE_SIZE)) { | 
|  | 279 | kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); | 
|  | 280 | goto out_mem; | 
|  | 281 | } | 
|  | 282 | free_page(mem); | 
|  | 283 | vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44); | 
|  | 284 | vcpu->arch.guest_gprs[0] = 0; | 
|  | 285 | return 0; | 
|  | 286 | out_mem: | 
|  | 287 | free_page(mem); | 
|  | 288 | out_fail: | 
|  | 289 | /* condition code 3 */ | 
|  | 290 | vcpu->arch.sie_block->gpsw.mask |= 3ul << 44; | 
|  | 291 | return 0; | 
|  | 292 | } | 
|  | 293 |  | 
|  | 294 | static intercept_handler_t priv_handlers[256] = { | 
|  | 295 | [0x02] = handle_stidp, | 
|  | 296 | [0x10] = handle_set_prefix, | 
|  | 297 | [0x11] = handle_store_prefix, | 
|  | 298 | [0x12] = handle_store_cpu_address, | 
|  | 299 | [0x29] = handle_skey, | 
|  | 300 | [0x2a] = handle_skey, | 
|  | 301 | [0x2b] = handle_skey, | 
|  | 302 | [0x34] = handle_stsch, | 
|  | 303 | [0x5f] = handle_chsc, | 
|  | 304 | [0x7d] = handle_stsi, | 
|  | 305 | [0xb1] = handle_stfl, | 
|  | 306 | }; | 
|  | 307 |  | 
| Christian Borntraeger | 70455a3 | 2009-01-22 10:28:29 +0100 | [diff] [blame] | 308 | int kvm_s390_handle_b2(struct kvm_vcpu *vcpu) | 
| Christian Borntraeger | 453423d | 2008-03-25 18:47:29 +0100 | [diff] [blame] | 309 | { | 
|  | 310 | intercept_handler_t handler; | 
|  | 311 |  | 
| Christian Borntraeger | 70455a3 | 2009-01-22 10:28:29 +0100 | [diff] [blame] | 312 | /* | 
|  | 313 | * a lot of B2 instructions are priviledged. We first check for | 
| Lucas De Marchi | 25985ed | 2011-03-30 22:57:33 -0300 | [diff] [blame] | 314 | * the privileged ones, that we can handle in the kernel. If the | 
| Christian Borntraeger | 70455a3 | 2009-01-22 10:28:29 +0100 | [diff] [blame] | 315 | * kernel can handle this instruction, we check for the problem | 
|  | 316 | * state bit and (a) handle the instruction or (b) send a code 2 | 
|  | 317 | * program check. | 
|  | 318 | * Anything else goes to userspace.*/ | 
| Christian Borntraeger | 453423d | 2008-03-25 18:47:29 +0100 | [diff] [blame] | 319 | handler = priv_handlers[vcpu->arch.sie_block->ipa & 0x00ff]; | 
| Christian Borntraeger | 70455a3 | 2009-01-22 10:28:29 +0100 | [diff] [blame] | 320 | if (handler) { | 
|  | 321 | if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) | 
|  | 322 | return kvm_s390_inject_program_int(vcpu, | 
|  | 323 | PGM_PRIVILEGED_OPERATION); | 
|  | 324 | else | 
|  | 325 | return handler(vcpu); | 
|  | 326 | } | 
| Heiko Carstens | b8e660b | 2010-02-26 22:37:41 +0100 | [diff] [blame] | 327 | return -EOPNOTSUPP; | 
| Christian Borntraeger | 453423d | 2008-03-25 18:47:29 +0100 | [diff] [blame] | 328 | } |