1 /* 2 * handling diagnose instructions 3 * 4 * Copyright IBM Corp. 2008, 2011 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 <asm/virtio-ccw.h> 17 #include "kvm-s390.h" 18 #include "trace.h" 19 #include "trace-s390.h" 20 21 static int diag_release_pages(struct kvm_vcpu *vcpu) 22 { 23 unsigned long start, end; 24 unsigned long prefix = vcpu->arch.sie_block->prefix; 25 26 start = vcpu->run->s.regs.gprs[(vcpu->arch.sie_block->ipa & 0xf0) >> 4]; 27 end = vcpu->run->s.regs.gprs[vcpu->arch.sie_block->ipa & 0xf] + 4096; 28 29 if (start & ~PAGE_MASK || end & ~PAGE_MASK || start > end 30 || start < 2 * PAGE_SIZE) 31 return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); 32 33 VCPU_EVENT(vcpu, 5, "diag release pages %lX %lX", start, end); 34 vcpu->stat.diagnose_10++; 35 36 /* we checked for start > end above */ 37 if (end < prefix || start >= prefix + 2 * PAGE_SIZE) { 38 gmap_discard(start, end, vcpu->arch.gmap); 39 } else { 40 if (start < prefix) 41 gmap_discard(start, prefix, vcpu->arch.gmap); 42 if (end >= prefix) 43 gmap_discard(prefix + 2 * PAGE_SIZE, 44 end, vcpu->arch.gmap); 45 } 46 return 0; 47 } 48 49 static int __diag_time_slice_end(struct kvm_vcpu *vcpu) 50 { 51 VCPU_EVENT(vcpu, 5, "%s", "diag time slice end"); 52 vcpu->stat.diagnose_44++; 53 kvm_vcpu_on_spin(vcpu); 54 return 0; 55 } 56 57 static int __diag_time_slice_end_directed(struct kvm_vcpu *vcpu) 58 { 59 struct kvm *kvm = vcpu->kvm; 60 struct kvm_vcpu *tcpu; 61 int tid; 62 int i; 63 64 tid = vcpu->run->s.regs.gprs[(vcpu->arch.sie_block->ipa & 0xf0) >> 4]; 65 vcpu->stat.diagnose_9c++; 66 VCPU_EVENT(vcpu, 5, "diag time slice end directed to %d", tid); 67 68 if (tid == vcpu->vcpu_id) 69 return 0; 70 71 kvm_for_each_vcpu(i, tcpu, kvm) 72 if (tcpu->vcpu_id == tid) { 73 kvm_vcpu_yield_to(tcpu); 74 break; 75 } 76 77 return 0; 78 } 79 80 static int __diag_ipl_functions(struct kvm_vcpu *vcpu) 81 { 82 unsigned int reg = vcpu->arch.sie_block->ipa & 0xf; 83 unsigned long subcode = vcpu->run->s.regs.gprs[reg] & 0xffff; 84 85 VCPU_EVENT(vcpu, 5, "diag ipl functions, subcode %lx", subcode); 86 switch (subcode) { 87 case 3: 88 vcpu->run->s390_reset_flags = KVM_S390_RESET_CLEAR; 89 break; 90 case 4: 91 vcpu->run->s390_reset_flags = 0; 92 break; 93 default: 94 return -EOPNOTSUPP; 95 } 96 97 atomic_set_mask(CPUSTAT_STOPPED, &vcpu->arch.sie_block->cpuflags); 98 vcpu->run->s390_reset_flags |= KVM_S390_RESET_SUBSYSTEM; 99 vcpu->run->s390_reset_flags |= KVM_S390_RESET_IPL; 100 vcpu->run->s390_reset_flags |= KVM_S390_RESET_CPU_INIT; 101 vcpu->run->exit_reason = KVM_EXIT_S390_RESET; 102 VCPU_EVENT(vcpu, 3, "requesting userspace resets %llx", 103 vcpu->run->s390_reset_flags); 104 trace_kvm_s390_request_resets(vcpu->run->s390_reset_flags); 105 return -EREMOTE; 106 } 107 108 static int __diag_virtio_hypercall(struct kvm_vcpu *vcpu) 109 { 110 int ret; 111 112 /* No virtio-ccw notification? Get out quickly. */ 113 if (!vcpu->kvm->arch.css_support || 114 (vcpu->run->s.regs.gprs[1] != KVM_S390_VIRTIO_CCW_NOTIFY)) 115 return -EOPNOTSUPP; 116 117 /* 118 * The layout is as follows: 119 * - gpr 2 contains the subchannel id (passed as addr) 120 * - gpr 3 contains the virtqueue index (passed as datamatch) 121 * - gpr 4 contains the index on the bus (optionally) 122 */ 123 ret = kvm_io_bus_write_cookie(vcpu->kvm, KVM_VIRTIO_CCW_NOTIFY_BUS, 124 vcpu->run->s.regs.gprs[2], 125 8, &vcpu->run->s.regs.gprs[3], 126 vcpu->run->s.regs.gprs[4]); 127 128 /* 129 * Return cookie in gpr 2, but don't overwrite the register if the 130 * diagnose will be handled by userspace. 131 */ 132 if (ret != -EOPNOTSUPP) 133 vcpu->run->s.regs.gprs[2] = ret; 134 /* kvm_io_bus_write_cookie returns -EOPNOTSUPP if it found no match. */ 135 return ret < 0 ? ret : 0; 136 } 137 138 int kvm_s390_handle_diag(struct kvm_vcpu *vcpu) 139 { 140 int code = (vcpu->arch.sie_block->ipb & 0xfff0000) >> 16; 141 142 if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) 143 return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); 144 145 trace_kvm_s390_handle_diag(vcpu, code); 146 switch (code) { 147 case 0x10: 148 return diag_release_pages(vcpu); 149 case 0x44: 150 return __diag_time_slice_end(vcpu); 151 case 0x9c: 152 return __diag_time_slice_end_directed(vcpu); 153 case 0x308: 154 return __diag_ipl_functions(vcpu); 155 case 0x500: 156 return __diag_virtio_hypercall(vcpu); 157 default: 158 return -EOPNOTSUPP; 159 } 160 } 161