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
28static 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
40static 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
52static 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 */
72int 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, 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(ppc_inst_val(inst), kvmppc_get_pc(vcpu), 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