1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License, version 2, as
4 * published by the Free Software Foundation.
5 *
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9 * GNU General Public License for more details.
10 *
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software
13 * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
14 *
15 * Copyright IBM Corp. 2007
16 * Copyright 2011 Freescale Semiconductor, Inc.
17 *
18 * Authors: Hollis Blanchard <hollisb@us.ibm.com>
19 */
20
21#include <linux/jiffies.h>
22#include <linux/hrtimer.h>
23#include <linux/types.h>
24#include <linux/string.h>
25#include <linux/kvm_host.h>
26#include <linux/clockchips.h>
27
28#include <asm/reg.h>
29#include <asm/time.h>
30#include <asm/byteorder.h>
31#include <asm/kvm_ppc.h>
32#include <asm/disassemble.h>
33#include <asm/ppc-opcode.h>
34#include "timing.h"
35#include "trace.h"
36
37/* XXX to do:
38 * lhax
39 * lhaux
40 * lswx
41 * lswi
42 * stswx
43 * stswi
44 * lha
45 * lhau
46 * lmw
47 * stmw
48 *
49 */
50int kvmppc_emulate_loadstore(struct kvm_vcpu *vcpu)
51{
52	struct kvm_run *run = vcpu->run;
53	u32 inst;
54	int ra, rs, rt;
55	enum emulation_result emulated;
56	int advance = 1;
57
58	/* this default type might be overwritten by subcategories */
59	kvmppc_set_exit_type(vcpu, EMULATED_INST_EXITS);
60
61	emulated = kvmppc_get_last_inst(vcpu, INST_GENERIC, &inst);
62	if (emulated != EMULATE_DONE)
63		return emulated;
64
65	ra = get_ra(inst);
66	rs = get_rs(inst);
67	rt = get_rt(inst);
68
69	switch (get_op(inst)) {
70	case 31:
71		switch (get_xop(inst)) {
72		case OP_31_XOP_LWZX:
73			emulated = kvmppc_handle_load(run, vcpu, rt, 4, 1);
74			break;
75
76		case OP_31_XOP_LBZX:
77			emulated = kvmppc_handle_load(run, vcpu, rt, 1, 1);
78			break;
79
80		case OP_31_XOP_LBZUX:
81			emulated = kvmppc_handle_load(run, vcpu, rt, 1, 1);
82			kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
83			break;
84
85		case OP_31_XOP_STWX:
86			emulated = kvmppc_handle_store(run, vcpu,
87						       kvmppc_get_gpr(vcpu, rs),
88			                               4, 1);
89			break;
90
91		case OP_31_XOP_STBX:
92			emulated = kvmppc_handle_store(run, vcpu,
93						       kvmppc_get_gpr(vcpu, rs),
94			                               1, 1);
95			break;
96
97		case OP_31_XOP_STBUX:
98			emulated = kvmppc_handle_store(run, vcpu,
99						       kvmppc_get_gpr(vcpu, rs),
100			                               1, 1);
101			kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
102			break;
103
104		case OP_31_XOP_LHAX:
105			emulated = kvmppc_handle_loads(run, vcpu, rt, 2, 1);
106			break;
107
108		case OP_31_XOP_LHZX:
109			emulated = kvmppc_handle_load(run, vcpu, rt, 2, 1);
110			break;
111
112		case OP_31_XOP_LHZUX:
113			emulated = kvmppc_handle_load(run, vcpu, rt, 2, 1);
114			kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
115			break;
116
117		case OP_31_XOP_STHX:
118			emulated = kvmppc_handle_store(run, vcpu,
119						       kvmppc_get_gpr(vcpu, rs),
120			                               2, 1);
121			break;
122
123		case OP_31_XOP_STHUX:
124			emulated = kvmppc_handle_store(run, vcpu,
125						       kvmppc_get_gpr(vcpu, rs),
126			                               2, 1);
127			kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
128			break;
129
130		case OP_31_XOP_DCBST:
131		case OP_31_XOP_DCBF:
132		case OP_31_XOP_DCBI:
133			/* Do nothing. The guest is performing dcbi because
134			 * hardware DMA is not snooped by the dcache, but
135			 * emulated DMA either goes through the dcache as
136			 * normal writes, or the host kernel has handled dcache
137			 * coherence. */
138			break;
139
140		case OP_31_XOP_LWBRX:
141			emulated = kvmppc_handle_load(run, vcpu, rt, 4, 0);
142			break;
143
144		case OP_31_XOP_STWBRX:
145			emulated = kvmppc_handle_store(run, vcpu,
146						       kvmppc_get_gpr(vcpu, rs),
147			                               4, 0);
148			break;
149
150		case OP_31_XOP_LHBRX:
151			emulated = kvmppc_handle_load(run, vcpu, rt, 2, 0);
152			break;
153
154		case OP_31_XOP_STHBRX:
155			emulated = kvmppc_handle_store(run, vcpu,
156						       kvmppc_get_gpr(vcpu, rs),
157			                               2, 0);
158			break;
159
160		default:
161			emulated = EMULATE_FAIL;
162			break;
163		}
164		break;
165
166	case OP_LWZ:
167		emulated = kvmppc_handle_load(run, vcpu, rt, 4, 1);
168		break;
169
170	/* TBD: Add support for other 64 bit load variants like ldu, ldux, ldx etc. */
171	case OP_LD:
172		rt = get_rt(inst);
173		emulated = kvmppc_handle_load(run, vcpu, rt, 8, 1);
174		break;
175
176	case OP_LWZU:
177		emulated = kvmppc_handle_load(run, vcpu, rt, 4, 1);
178		kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
179		break;
180
181	case OP_LBZ:
182		emulated = kvmppc_handle_load(run, vcpu, rt, 1, 1);
183		break;
184
185	case OP_LBZU:
186		emulated = kvmppc_handle_load(run, vcpu, rt, 1, 1);
187		kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
188		break;
189
190	case OP_STW:
191		emulated = kvmppc_handle_store(run, vcpu,
192					       kvmppc_get_gpr(vcpu, rs),
193		                               4, 1);
194		break;
195
196	/* TBD: Add support for other 64 bit store variants like stdu, stdux, stdx etc. */
197	case OP_STD:
198		rs = get_rs(inst);
199		emulated = kvmppc_handle_store(run, vcpu,
200					       kvmppc_get_gpr(vcpu, rs),
201		                               8, 1);
202		break;
203
204	case OP_STWU:
205		emulated = kvmppc_handle_store(run, vcpu,
206					       kvmppc_get_gpr(vcpu, rs),
207		                               4, 1);
208		kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
209		break;
210
211	case OP_STB:
212		emulated = kvmppc_handle_store(run, vcpu,
213					       kvmppc_get_gpr(vcpu, rs),
214		                               1, 1);
215		break;
216
217	case OP_STBU:
218		emulated = kvmppc_handle_store(run, vcpu,
219					       kvmppc_get_gpr(vcpu, rs),
220		                               1, 1);
221		kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
222		break;
223
224	case OP_LHZ:
225		emulated = kvmppc_handle_load(run, vcpu, rt, 2, 1);
226		break;
227
228	case OP_LHZU:
229		emulated = kvmppc_handle_load(run, vcpu, rt, 2, 1);
230		kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
231		break;
232
233	case OP_LHA:
234		emulated = kvmppc_handle_loads(run, vcpu, rt, 2, 1);
235		break;
236
237	case OP_LHAU:
238		emulated = kvmppc_handle_loads(run, vcpu, rt, 2, 1);
239		kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
240		break;
241
242	case OP_STH:
243		emulated = kvmppc_handle_store(run, vcpu,
244					       kvmppc_get_gpr(vcpu, rs),
245		                               2, 1);
246		break;
247
248	case OP_STHU:
249		emulated = kvmppc_handle_store(run, vcpu,
250					       kvmppc_get_gpr(vcpu, rs),
251		                               2, 1);
252		kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
253		break;
254
255	default:
256		emulated = EMULATE_FAIL;
257		break;
258	}
259
260	if (emulated == EMULATE_FAIL) {
261		advance = 0;
262		kvmppc_core_queue_program(vcpu, 0);
263	}
264
265	trace_kvm_ppc_instr(inst, kvmppc_get_pc(vcpu), emulated);
266
267	/* Advance past emulated instruction. */
268	if (advance)
269		kvmppc_set_pc(vcpu, kvmppc_get_pc(vcpu) + 4);
270
271	return emulated;
272}
273