xref: /openbmc/linux/arch/s390/kvm/diag.c (revision 7e035230)
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