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 "kvm-s390.h" 17 #include "trace.h" 18 #include "trace-s390.h" 19 20 static int diag_release_pages(struct kvm_vcpu *vcpu) 21 { 22 unsigned long start, end; 23 unsigned long prefix = vcpu->arch.sie_block->prefix; 24 25 start = vcpu->run->s.regs.gprs[(vcpu->arch.sie_block->ipa & 0xf0) >> 4]; 26 end = vcpu->run->s.regs.gprs[vcpu->arch.sie_block->ipa & 0xf] + 4096; 27 28 if (start & ~PAGE_MASK || end & ~PAGE_MASK || start > end 29 || start < 2 * PAGE_SIZE) 30 return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); 31 32 VCPU_EVENT(vcpu, 5, "diag release pages %lX %lX", start, end); 33 vcpu->stat.diagnose_10++; 34 35 /* we checked for start > end above */ 36 if (end < prefix || start >= prefix + 2 * PAGE_SIZE) { 37 gmap_discard(start, end, vcpu->arch.gmap); 38 } else { 39 if (start < prefix) 40 gmap_discard(start, prefix, vcpu->arch.gmap); 41 if (end >= prefix) 42 gmap_discard(prefix + 2 * PAGE_SIZE, 43 end, vcpu->arch.gmap); 44 } 45 return 0; 46 } 47 48 static int __diag_time_slice_end(struct kvm_vcpu *vcpu) 49 { 50 VCPU_EVENT(vcpu, 5, "%s", "diag time slice end"); 51 vcpu->stat.diagnose_44++; 52 kvm_vcpu_on_spin(vcpu); 53 return 0; 54 } 55 56 static int __diag_time_slice_end_directed(struct kvm_vcpu *vcpu) 57 { 58 struct kvm *kvm = vcpu->kvm; 59 struct kvm_vcpu *tcpu; 60 int tid; 61 int i; 62 63 tid = vcpu->run->s.regs.gprs[(vcpu->arch.sie_block->ipa & 0xf0) >> 4]; 64 vcpu->stat.diagnose_9c++; 65 VCPU_EVENT(vcpu, 5, "diag time slice end directed to %d", tid); 66 67 if (tid == vcpu->vcpu_id) 68 return 0; 69 70 kvm_for_each_vcpu(i, tcpu, kvm) 71 if (tcpu->vcpu_id == tid) { 72 kvm_vcpu_yield_to(tcpu); 73 break; 74 } 75 76 return 0; 77 } 78 79 static int __diag_ipl_functions(struct kvm_vcpu *vcpu) 80 { 81 unsigned int reg = vcpu->arch.sie_block->ipa & 0xf; 82 unsigned long subcode = vcpu->run->s.regs.gprs[reg] & 0xffff; 83 84 VCPU_EVENT(vcpu, 5, "diag ipl functions, subcode %lx", subcode); 85 switch (subcode) { 86 case 3: 87 vcpu->run->s390_reset_flags = KVM_S390_RESET_CLEAR; 88 break; 89 case 4: 90 vcpu->run->s390_reset_flags = 0; 91 break; 92 default: 93 return -EOPNOTSUPP; 94 } 95 96 atomic_set_mask(CPUSTAT_STOPPED, &vcpu->arch.sie_block->cpuflags); 97 vcpu->run->s390_reset_flags |= KVM_S390_RESET_SUBSYSTEM; 98 vcpu->run->s390_reset_flags |= KVM_S390_RESET_IPL; 99 vcpu->run->s390_reset_flags |= KVM_S390_RESET_CPU_INIT; 100 vcpu->run->exit_reason = KVM_EXIT_S390_RESET; 101 VCPU_EVENT(vcpu, 3, "requesting userspace resets %llx", 102 vcpu->run->s390_reset_flags); 103 trace_kvm_s390_request_resets(vcpu->run->s390_reset_flags); 104 return -EREMOTE; 105 } 106 107 int kvm_s390_handle_diag(struct kvm_vcpu *vcpu) 108 { 109 int code = (vcpu->arch.sie_block->ipb & 0xfff0000) >> 16; 110 111 trace_kvm_s390_handle_diag(vcpu, code); 112 switch (code) { 113 case 0x10: 114 return diag_release_pages(vcpu); 115 case 0x44: 116 return __diag_time_slice_end(vcpu); 117 case 0x9c: 118 return __diag_time_slice_end_directed(vcpu); 119 case 0x308: 120 return __diag_ipl_functions(vcpu); 121 default: 122 return -EOPNOTSUPP; 123 } 124 } 125