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