xref: /linux/arch/s390/kvm/intercept.c (revision c4ee0af3fa0dc65f690fc908f02b8355f9576ea0)
1 /*
2  * in-kernel handling for sie intercepts
3  *
4  * Copyright IBM Corp. 2008, 2009
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_host.h>
15 #include <linux/errno.h>
16 #include <linux/pagemap.h>
17 
18 #include <asm/kvm_host.h>
19 
20 #include "kvm-s390.h"
21 #include "gaccess.h"
22 #include "trace.h"
23 #include "trace-s390.h"
24 
25 
26 static const intercept_handler_t instruction_handlers[256] = {
27 	[0x01] = kvm_s390_handle_01,
28 	[0x82] = kvm_s390_handle_lpsw,
29 	[0x83] = kvm_s390_handle_diag,
30 	[0xae] = kvm_s390_handle_sigp,
31 	[0xb2] = kvm_s390_handle_b2,
32 	[0xb7] = kvm_s390_handle_lctl,
33 	[0xb9] = kvm_s390_handle_b9,
34 	[0xe5] = kvm_s390_handle_e5,
35 	[0xeb] = kvm_s390_handle_eb,
36 };
37 
38 static int handle_noop(struct kvm_vcpu *vcpu)
39 {
40 	switch (vcpu->arch.sie_block->icptcode) {
41 	case 0x0:
42 		vcpu->stat.exit_null++;
43 		break;
44 	case 0x10:
45 		vcpu->stat.exit_external_request++;
46 		break;
47 	case 0x14:
48 		vcpu->stat.exit_external_interrupt++;
49 		break;
50 	default:
51 		break; /* nothing */
52 	}
53 	return 0;
54 }
55 
56 static int handle_stop(struct kvm_vcpu *vcpu)
57 {
58 	int rc = 0;
59 
60 	vcpu->stat.exit_stop_request++;
61 	spin_lock_bh(&vcpu->arch.local_int.lock);
62 
63 	trace_kvm_s390_stop_request(vcpu->arch.local_int.action_bits);
64 
65 	if (vcpu->arch.local_int.action_bits & ACTION_STOP_ON_STOP) {
66 		atomic_set_mask(CPUSTAT_STOPPED,
67 				&vcpu->arch.sie_block->cpuflags);
68 		vcpu->arch.local_int.action_bits &= ~ACTION_STOP_ON_STOP;
69 		VCPU_EVENT(vcpu, 3, "%s", "cpu stopped");
70 		rc = -EOPNOTSUPP;
71 	}
72 
73 	if (vcpu->arch.local_int.action_bits & ACTION_STORE_ON_STOP) {
74 		vcpu->arch.local_int.action_bits &= ~ACTION_STORE_ON_STOP;
75 		/* store status must be called unlocked. Since local_int.lock
76 		 * only protects local_int.* and not guest memory we can give
77 		 * up the lock here */
78 		spin_unlock_bh(&vcpu->arch.local_int.lock);
79 		rc = kvm_s390_vcpu_store_status(vcpu,
80 						KVM_S390_STORE_STATUS_NOADDR);
81 		if (rc >= 0)
82 			rc = -EOPNOTSUPP;
83 	} else
84 		spin_unlock_bh(&vcpu->arch.local_int.lock);
85 	return rc;
86 }
87 
88 static int handle_validity(struct kvm_vcpu *vcpu)
89 {
90 	int viwhy = vcpu->arch.sie_block->ipb >> 16;
91 
92 	vcpu->stat.exit_validity++;
93 	trace_kvm_s390_intercept_validity(vcpu, viwhy);
94 	WARN_ONCE(true, "kvm: unhandled validity intercept 0x%x\n", viwhy);
95 	return -EOPNOTSUPP;
96 }
97 
98 static int handle_instruction(struct kvm_vcpu *vcpu)
99 {
100 	intercept_handler_t handler;
101 
102 	vcpu->stat.exit_instruction++;
103 	trace_kvm_s390_intercept_instruction(vcpu,
104 					     vcpu->arch.sie_block->ipa,
105 					     vcpu->arch.sie_block->ipb);
106 	handler = instruction_handlers[vcpu->arch.sie_block->ipa >> 8];
107 	if (handler)
108 		return handler(vcpu);
109 	return -EOPNOTSUPP;
110 }
111 
112 static int handle_prog(struct kvm_vcpu *vcpu)
113 {
114 	vcpu->stat.exit_program_interruption++;
115 	trace_kvm_s390_intercept_prog(vcpu, vcpu->arch.sie_block->iprcc);
116 	return kvm_s390_inject_program_int(vcpu, vcpu->arch.sie_block->iprcc);
117 }
118 
119 static int handle_instruction_and_prog(struct kvm_vcpu *vcpu)
120 {
121 	int rc, rc2;
122 
123 	vcpu->stat.exit_instr_and_program++;
124 	rc = handle_instruction(vcpu);
125 	rc2 = handle_prog(vcpu);
126 
127 	if (rc == -EOPNOTSUPP)
128 		vcpu->arch.sie_block->icptcode = 0x04;
129 	if (rc)
130 		return rc;
131 	return rc2;
132 }
133 
134 static const intercept_handler_t intercept_funcs[] = {
135 	[0x00 >> 2] = handle_noop,
136 	[0x04 >> 2] = handle_instruction,
137 	[0x08 >> 2] = handle_prog,
138 	[0x0C >> 2] = handle_instruction_and_prog,
139 	[0x10 >> 2] = handle_noop,
140 	[0x14 >> 2] = handle_noop,
141 	[0x18 >> 2] = handle_noop,
142 	[0x1C >> 2] = kvm_s390_handle_wait,
143 	[0x20 >> 2] = handle_validity,
144 	[0x28 >> 2] = handle_stop,
145 };
146 
147 int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu)
148 {
149 	intercept_handler_t func;
150 	u8 code = vcpu->arch.sie_block->icptcode;
151 
152 	if (code & 3 || (code >> 2) >= ARRAY_SIZE(intercept_funcs))
153 		return -EOPNOTSUPP;
154 	func = intercept_funcs[code >> 2];
155 	if (func)
156 		return func(vcpu);
157 	return -EOPNOTSUPP;
158 }
159