1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * handling diagnose instructions |
4 | * |
5 | * Copyright IBM Corp. 2008, 2020 |
6 | * |
7 | * Author(s): Carsten Otte <cotte@de.ibm.com> |
8 | * Christian Borntraeger <borntraeger@de.ibm.com> |
9 | */ |
10 | |
11 | #include <linux/kvm.h> |
12 | #include <linux/kvm_host.h> |
13 | #include <asm/gmap.h> |
14 | #include <asm/virtio-ccw.h> |
15 | #include "kvm-s390.h" |
16 | #include "trace.h" |
17 | #include "trace-s390.h" |
18 | #include "gaccess.h" |
19 | |
20 | static int diag_release_pages(struct kvm_vcpu *vcpu) |
21 | { |
22 | unsigned long start, end; |
23 | unsigned long prefix = kvm_s390_get_prefix(vcpu); |
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] + PAGE_SIZE; |
27 | vcpu->stat.instruction_diagnose_10++; |
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 | |
35 | /* |
36 | * We checked for start >= end above, so lets check for the |
37 | * fast path (no prefix swap page involved) |
38 | */ |
39 | if (end <= prefix || start >= prefix + 2 * PAGE_SIZE) { |
40 | gmap_discard(vcpu->arch.gmap, start, end); |
41 | } else { |
42 | /* |
43 | * This is slow path. gmap_discard will check for start |
44 | * so lets split this into before prefix, prefix, after |
45 | * prefix and let gmap_discard make some of these calls |
46 | * NOPs. |
47 | */ |
48 | gmap_discard(vcpu->arch.gmap, start, prefix); |
49 | if (start <= prefix) |
50 | gmap_discard(vcpu->arch.gmap, 0, PAGE_SIZE); |
51 | if (end > prefix + PAGE_SIZE) |
52 | gmap_discard(vcpu->arch.gmap, PAGE_SIZE, 2 * PAGE_SIZE); |
53 | gmap_discard(vcpu->arch.gmap, prefix + 2 * PAGE_SIZE, end); |
54 | } |
55 | return 0; |
56 | } |
57 | |
58 | static int __diag_page_ref_service(struct kvm_vcpu *vcpu) |
59 | { |
60 | struct prs_parm { |
61 | u16 code; |
62 | u16 subcode; |
63 | u16 parm_len; |
64 | u16 parm_version; |
65 | u64 token_addr; |
66 | u64 select_mask; |
67 | u64 compare_mask; |
68 | u64 zarch; |
69 | }; |
70 | struct prs_parm parm; |
71 | int rc; |
72 | u16 rx = (vcpu->arch.sie_block->ipa & 0xf0) >> 4; |
73 | u16 ry = (vcpu->arch.sie_block->ipa & 0x0f); |
74 | |
75 | VCPU_EVENT(vcpu, 3, "diag page reference parameter block at 0x%llx" , |
76 | vcpu->run->s.regs.gprs[rx]); |
77 | vcpu->stat.instruction_diagnose_258++; |
78 | if (vcpu->run->s.regs.gprs[rx] & 7) |
79 | return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); |
80 | rc = read_guest(vcpu, ga: vcpu->run->s.regs.gprs[rx], ar: rx, data: &parm, len: sizeof(parm)); |
81 | if (rc) |
82 | return kvm_s390_inject_prog_cond(vcpu, rc); |
83 | if (parm.parm_version != 2 || parm.parm_len < 5 || parm.code != 0x258) |
84 | return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); |
85 | |
86 | switch (parm.subcode) { |
87 | case 0: /* TOKEN */ |
88 | VCPU_EVENT(vcpu, 3, "pageref token addr 0x%llx " |
89 | "select mask 0x%llx compare mask 0x%llx" , |
90 | parm.token_addr, parm.select_mask, parm.compare_mask); |
91 | if (vcpu->arch.pfault_token != KVM_S390_PFAULT_TOKEN_INVALID) { |
92 | /* |
93 | * If the pagefault handshake is already activated, |
94 | * the token must not be changed. We have to return |
95 | * decimal 8 instead, as mandated in SC24-6084. |
96 | */ |
97 | vcpu->run->s.regs.gprs[ry] = 8; |
98 | return 0; |
99 | } |
100 | |
101 | if ((parm.compare_mask & parm.select_mask) != parm.compare_mask || |
102 | parm.token_addr & 7 || parm.zarch != 0x8000000000000000ULL) |
103 | return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); |
104 | |
105 | if (!kvm_is_gpa_in_memslot(vcpu->kvm, parm.token_addr)) |
106 | return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); |
107 | |
108 | vcpu->arch.pfault_token = parm.token_addr; |
109 | vcpu->arch.pfault_select = parm.select_mask; |
110 | vcpu->arch.pfault_compare = parm.compare_mask; |
111 | vcpu->run->s.regs.gprs[ry] = 0; |
112 | rc = 0; |
113 | break; |
114 | case 1: /* |
115 | * CANCEL |
116 | * Specification allows to let already pending tokens survive |
117 | * the cancel, therefore to reduce code complexity, we assume |
118 | * all outstanding tokens are already pending. |
119 | */ |
120 | VCPU_EVENT(vcpu, 3, "pageref cancel addr 0x%llx" , parm.token_addr); |
121 | if (parm.token_addr || parm.select_mask || |
122 | parm.compare_mask || parm.zarch) |
123 | return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); |
124 | |
125 | vcpu->run->s.regs.gprs[ry] = 0; |
126 | /* |
127 | * If the pfault handling was not established or is already |
128 | * canceled SC24-6084 requests to return decimal 4. |
129 | */ |
130 | if (vcpu->arch.pfault_token == KVM_S390_PFAULT_TOKEN_INVALID) |
131 | vcpu->run->s.regs.gprs[ry] = 4; |
132 | else |
133 | vcpu->arch.pfault_token = KVM_S390_PFAULT_TOKEN_INVALID; |
134 | |
135 | rc = 0; |
136 | break; |
137 | default: |
138 | rc = -EOPNOTSUPP; |
139 | break; |
140 | } |
141 | |
142 | return rc; |
143 | } |
144 | |
145 | static int __diag_time_slice_end(struct kvm_vcpu *vcpu) |
146 | { |
147 | VCPU_EVENT(vcpu, 5, "%s" , "diag time slice end" ); |
148 | vcpu->stat.instruction_diagnose_44++; |
149 | kvm_vcpu_on_spin(vcpu, yield_to_kernel_mode: true); |
150 | return 0; |
151 | } |
152 | |
153 | static int forward_cnt; |
154 | static unsigned long cur_slice; |
155 | |
156 | static int diag9c_forwarding_overrun(void) |
157 | { |
158 | /* Reset the count on a new slice */ |
159 | if (time_after(jiffies, cur_slice)) { |
160 | cur_slice = jiffies; |
161 | forward_cnt = diag9c_forwarding_hz / HZ; |
162 | } |
163 | return forward_cnt-- <= 0 ? 1 : 0; |
164 | } |
165 | |
166 | static int __diag_time_slice_end_directed(struct kvm_vcpu *vcpu) |
167 | { |
168 | struct kvm_vcpu *tcpu; |
169 | int tcpu_cpu; |
170 | int tid; |
171 | |
172 | tid = vcpu->run->s.regs.gprs[(vcpu->arch.sie_block->ipa & 0xf0) >> 4]; |
173 | vcpu->stat.instruction_diagnose_9c++; |
174 | |
175 | /* yield to self */ |
176 | if (tid == vcpu->vcpu_id) |
177 | goto no_yield; |
178 | |
179 | /* yield to invalid */ |
180 | tcpu = kvm_get_vcpu_by_id(kvm: vcpu->kvm, id: tid); |
181 | if (!tcpu) |
182 | goto no_yield; |
183 | |
184 | /* target guest VCPU already running */ |
185 | tcpu_cpu = READ_ONCE(tcpu->cpu); |
186 | if (tcpu_cpu >= 0) { |
187 | if (!diag9c_forwarding_hz || diag9c_forwarding_overrun()) |
188 | goto no_yield; |
189 | |
190 | /* target host CPU already running */ |
191 | if (!vcpu_is_preempted(cpu: tcpu_cpu)) |
192 | goto no_yield; |
193 | smp_yield_cpu(tcpu_cpu); |
194 | VCPU_EVENT(vcpu, 5, |
195 | "diag time slice end directed to %d: yield forwarded" , |
196 | tid); |
197 | vcpu->stat.diag_9c_forward++; |
198 | return 0; |
199 | } |
200 | |
201 | if (kvm_vcpu_yield_to(target: tcpu) <= 0) |
202 | goto no_yield; |
203 | |
204 | VCPU_EVENT(vcpu, 5, "diag time slice end directed to %d: done" , tid); |
205 | return 0; |
206 | no_yield: |
207 | VCPU_EVENT(vcpu, 5, "diag time slice end directed to %d: ignored" , tid); |
208 | vcpu->stat.diag_9c_ignored++; |
209 | return 0; |
210 | } |
211 | |
212 | static int __diag_ipl_functions(struct kvm_vcpu *vcpu) |
213 | { |
214 | unsigned int reg = vcpu->arch.sie_block->ipa & 0xf; |
215 | unsigned long subcode = vcpu->run->s.regs.gprs[reg] & 0xffff; |
216 | |
217 | VCPU_EVENT(vcpu, 3, "diag ipl functions, subcode %lx" , subcode); |
218 | vcpu->stat.instruction_diagnose_308++; |
219 | switch (subcode) { |
220 | case 3: |
221 | vcpu->run->s390_reset_flags = KVM_S390_RESET_CLEAR; |
222 | break; |
223 | case 4: |
224 | vcpu->run->s390_reset_flags = 0; |
225 | break; |
226 | default: |
227 | return -EOPNOTSUPP; |
228 | } |
229 | |
230 | /* |
231 | * no need to check the return value of vcpu_stop as it can only have |
232 | * an error for protvirt, but protvirt means user cpu state |
233 | */ |
234 | if (!kvm_s390_user_cpu_state_ctrl(kvm: vcpu->kvm)) |
235 | kvm_s390_vcpu_stop(vcpu); |
236 | vcpu->run->s390_reset_flags |= KVM_S390_RESET_SUBSYSTEM; |
237 | vcpu->run->s390_reset_flags |= KVM_S390_RESET_IPL; |
238 | vcpu->run->s390_reset_flags |= KVM_S390_RESET_CPU_INIT; |
239 | vcpu->run->exit_reason = KVM_EXIT_S390_RESET; |
240 | VCPU_EVENT(vcpu, 3, "requesting userspace resets %llx" , |
241 | vcpu->run->s390_reset_flags); |
242 | trace_kvm_s390_request_resets(resets: vcpu->run->s390_reset_flags); |
243 | return -EREMOTE; |
244 | } |
245 | |
246 | static int __diag_virtio_hypercall(struct kvm_vcpu *vcpu) |
247 | { |
248 | int ret; |
249 | |
250 | vcpu->stat.instruction_diagnose_500++; |
251 | /* No virtio-ccw notification? Get out quickly. */ |
252 | if (!vcpu->kvm->arch.css_support || |
253 | (vcpu->run->s.regs.gprs[1] != KVM_S390_VIRTIO_CCW_NOTIFY)) |
254 | return -EOPNOTSUPP; |
255 | |
256 | VCPU_EVENT(vcpu, 4, "diag 0x500 schid 0x%8.8x queue 0x%x cookie 0x%llx" , |
257 | (u32) vcpu->run->s.regs.gprs[2], |
258 | (u32) vcpu->run->s.regs.gprs[3], |
259 | vcpu->run->s.regs.gprs[4]); |
260 | |
261 | /* |
262 | * The layout is as follows: |
263 | * - gpr 2 contains the subchannel id (passed as addr) |
264 | * - gpr 3 contains the virtqueue index (passed as datamatch) |
265 | * - gpr 4 contains the index on the bus (optionally) |
266 | */ |
267 | ret = kvm_io_bus_write_cookie(vcpu, bus_idx: KVM_VIRTIO_CCW_NOTIFY_BUS, |
268 | addr: vcpu->run->s.regs.gprs[2] & 0xffffffff, |
269 | len: 8, val: &vcpu->run->s.regs.gprs[3], |
270 | cookie: vcpu->run->s.regs.gprs[4]); |
271 | |
272 | /* |
273 | * Return cookie in gpr 2, but don't overwrite the register if the |
274 | * diagnose will be handled by userspace. |
275 | */ |
276 | if (ret != -EOPNOTSUPP) |
277 | vcpu->run->s.regs.gprs[2] = ret; |
278 | /* kvm_io_bus_write_cookie returns -EOPNOTSUPP if it found no match. */ |
279 | return ret < 0 ? ret : 0; |
280 | } |
281 | |
282 | int kvm_s390_handle_diag(struct kvm_vcpu *vcpu) |
283 | { |
284 | int code = kvm_s390_get_base_disp_rs(vcpu, NULL) & 0xffff; |
285 | |
286 | if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) |
287 | return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); |
288 | |
289 | trace_kvm_s390_handle_diag(vcpu, code); |
290 | switch (code) { |
291 | case 0x10: |
292 | return diag_release_pages(vcpu); |
293 | case 0x44: |
294 | return __diag_time_slice_end(vcpu); |
295 | case 0x9c: |
296 | return __diag_time_slice_end_directed(vcpu); |
297 | case 0x258: |
298 | return __diag_page_ref_service(vcpu); |
299 | case 0x308: |
300 | return __diag_ipl_functions(vcpu); |
301 | case 0x500: |
302 | return __diag_virtio_hypercall(vcpu); |
303 | default: |
304 | vcpu->stat.instruction_diagnose_other++; |
305 | return -EOPNOTSUPP; |
306 | } |
307 | } |
308 | |