1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * |
4 | * Copyright IBM Corp. 2007 |
5 | * Copyright 2011 Freescale Semiconductor, Inc. |
6 | * |
7 | * Authors: Hollis Blanchard <hollisb@us.ibm.com> |
8 | */ |
9 | |
10 | #include <linux/jiffies.h> |
11 | #include <linux/hrtimer.h> |
12 | #include <linux/types.h> |
13 | #include <linux/string.h> |
14 | #include <linux/kvm_host.h> |
15 | #include <linux/clockchips.h> |
16 | |
17 | #include <asm/reg.h> |
18 | #include <asm/time.h> |
19 | #include <asm/byteorder.h> |
20 | #include <asm/kvm_ppc.h> |
21 | #include <asm/disassemble.h> |
22 | #include <asm/ppc-opcode.h> |
23 | #include <asm/sstep.h> |
24 | #include "timing.h" |
25 | #include "trace.h" |
26 | |
27 | #ifdef CONFIG_PPC_FPU |
28 | static bool kvmppc_check_fp_disabled(struct kvm_vcpu *vcpu) |
29 | { |
30 | if (!(kvmppc_get_msr(vcpu) & MSR_FP)) { |
31 | kvmppc_core_queue_fpunavail(vcpu, kvmppc_get_msr(vcpu) & SRR1_PREFIXED); |
32 | return true; |
33 | } |
34 | |
35 | return false; |
36 | } |
37 | #endif /* CONFIG_PPC_FPU */ |
38 | |
39 | #ifdef CONFIG_VSX |
40 | static bool kvmppc_check_vsx_disabled(struct kvm_vcpu *vcpu) |
41 | { |
42 | if (!(kvmppc_get_msr(vcpu) & MSR_VSX)) { |
43 | kvmppc_core_queue_vsx_unavail(vcpu, kvmppc_get_msr(vcpu) & SRR1_PREFIXED); |
44 | return true; |
45 | } |
46 | |
47 | return false; |
48 | } |
49 | #endif /* CONFIG_VSX */ |
50 | |
51 | #ifdef CONFIG_ALTIVEC |
52 | static bool kvmppc_check_altivec_disabled(struct kvm_vcpu *vcpu) |
53 | { |
54 | if (!(kvmppc_get_msr(vcpu) & MSR_VEC)) { |
55 | kvmppc_core_queue_vec_unavail(vcpu, kvmppc_get_msr(vcpu) & SRR1_PREFIXED); |
56 | return true; |
57 | } |
58 | |
59 | return false; |
60 | } |
61 | #endif /* CONFIG_ALTIVEC */ |
62 | |
63 | /* |
64 | * XXX to do: |
65 | * lfiwax, lfiwzx |
66 | * vector loads and stores |
67 | * |
68 | * Instructions that trap when used on cache-inhibited mappings |
69 | * are not emulated here: multiple and string instructions, |
70 | * lq/stq, and the load-reserve/store-conditional instructions. |
71 | */ |
72 | int kvmppc_emulate_loadstore(struct kvm_vcpu *vcpu) |
73 | { |
74 | ppc_inst_t inst; |
75 | enum emulation_result emulated = EMULATE_FAIL; |
76 | struct instruction_op op; |
77 | |
78 | /* this default type might be overwritten by subcategories */ |
79 | kvmppc_set_exit_type(vcpu, type: EMULATED_INST_EXITS); |
80 | |
81 | emulated = kvmppc_get_last_inst(vcpu, INST_GENERIC, &inst); |
82 | if (emulated != EMULATE_DONE) |
83 | return emulated; |
84 | |
85 | vcpu->arch.mmio_vsx_copy_nums = 0; |
86 | vcpu->arch.mmio_vsx_offset = 0; |
87 | vcpu->arch.mmio_copy_type = KVMPPC_VSX_COPY_NONE; |
88 | vcpu->arch.mmio_sp64_extend = 0; |
89 | vcpu->arch.mmio_sign_extend = 0; |
90 | vcpu->arch.mmio_vmx_copy_nums = 0; |
91 | vcpu->arch.mmio_vmx_offset = 0; |
92 | vcpu->arch.mmio_host_swabbed = 0; |
93 | |
94 | emulated = EMULATE_FAIL; |
95 | vcpu->arch.regs.msr = kvmppc_get_msr(vcpu); |
96 | if (analyse_instr(&op, &vcpu->arch.regs, inst) == 0) { |
97 | int type = op.type & INSTR_TYPE_MASK; |
98 | int size = GETSIZE(op.type); |
99 | |
100 | vcpu->mmio_is_write = OP_IS_STORE(type); |
101 | |
102 | switch (type) { |
103 | case LOAD: { |
104 | int instr_byte_swap = op.type & BYTEREV; |
105 | |
106 | if (op.type & SIGNEXT) |
107 | emulated = kvmppc_handle_loads(vcpu, |
108 | op.reg, size, !instr_byte_swap); |
109 | else |
110 | emulated = kvmppc_handle_load(vcpu, |
111 | op.reg, size, !instr_byte_swap); |
112 | |
113 | if ((op.type & UPDATE) && (emulated != EMULATE_FAIL)) |
114 | kvmppc_set_gpr(vcpu, op.update_reg, vcpu->arch.vaddr_accessed); |
115 | |
116 | break; |
117 | } |
118 | #ifdef CONFIG_PPC_FPU |
119 | case LOAD_FP: |
120 | if (kvmppc_check_fp_disabled(vcpu)) |
121 | return EMULATE_DONE; |
122 | |
123 | if (op.type & FPCONV) |
124 | vcpu->arch.mmio_sp64_extend = 1; |
125 | |
126 | if (op.type & SIGNEXT) |
127 | emulated = kvmppc_handle_loads(vcpu, |
128 | KVM_MMIO_REG_FPR|op.reg, size, 1); |
129 | else |
130 | emulated = kvmppc_handle_load(vcpu, |
131 | KVM_MMIO_REG_FPR|op.reg, size, 1); |
132 | |
133 | if ((op.type & UPDATE) && (emulated != EMULATE_FAIL)) |
134 | kvmppc_set_gpr(vcpu, op.update_reg, vcpu->arch.vaddr_accessed); |
135 | |
136 | break; |
137 | #endif |
138 | #ifdef CONFIG_ALTIVEC |
139 | case LOAD_VMX: |
140 | if (kvmppc_check_altivec_disabled(vcpu)) |
141 | return EMULATE_DONE; |
142 | |
143 | /* Hardware enforces alignment of VMX accesses */ |
144 | vcpu->arch.vaddr_accessed &= ~((unsigned long)size - 1); |
145 | vcpu->arch.paddr_accessed &= ~((unsigned long)size - 1); |
146 | |
147 | if (size == 16) { /* lvx */ |
148 | vcpu->arch.mmio_copy_type = |
149 | KVMPPC_VMX_COPY_DWORD; |
150 | } else if (size == 4) { /* lvewx */ |
151 | vcpu->arch.mmio_copy_type = |
152 | KVMPPC_VMX_COPY_WORD; |
153 | } else if (size == 2) { /* lvehx */ |
154 | vcpu->arch.mmio_copy_type = |
155 | KVMPPC_VMX_COPY_HWORD; |
156 | } else if (size == 1) { /* lvebx */ |
157 | vcpu->arch.mmio_copy_type = |
158 | KVMPPC_VMX_COPY_BYTE; |
159 | } else |
160 | break; |
161 | |
162 | vcpu->arch.mmio_vmx_offset = |
163 | (vcpu->arch.vaddr_accessed & 0xf)/size; |
164 | |
165 | if (size == 16) { |
166 | vcpu->arch.mmio_vmx_copy_nums = 2; |
167 | emulated = kvmppc_handle_vmx_load(vcpu, |
168 | KVM_MMIO_REG_VMX|op.reg, |
169 | 8, 1); |
170 | } else { |
171 | vcpu->arch.mmio_vmx_copy_nums = 1; |
172 | emulated = kvmppc_handle_vmx_load(vcpu, |
173 | KVM_MMIO_REG_VMX|op.reg, |
174 | size, 1); |
175 | } |
176 | break; |
177 | #endif |
178 | #ifdef CONFIG_VSX |
179 | case LOAD_VSX: { |
180 | int io_size_each; |
181 | |
182 | if (op.vsx_flags & VSX_CHECK_VEC) { |
183 | if (kvmppc_check_altivec_disabled(vcpu)) |
184 | return EMULATE_DONE; |
185 | } else { |
186 | if (kvmppc_check_vsx_disabled(vcpu)) |
187 | return EMULATE_DONE; |
188 | } |
189 | |
190 | if (op.vsx_flags & VSX_FPCONV) |
191 | vcpu->arch.mmio_sp64_extend = 1; |
192 | |
193 | if (op.element_size == 8) { |
194 | if (op.vsx_flags & VSX_SPLAT) |
195 | vcpu->arch.mmio_copy_type = |
196 | KVMPPC_VSX_COPY_DWORD_LOAD_DUMP; |
197 | else |
198 | vcpu->arch.mmio_copy_type = |
199 | KVMPPC_VSX_COPY_DWORD; |
200 | } else if (op.element_size == 4) { |
201 | if (op.vsx_flags & VSX_SPLAT) |
202 | vcpu->arch.mmio_copy_type = |
203 | KVMPPC_VSX_COPY_WORD_LOAD_DUMP; |
204 | else |
205 | vcpu->arch.mmio_copy_type = |
206 | KVMPPC_VSX_COPY_WORD; |
207 | } else |
208 | break; |
209 | |
210 | if (size < op.element_size) { |
211 | /* precision convert case: lxsspx, etc */ |
212 | vcpu->arch.mmio_vsx_copy_nums = 1; |
213 | io_size_each = size; |
214 | } else { /* lxvw4x, lxvd2x, etc */ |
215 | vcpu->arch.mmio_vsx_copy_nums = |
216 | size/op.element_size; |
217 | io_size_each = op.element_size; |
218 | } |
219 | |
220 | emulated = kvmppc_handle_vsx_load(vcpu, |
221 | KVM_MMIO_REG_VSX|op.reg, io_size_each, |
222 | 1, op.type & SIGNEXT); |
223 | break; |
224 | } |
225 | #endif |
226 | case STORE: { |
227 | int instr_byte_swap = op.type & BYTEREV; |
228 | |
229 | emulated = kvmppc_handle_store(vcpu, kvmppc_get_gpr(vcpu, op.reg), |
230 | size, !instr_byte_swap); |
231 | |
232 | if ((op.type & UPDATE) && (emulated != EMULATE_FAIL)) |
233 | kvmppc_set_gpr(vcpu, op.update_reg, vcpu->arch.vaddr_accessed); |
234 | |
235 | break; |
236 | } |
237 | #ifdef CONFIG_PPC_FPU |
238 | case STORE_FP: |
239 | if (kvmppc_check_fp_disabled(vcpu)) |
240 | return EMULATE_DONE; |
241 | |
242 | /* The FP registers need to be flushed so that |
243 | * kvmppc_handle_store() can read actual FP vals |
244 | * from vcpu->arch. |
245 | */ |
246 | if (vcpu->kvm->arch.kvm_ops->giveup_ext) |
247 | vcpu->kvm->arch.kvm_ops->giveup_ext(vcpu, |
248 | MSR_FP); |
249 | |
250 | if (op.type & FPCONV) |
251 | vcpu->arch.mmio_sp64_extend = 1; |
252 | |
253 | emulated = kvmppc_handle_store(vcpu, |
254 | kvmppc_get_fpr(vcpu, op.reg), size, 1); |
255 | |
256 | if ((op.type & UPDATE) && (emulated != EMULATE_FAIL)) |
257 | kvmppc_set_gpr(vcpu, op.update_reg, vcpu->arch.vaddr_accessed); |
258 | |
259 | break; |
260 | #endif |
261 | #ifdef CONFIG_ALTIVEC |
262 | case STORE_VMX: |
263 | if (kvmppc_check_altivec_disabled(vcpu)) |
264 | return EMULATE_DONE; |
265 | |
266 | /* Hardware enforces alignment of VMX accesses. */ |
267 | vcpu->arch.vaddr_accessed &= ~((unsigned long)size - 1); |
268 | vcpu->arch.paddr_accessed &= ~((unsigned long)size - 1); |
269 | |
270 | if (vcpu->kvm->arch.kvm_ops->giveup_ext) |
271 | vcpu->kvm->arch.kvm_ops->giveup_ext(vcpu, |
272 | MSR_VEC); |
273 | if (size == 16) { /* stvx */ |
274 | vcpu->arch.mmio_copy_type = |
275 | KVMPPC_VMX_COPY_DWORD; |
276 | } else if (size == 4) { /* stvewx */ |
277 | vcpu->arch.mmio_copy_type = |
278 | KVMPPC_VMX_COPY_WORD; |
279 | } else if (size == 2) { /* stvehx */ |
280 | vcpu->arch.mmio_copy_type = |
281 | KVMPPC_VMX_COPY_HWORD; |
282 | } else if (size == 1) { /* stvebx */ |
283 | vcpu->arch.mmio_copy_type = |
284 | KVMPPC_VMX_COPY_BYTE; |
285 | } else |
286 | break; |
287 | |
288 | vcpu->arch.mmio_vmx_offset = |
289 | (vcpu->arch.vaddr_accessed & 0xf)/size; |
290 | |
291 | if (size == 16) { |
292 | vcpu->arch.mmio_vmx_copy_nums = 2; |
293 | emulated = kvmppc_handle_vmx_store(vcpu, |
294 | op.reg, 8, 1); |
295 | } else { |
296 | vcpu->arch.mmio_vmx_copy_nums = 1; |
297 | emulated = kvmppc_handle_vmx_store(vcpu, |
298 | op.reg, size, 1); |
299 | } |
300 | |
301 | break; |
302 | #endif |
303 | #ifdef CONFIG_VSX |
304 | case STORE_VSX: { |
305 | int io_size_each; |
306 | |
307 | if (op.vsx_flags & VSX_CHECK_VEC) { |
308 | if (kvmppc_check_altivec_disabled(vcpu)) |
309 | return EMULATE_DONE; |
310 | } else { |
311 | if (kvmppc_check_vsx_disabled(vcpu)) |
312 | return EMULATE_DONE; |
313 | } |
314 | |
315 | if (vcpu->kvm->arch.kvm_ops->giveup_ext) |
316 | vcpu->kvm->arch.kvm_ops->giveup_ext(vcpu, |
317 | MSR_VSX); |
318 | |
319 | if (op.vsx_flags & VSX_FPCONV) |
320 | vcpu->arch.mmio_sp64_extend = 1; |
321 | |
322 | if (op.element_size == 8) |
323 | vcpu->arch.mmio_copy_type = |
324 | KVMPPC_VSX_COPY_DWORD; |
325 | else if (op.element_size == 4) |
326 | vcpu->arch.mmio_copy_type = |
327 | KVMPPC_VSX_COPY_WORD; |
328 | else |
329 | break; |
330 | |
331 | if (size < op.element_size) { |
332 | /* precise conversion case, like stxsspx */ |
333 | vcpu->arch.mmio_vsx_copy_nums = 1; |
334 | io_size_each = size; |
335 | } else { /* stxvw4x, stxvd2x, etc */ |
336 | vcpu->arch.mmio_vsx_copy_nums = |
337 | size/op.element_size; |
338 | io_size_each = op.element_size; |
339 | } |
340 | |
341 | emulated = kvmppc_handle_vsx_store(vcpu, |
342 | op.reg, io_size_each, 1); |
343 | break; |
344 | } |
345 | #endif |
346 | case CACHEOP: |
347 | /* Do nothing. The guest is performing dcbi because |
348 | * hardware DMA is not snooped by the dcache, but |
349 | * emulated DMA either goes through the dcache as |
350 | * normal writes, or the host kernel has handled dcache |
351 | * coherence. |
352 | */ |
353 | emulated = EMULATE_DONE; |
354 | break; |
355 | default: |
356 | break; |
357 | } |
358 | } |
359 | |
360 | trace_kvm_ppc_instr(inst: ppc_inst_val(inst), pc: kvmppc_get_pc(vcpu), emulate: emulated); |
361 | |
362 | /* Advance past emulated instruction. */ |
363 | if (emulated != EMULATE_FAIL) |
364 | kvmppc_set_pc(vcpu, kvmppc_get_pc(vcpu) + ppc_inst_len(inst)); |
365 | |
366 | return emulated; |
367 | } |
368 | |