]>
Commit | Line | Data |
---|---|---|
bbf45ba5 HB |
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 | * | |
17 | * Authors: Hollis Blanchard <hollisb@us.ibm.com> | |
18 | */ | |
19 | ||
20 | #include <linux/jiffies.h> | |
21 | #include <linux/timer.h> | |
22 | #include <linux/types.h> | |
23 | #include <linux/string.h> | |
24 | #include <linux/kvm_host.h> | |
25 | ||
75f74f0d | 26 | #include <asm/reg.h> |
bbf45ba5 HB |
27 | #include <asm/time.h> |
28 | #include <asm/byteorder.h> | |
29 | #include <asm/kvm_ppc.h> | |
c381a043 | 30 | #include <asm/disassemble.h> |
73e75b41 | 31 | #include "timing.h" |
bbf45ba5 | 32 | |
cea5d8c9 HB |
33 | #define OP_TRAP 3 |
34 | ||
35 | #define OP_31_XOP_LWZX 23 | |
36 | #define OP_31_XOP_LBZX 87 | |
37 | #define OP_31_XOP_STWX 151 | |
38 | #define OP_31_XOP_STBX 215 | |
39 | #define OP_31_XOP_STBUX 247 | |
40 | #define OP_31_XOP_LHZX 279 | |
41 | #define OP_31_XOP_LHZUX 311 | |
42 | #define OP_31_XOP_MFSPR 339 | |
43 | #define OP_31_XOP_STHX 407 | |
44 | #define OP_31_XOP_STHUX 439 | |
45 | #define OP_31_XOP_MTSPR 467 | |
46 | #define OP_31_XOP_DCBI 470 | |
47 | #define OP_31_XOP_LWBRX 534 | |
48 | #define OP_31_XOP_TLBSYNC 566 | |
49 | #define OP_31_XOP_STWBRX 662 | |
50 | #define OP_31_XOP_LHBRX 790 | |
51 | #define OP_31_XOP_STHBRX 918 | |
52 | ||
53 | #define OP_LWZ 32 | |
54 | #define OP_LWZU 33 | |
55 | #define OP_LBZ 34 | |
56 | #define OP_LBZU 35 | |
57 | #define OP_STW 36 | |
58 | #define OP_STWU 37 | |
59 | #define OP_STB 38 | |
60 | #define OP_STBU 39 | |
61 | #define OP_LHZ 40 | |
62 | #define OP_LHZU 41 | |
63 | #define OP_STH 44 | |
64 | #define OP_STHU 45 | |
65 | ||
75f74f0d | 66 | void kvmppc_emulate_dec(struct kvm_vcpu *vcpu) |
bbf45ba5 HB |
67 | { |
68 | if (vcpu->arch.tcr & TCR_DIE) { | |
69 | /* The decrementer ticks at the same rate as the timebase, so | |
70 | * that's how we convert the guest DEC value to the number of | |
71 | * host ticks. */ | |
72 | unsigned long nr_jiffies; | |
73 | ||
74 | nr_jiffies = vcpu->arch.dec / tb_ticks_per_jiffy; | |
75 | mod_timer(&vcpu->arch.dec_timer, | |
76 | get_jiffies_64() + nr_jiffies); | |
77 | } else { | |
78 | del_timer(&vcpu->arch.dec_timer); | |
79 | } | |
80 | } | |
81 | ||
bbf45ba5 HB |
82 | /* XXX to do: |
83 | * lhax | |
84 | * lhaux | |
85 | * lswx | |
86 | * lswi | |
87 | * stswx | |
88 | * stswi | |
89 | * lha | |
90 | * lhau | |
91 | * lmw | |
92 | * stmw | |
93 | * | |
94 | * XXX is_bigendian should depend on MMU mapping or MSR[LE] | |
95 | */ | |
75f74f0d HB |
96 | /* XXX Should probably auto-generate instruction decoding for a particular core |
97 | * from opcode tables in the future. */ | |
bbf45ba5 HB |
98 | int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu) |
99 | { | |
100 | u32 inst = vcpu->arch.last_inst; | |
101 | u32 ea; | |
102 | int ra; | |
103 | int rb; | |
bbf45ba5 HB |
104 | int rs; |
105 | int rt; | |
106 | int sprn; | |
bbf45ba5 HB |
107 | enum emulation_result emulated = EMULATE_DONE; |
108 | int advance = 1; | |
109 | ||
73e75b41 HB |
110 | /* this default type might be overwritten by subcategories */ |
111 | kvmppc_set_exit_type(vcpu, EMULATED_INST_EXITS); | |
112 | ||
bbf45ba5 | 113 | switch (get_op(inst)) { |
cea5d8c9 | 114 | case OP_TRAP: |
fcfdbd26 | 115 | vcpu->arch.esr |= ESR_PTR; |
9dd921cf | 116 | kvmppc_core_queue_program(vcpu); |
bbf45ba5 HB |
117 | advance = 0; |
118 | break; | |
119 | ||
bbf45ba5 HB |
120 | case 31: |
121 | switch (get_xop(inst)) { | |
122 | ||
cea5d8c9 | 123 | case OP_31_XOP_LWZX: |
ac3cd34e HB |
124 | rt = get_rt(inst); |
125 | emulated = kvmppc_handle_load(run, vcpu, rt, 4, 1); | |
126 | break; | |
127 | ||
cea5d8c9 | 128 | case OP_31_XOP_LBZX: |
bbf45ba5 HB |
129 | rt = get_rt(inst); |
130 | emulated = kvmppc_handle_load(run, vcpu, rt, 1, 1); | |
131 | break; | |
132 | ||
cea5d8c9 | 133 | case OP_31_XOP_STWX: |
ac3cd34e HB |
134 | rs = get_rs(inst); |
135 | emulated = kvmppc_handle_store(run, vcpu, | |
136 | vcpu->arch.gpr[rs], | |
137 | 4, 1); | |
138 | break; | |
139 | ||
cea5d8c9 | 140 | case OP_31_XOP_STBX: |
bbf45ba5 HB |
141 | rs = get_rs(inst); |
142 | emulated = kvmppc_handle_store(run, vcpu, | |
143 | vcpu->arch.gpr[rs], | |
144 | 1, 1); | |
145 | break; | |
146 | ||
cea5d8c9 | 147 | case OP_31_XOP_STBUX: |
bbf45ba5 HB |
148 | rs = get_rs(inst); |
149 | ra = get_ra(inst); | |
150 | rb = get_rb(inst); | |
151 | ||
152 | ea = vcpu->arch.gpr[rb]; | |
153 | if (ra) | |
154 | ea += vcpu->arch.gpr[ra]; | |
155 | ||
156 | emulated = kvmppc_handle_store(run, vcpu, | |
157 | vcpu->arch.gpr[rs], | |
158 | 1, 1); | |
159 | vcpu->arch.gpr[rs] = ea; | |
160 | break; | |
161 | ||
cea5d8c9 | 162 | case OP_31_XOP_LHZX: |
bbf45ba5 HB |
163 | rt = get_rt(inst); |
164 | emulated = kvmppc_handle_load(run, vcpu, rt, 2, 1); | |
165 | break; | |
166 | ||
cea5d8c9 | 167 | case OP_31_XOP_LHZUX: |
bbf45ba5 HB |
168 | rt = get_rt(inst); |
169 | ra = get_ra(inst); | |
170 | rb = get_rb(inst); | |
171 | ||
172 | ea = vcpu->arch.gpr[rb]; | |
173 | if (ra) | |
174 | ea += vcpu->arch.gpr[ra]; | |
175 | ||
176 | emulated = kvmppc_handle_load(run, vcpu, rt, 2, 1); | |
177 | vcpu->arch.gpr[ra] = ea; | |
178 | break; | |
179 | ||
cea5d8c9 | 180 | case OP_31_XOP_MFSPR: |
bbf45ba5 HB |
181 | sprn = get_sprn(inst); |
182 | rt = get_rt(inst); | |
183 | ||
184 | switch (sprn) { | |
185 | case SPRN_SRR0: | |
186 | vcpu->arch.gpr[rt] = vcpu->arch.srr0; break; | |
187 | case SPRN_SRR1: | |
188 | vcpu->arch.gpr[rt] = vcpu->arch.srr1; break; | |
bbf45ba5 | 189 | case SPRN_PVR: |
5b7c1a2c | 190 | vcpu->arch.gpr[rt] = mfspr(SPRN_PVR); break; |
bbf45ba5 HB |
191 | |
192 | /* Note: mftb and TBRL/TBWL are user-accessible, so | |
193 | * the guest can always access the real TB anyways. | |
194 | * In fact, we probably will never see these traps. */ | |
195 | case SPRN_TBWL: | |
196 | vcpu->arch.gpr[rt] = mftbl(); break; | |
197 | case SPRN_TBWU: | |
198 | vcpu->arch.gpr[rt] = mftbu(); break; | |
199 | ||
200 | case SPRN_SPRG0: | |
201 | vcpu->arch.gpr[rt] = vcpu->arch.sprg0; break; | |
202 | case SPRN_SPRG1: | |
203 | vcpu->arch.gpr[rt] = vcpu->arch.sprg1; break; | |
204 | case SPRN_SPRG2: | |
205 | vcpu->arch.gpr[rt] = vcpu->arch.sprg2; break; | |
206 | case SPRN_SPRG3: | |
207 | vcpu->arch.gpr[rt] = vcpu->arch.sprg3; break; | |
208 | /* Note: SPRG4-7 are user-readable, so we don't get | |
209 | * a trap. */ | |
210 | ||
bbf45ba5 | 211 | default: |
75f74f0d HB |
212 | emulated = kvmppc_core_emulate_mfspr(vcpu, sprn, rt); |
213 | if (emulated == EMULATE_FAIL) { | |
214 | printk("mfspr: unknown spr %x\n", sprn); | |
215 | vcpu->arch.gpr[rt] = 0; | |
216 | } | |
bbf45ba5 HB |
217 | break; |
218 | } | |
219 | break; | |
220 | ||
cea5d8c9 | 221 | case OP_31_XOP_STHX: |
bbf45ba5 HB |
222 | rs = get_rs(inst); |
223 | ra = get_ra(inst); | |
224 | rb = get_rb(inst); | |
225 | ||
226 | emulated = kvmppc_handle_store(run, vcpu, | |
227 | vcpu->arch.gpr[rs], | |
228 | 2, 1); | |
229 | break; | |
230 | ||
cea5d8c9 | 231 | case OP_31_XOP_STHUX: |
bbf45ba5 HB |
232 | rs = get_rs(inst); |
233 | ra = get_ra(inst); | |
234 | rb = get_rb(inst); | |
235 | ||
236 | ea = vcpu->arch.gpr[rb]; | |
237 | if (ra) | |
238 | ea += vcpu->arch.gpr[ra]; | |
239 | ||
240 | emulated = kvmppc_handle_store(run, vcpu, | |
241 | vcpu->arch.gpr[rs], | |
242 | 2, 1); | |
243 | vcpu->arch.gpr[ra] = ea; | |
244 | break; | |
245 | ||
cea5d8c9 | 246 | case OP_31_XOP_MTSPR: |
bbf45ba5 HB |
247 | sprn = get_sprn(inst); |
248 | rs = get_rs(inst); | |
249 | switch (sprn) { | |
250 | case SPRN_SRR0: | |
251 | vcpu->arch.srr0 = vcpu->arch.gpr[rs]; break; | |
252 | case SPRN_SRR1: | |
253 | vcpu->arch.srr1 = vcpu->arch.gpr[rs]; break; | |
bbf45ba5 HB |
254 | |
255 | /* XXX We need to context-switch the timebase for | |
256 | * watchdog and FIT. */ | |
257 | case SPRN_TBWL: break; | |
258 | case SPRN_TBWU: break; | |
259 | ||
260 | case SPRN_DEC: | |
261 | vcpu->arch.dec = vcpu->arch.gpr[rs]; | |
262 | kvmppc_emulate_dec(vcpu); | |
263 | break; | |
264 | ||
bbf45ba5 HB |
265 | case SPRN_SPRG0: |
266 | vcpu->arch.sprg0 = vcpu->arch.gpr[rs]; break; | |
267 | case SPRN_SPRG1: | |
268 | vcpu->arch.sprg1 = vcpu->arch.gpr[rs]; break; | |
269 | case SPRN_SPRG2: | |
270 | vcpu->arch.sprg2 = vcpu->arch.gpr[rs]; break; | |
271 | case SPRN_SPRG3: | |
272 | vcpu->arch.sprg3 = vcpu->arch.gpr[rs]; break; | |
273 | ||
bbf45ba5 | 274 | default: |
75f74f0d HB |
275 | emulated = kvmppc_core_emulate_mtspr(vcpu, sprn, rs); |
276 | if (emulated == EMULATE_FAIL) | |
277 | printk("mtspr: unknown spr %x\n", sprn); | |
bbf45ba5 HB |
278 | break; |
279 | } | |
280 | break; | |
281 | ||
cea5d8c9 | 282 | case OP_31_XOP_DCBI: |
bbf45ba5 HB |
283 | /* Do nothing. The guest is performing dcbi because |
284 | * hardware DMA is not snooped by the dcache, but | |
285 | * emulated DMA either goes through the dcache as | |
286 | * normal writes, or the host kernel has handled dcache | |
287 | * coherence. */ | |
288 | break; | |
289 | ||
cea5d8c9 | 290 | case OP_31_XOP_LWBRX: |
bbf45ba5 HB |
291 | rt = get_rt(inst); |
292 | emulated = kvmppc_handle_load(run, vcpu, rt, 4, 0); | |
293 | break; | |
294 | ||
cea5d8c9 | 295 | case OP_31_XOP_TLBSYNC: |
bbf45ba5 HB |
296 | break; |
297 | ||
cea5d8c9 | 298 | case OP_31_XOP_STWBRX: |
bbf45ba5 HB |
299 | rs = get_rs(inst); |
300 | ra = get_ra(inst); | |
301 | rb = get_rb(inst); | |
302 | ||
303 | emulated = kvmppc_handle_store(run, vcpu, | |
304 | vcpu->arch.gpr[rs], | |
305 | 4, 0); | |
306 | break; | |
307 | ||
cea5d8c9 | 308 | case OP_31_XOP_LHBRX: |
bbf45ba5 HB |
309 | rt = get_rt(inst); |
310 | emulated = kvmppc_handle_load(run, vcpu, rt, 2, 0); | |
311 | break; | |
312 | ||
cea5d8c9 | 313 | case OP_31_XOP_STHBRX: |
bbf45ba5 HB |
314 | rs = get_rs(inst); |
315 | ra = get_ra(inst); | |
316 | rb = get_rb(inst); | |
317 | ||
318 | emulated = kvmppc_handle_store(run, vcpu, | |
319 | vcpu->arch.gpr[rs], | |
320 | 2, 0); | |
321 | break; | |
322 | ||
bbf45ba5 | 323 | default: |
75f74f0d | 324 | /* Attempt core-specific emulation below. */ |
bbf45ba5 | 325 | emulated = EMULATE_FAIL; |
bbf45ba5 HB |
326 | } |
327 | break; | |
328 | ||
cea5d8c9 | 329 | case OP_LWZ: |
bbf45ba5 HB |
330 | rt = get_rt(inst); |
331 | emulated = kvmppc_handle_load(run, vcpu, rt, 4, 1); | |
332 | break; | |
333 | ||
cea5d8c9 | 334 | case OP_LWZU: |
bbf45ba5 HB |
335 | ra = get_ra(inst); |
336 | rt = get_rt(inst); | |
337 | emulated = kvmppc_handle_load(run, vcpu, rt, 4, 1); | |
338 | vcpu->arch.gpr[ra] = vcpu->arch.paddr_accessed; | |
339 | break; | |
340 | ||
cea5d8c9 | 341 | case OP_LBZ: |
bbf45ba5 HB |
342 | rt = get_rt(inst); |
343 | emulated = kvmppc_handle_load(run, vcpu, rt, 1, 1); | |
344 | break; | |
345 | ||
cea5d8c9 | 346 | case OP_LBZU: |
bbf45ba5 HB |
347 | ra = get_ra(inst); |
348 | rt = get_rt(inst); | |
349 | emulated = kvmppc_handle_load(run, vcpu, rt, 1, 1); | |
350 | vcpu->arch.gpr[ra] = vcpu->arch.paddr_accessed; | |
351 | break; | |
352 | ||
cea5d8c9 | 353 | case OP_STW: |
bbf45ba5 HB |
354 | rs = get_rs(inst); |
355 | emulated = kvmppc_handle_store(run, vcpu, vcpu->arch.gpr[rs], | |
356 | 4, 1); | |
357 | break; | |
358 | ||
cea5d8c9 | 359 | case OP_STWU: |
bbf45ba5 HB |
360 | ra = get_ra(inst); |
361 | rs = get_rs(inst); | |
362 | emulated = kvmppc_handle_store(run, vcpu, vcpu->arch.gpr[rs], | |
363 | 4, 1); | |
364 | vcpu->arch.gpr[ra] = vcpu->arch.paddr_accessed; | |
365 | break; | |
366 | ||
cea5d8c9 | 367 | case OP_STB: |
bbf45ba5 HB |
368 | rs = get_rs(inst); |
369 | emulated = kvmppc_handle_store(run, vcpu, vcpu->arch.gpr[rs], | |
370 | 1, 1); | |
371 | break; | |
372 | ||
cea5d8c9 | 373 | case OP_STBU: |
bbf45ba5 HB |
374 | ra = get_ra(inst); |
375 | rs = get_rs(inst); | |
376 | emulated = kvmppc_handle_store(run, vcpu, vcpu->arch.gpr[rs], | |
377 | 1, 1); | |
378 | vcpu->arch.gpr[ra] = vcpu->arch.paddr_accessed; | |
379 | break; | |
380 | ||
cea5d8c9 | 381 | case OP_LHZ: |
bbf45ba5 HB |
382 | rt = get_rt(inst); |
383 | emulated = kvmppc_handle_load(run, vcpu, rt, 2, 1); | |
384 | break; | |
385 | ||
cea5d8c9 | 386 | case OP_LHZU: |
bbf45ba5 HB |
387 | ra = get_ra(inst); |
388 | rt = get_rt(inst); | |
389 | emulated = kvmppc_handle_load(run, vcpu, rt, 2, 1); | |
390 | vcpu->arch.gpr[ra] = vcpu->arch.paddr_accessed; | |
391 | break; | |
392 | ||
cea5d8c9 | 393 | case OP_STH: |
bbf45ba5 HB |
394 | rs = get_rs(inst); |
395 | emulated = kvmppc_handle_store(run, vcpu, vcpu->arch.gpr[rs], | |
396 | 2, 1); | |
397 | break; | |
398 | ||
cea5d8c9 | 399 | case OP_STHU: |
bbf45ba5 HB |
400 | ra = get_ra(inst); |
401 | rs = get_rs(inst); | |
402 | emulated = kvmppc_handle_store(run, vcpu, vcpu->arch.gpr[rs], | |
403 | 2, 1); | |
404 | vcpu->arch.gpr[ra] = vcpu->arch.paddr_accessed; | |
405 | break; | |
406 | ||
407 | default: | |
bbf45ba5 | 408 | emulated = EMULATE_FAIL; |
75f74f0d HB |
409 | } |
410 | ||
411 | if (emulated == EMULATE_FAIL) { | |
412 | emulated = kvmppc_core_emulate_op(run, vcpu, inst, &advance); | |
413 | if (emulated == EMULATE_FAIL) { | |
414 | advance = 0; | |
415 | printk(KERN_ERR "Couldn't emulate instruction 0x%08x " | |
416 | "(op %d xop %d)\n", inst, get_op(inst), get_xop(inst)); | |
417 | } | |
bbf45ba5 HB |
418 | } |
419 | ||
5cf8ca22 | 420 | KVMTRACE_3D(PPC_INSTR, vcpu, inst, (int)vcpu->arch.pc, emulated, entryexit); |
3b4bd796 | 421 | |
bbf45ba5 HB |
422 | if (advance) |
423 | vcpu->arch.pc += 4; /* Advance past emulated instruction. */ | |
424 | ||
425 | return emulated; | |
426 | } |