1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * |
4 | * Copyright Novell Inc 2010 |
5 | * |
6 | * Authors: Alexander Graf <agraf@suse.de> |
7 | */ |
8 | |
9 | #include <asm/kvm.h> |
10 | #include <asm/kvm_ppc.h> |
11 | #include <asm/disassemble.h> |
12 | #include <asm/kvm_book3s.h> |
13 | #include <asm/kvm_fpu.h> |
14 | #include <asm/reg.h> |
15 | #include <asm/cacheflush.h> |
16 | #include <asm/switch_to.h> |
17 | #include <linux/vmalloc.h> |
18 | |
19 | /* #define DEBUG */ |
20 | |
21 | #ifdef DEBUG |
22 | #define dprintk printk |
23 | #else |
24 | #define dprintk(...) do { } while(0); |
25 | #endif |
26 | |
27 | #define OP_LFS 48 |
28 | #define OP_LFSU 49 |
29 | #define OP_LFD 50 |
30 | #define OP_LFDU 51 |
31 | #define OP_STFS 52 |
32 | #define OP_STFSU 53 |
33 | #define OP_STFD 54 |
34 | #define OP_STFDU 55 |
35 | #define OP_PSQ_L 56 |
36 | #define OP_PSQ_LU 57 |
37 | #define OP_PSQ_ST 60 |
38 | #define OP_PSQ_STU 61 |
39 | |
40 | #define OP_31_LFSX 535 |
41 | #define OP_31_LFSUX 567 |
42 | #define OP_31_LFDX 599 |
43 | #define OP_31_LFDUX 631 |
44 | #define OP_31_STFSX 663 |
45 | #define OP_31_STFSUX 695 |
46 | #define OP_31_STFX 727 |
47 | #define OP_31_STFUX 759 |
48 | #define OP_31_LWIZX 887 |
49 | #define OP_31_STFIWX 983 |
50 | |
51 | #define OP_59_FADDS 21 |
52 | #define OP_59_FSUBS 20 |
53 | #define OP_59_FSQRTS 22 |
54 | #define OP_59_FDIVS 18 |
55 | #define OP_59_FRES 24 |
56 | #define OP_59_FMULS 25 |
57 | #define OP_59_FRSQRTES 26 |
58 | #define OP_59_FMSUBS 28 |
59 | #define OP_59_FMADDS 29 |
60 | #define OP_59_FNMSUBS 30 |
61 | #define OP_59_FNMADDS 31 |
62 | |
63 | #define OP_63_FCMPU 0 |
64 | #define OP_63_FCPSGN 8 |
65 | #define OP_63_FRSP 12 |
66 | #define OP_63_FCTIW 14 |
67 | #define OP_63_FCTIWZ 15 |
68 | #define OP_63_FDIV 18 |
69 | #define OP_63_FADD 21 |
70 | #define OP_63_FSQRT 22 |
71 | #define OP_63_FSEL 23 |
72 | #define OP_63_FRE 24 |
73 | #define OP_63_FMUL 25 |
74 | #define OP_63_FRSQRTE 26 |
75 | #define OP_63_FMSUB 28 |
76 | #define OP_63_FMADD 29 |
77 | #define OP_63_FNMSUB 30 |
78 | #define OP_63_FNMADD 31 |
79 | #define OP_63_FCMPO 32 |
80 | #define OP_63_MTFSB1 38 // XXX |
81 | #define OP_63_FSUB 20 |
82 | #define OP_63_FNEG 40 |
83 | #define OP_63_MCRFS 64 |
84 | #define OP_63_MTFSB0 70 |
85 | #define OP_63_FMR 72 |
86 | #define OP_63_MTFSFI 134 |
87 | #define OP_63_FABS 264 |
88 | #define OP_63_MFFS 583 |
89 | #define OP_63_MTFSF 711 |
90 | |
91 | #define OP_4X_PS_CMPU0 0 |
92 | #define OP_4X_PSQ_LX 6 |
93 | #define OP_4XW_PSQ_STX 7 |
94 | #define OP_4A_PS_SUM0 10 |
95 | #define OP_4A_PS_SUM1 11 |
96 | #define OP_4A_PS_MULS0 12 |
97 | #define OP_4A_PS_MULS1 13 |
98 | #define OP_4A_PS_MADDS0 14 |
99 | #define OP_4A_PS_MADDS1 15 |
100 | #define OP_4A_PS_DIV 18 |
101 | #define OP_4A_PS_SUB 20 |
102 | #define OP_4A_PS_ADD 21 |
103 | #define OP_4A_PS_SEL 23 |
104 | #define OP_4A_PS_RES 24 |
105 | #define OP_4A_PS_MUL 25 |
106 | #define OP_4A_PS_RSQRTE 26 |
107 | #define OP_4A_PS_MSUB 28 |
108 | #define OP_4A_PS_MADD 29 |
109 | #define OP_4A_PS_NMSUB 30 |
110 | #define OP_4A_PS_NMADD 31 |
111 | #define OP_4X_PS_CMPO0 32 |
112 | #define OP_4X_PSQ_LUX 38 |
113 | #define OP_4XW_PSQ_STUX 39 |
114 | #define OP_4X_PS_NEG 40 |
115 | #define OP_4X_PS_CMPU1 64 |
116 | #define OP_4X_PS_MR 72 |
117 | #define OP_4X_PS_CMPO1 96 |
118 | #define OP_4X_PS_NABS 136 |
119 | #define OP_4X_PS_ABS 264 |
120 | #define OP_4X_PS_MERGE00 528 |
121 | #define OP_4X_PS_MERGE01 560 |
122 | #define OP_4X_PS_MERGE10 592 |
123 | #define OP_4X_PS_MERGE11 624 |
124 | |
125 | #define SCALAR_NONE 0 |
126 | #define SCALAR_HIGH (1 << 0) |
127 | #define SCALAR_LOW (1 << 1) |
128 | #define SCALAR_NO_PS0 (1 << 2) |
129 | #define SCALAR_NO_PS1 (1 << 3) |
130 | |
131 | #define GQR_ST_TYPE_MASK 0x00000007 |
132 | #define GQR_ST_TYPE_SHIFT 0 |
133 | #define GQR_ST_SCALE_MASK 0x00003f00 |
134 | #define GQR_ST_SCALE_SHIFT 8 |
135 | #define GQR_LD_TYPE_MASK 0x00070000 |
136 | #define GQR_LD_TYPE_SHIFT 16 |
137 | #define GQR_LD_SCALE_MASK 0x3f000000 |
138 | #define GQR_LD_SCALE_SHIFT 24 |
139 | |
140 | #define GQR_QUANTIZE_FLOAT 0 |
141 | #define GQR_QUANTIZE_U8 4 |
142 | #define GQR_QUANTIZE_U16 5 |
143 | #define GQR_QUANTIZE_S8 6 |
144 | #define GQR_QUANTIZE_S16 7 |
145 | |
146 | #define FPU_LS_SINGLE 0 |
147 | #define FPU_LS_DOUBLE 1 |
148 | #define FPU_LS_SINGLE_LOW 2 |
149 | |
150 | static inline void kvmppc_sync_qpr(struct kvm_vcpu *vcpu, int rt) |
151 | { |
152 | kvm_cvt_df(&VCPU_FPR(vcpu, rt), &vcpu->arch.qpr[rt]); |
153 | } |
154 | |
155 | static void kvmppc_inject_pf(struct kvm_vcpu *vcpu, ulong eaddr, bool is_store) |
156 | { |
157 | u32 dsisr; |
158 | u64 msr = kvmppc_get_msr(vcpu); |
159 | |
160 | msr = kvmppc_set_field(msr, 33, 36, 0); |
161 | msr = kvmppc_set_field(msr, 42, 47, 0); |
162 | kvmppc_set_msr(vcpu, msr); |
163 | kvmppc_set_dar(vcpu, eaddr); |
164 | /* Page Fault */ |
165 | dsisr = kvmppc_set_field(0, 33, 33, 1); |
166 | if (is_store) |
167 | dsisr = kvmppc_set_field(dsisr, 38, 38, 1); |
168 | kvmppc_set_dsisr(vcpu, dsisr); |
169 | kvmppc_book3s_queue_irqprio(vcpu, BOOK3S_INTERRUPT_DATA_STORAGE); |
170 | } |
171 | |
172 | static int kvmppc_emulate_fpr_load(struct kvm_vcpu *vcpu, |
173 | int rs, ulong addr, int ls_type) |
174 | { |
175 | int emulated = EMULATE_FAIL; |
176 | int r; |
177 | char tmp[8]; |
178 | int len = sizeof(u32); |
179 | |
180 | if (ls_type == FPU_LS_DOUBLE) |
181 | len = sizeof(u64); |
182 | |
183 | /* read from memory */ |
184 | r = kvmppc_ld(vcpu, &addr, len, tmp, true); |
185 | vcpu->arch.paddr_accessed = addr; |
186 | |
187 | if (r < 0) { |
188 | kvmppc_inject_pf(vcpu, eaddr: addr, is_store: false); |
189 | goto done_load; |
190 | } else if (r == EMULATE_DO_MMIO) { |
191 | emulated = kvmppc_handle_load(vcpu, KVM_MMIO_REG_FPR | rs, |
192 | len, 1); |
193 | goto done_load; |
194 | } |
195 | |
196 | emulated = EMULATE_DONE; |
197 | |
198 | /* put in registers */ |
199 | switch (ls_type) { |
200 | case FPU_LS_SINGLE: |
201 | kvm_cvt_fd((u32*)tmp, &VCPU_FPR(vcpu, rs)); |
202 | vcpu->arch.qpr[rs] = *((u32*)tmp); |
203 | break; |
204 | case FPU_LS_DOUBLE: |
205 | VCPU_FPR(vcpu, rs) = *((u64*)tmp); |
206 | break; |
207 | } |
208 | |
209 | dprintk(KERN_INFO "KVM: FPR_LD [0x%llx] at 0x%lx (%d)\n" , *(u64*)tmp, |
210 | addr, len); |
211 | |
212 | done_load: |
213 | return emulated; |
214 | } |
215 | |
216 | static int kvmppc_emulate_fpr_store(struct kvm_vcpu *vcpu, |
217 | int rs, ulong addr, int ls_type) |
218 | { |
219 | int emulated = EMULATE_FAIL; |
220 | int r; |
221 | char tmp[8]; |
222 | u64 val; |
223 | int len; |
224 | |
225 | switch (ls_type) { |
226 | case FPU_LS_SINGLE: |
227 | kvm_cvt_df(&VCPU_FPR(vcpu, rs), (u32*)tmp); |
228 | val = *((u32*)tmp); |
229 | len = sizeof(u32); |
230 | break; |
231 | case FPU_LS_SINGLE_LOW: |
232 | *((u32*)tmp) = VCPU_FPR(vcpu, rs); |
233 | val = VCPU_FPR(vcpu, rs) & 0xffffffff; |
234 | len = sizeof(u32); |
235 | break; |
236 | case FPU_LS_DOUBLE: |
237 | *((u64*)tmp) = VCPU_FPR(vcpu, rs); |
238 | val = VCPU_FPR(vcpu, rs); |
239 | len = sizeof(u64); |
240 | break; |
241 | default: |
242 | val = 0; |
243 | len = 0; |
244 | } |
245 | |
246 | r = kvmppc_st(vcpu, &addr, len, tmp, true); |
247 | vcpu->arch.paddr_accessed = addr; |
248 | if (r < 0) { |
249 | kvmppc_inject_pf(vcpu, eaddr: addr, is_store: true); |
250 | } else if (r == EMULATE_DO_MMIO) { |
251 | emulated = kvmppc_handle_store(vcpu, val, len, 1); |
252 | } else { |
253 | emulated = EMULATE_DONE; |
254 | } |
255 | |
256 | dprintk(KERN_INFO "KVM: FPR_ST [0x%llx] at 0x%lx (%d)\n" , |
257 | val, addr, len); |
258 | |
259 | return emulated; |
260 | } |
261 | |
262 | static int kvmppc_emulate_psq_load(struct kvm_vcpu *vcpu, |
263 | int rs, ulong addr, bool w, int i) |
264 | { |
265 | int emulated = EMULATE_FAIL; |
266 | int r; |
267 | float one = 1.0; |
268 | u32 tmp[2]; |
269 | |
270 | /* read from memory */ |
271 | if (w) { |
272 | r = kvmppc_ld(vcpu, &addr, sizeof(u32), tmp, true); |
273 | memcpy(&tmp[1], &one, sizeof(u32)); |
274 | } else { |
275 | r = kvmppc_ld(vcpu, &addr, sizeof(u32) * 2, tmp, true); |
276 | } |
277 | vcpu->arch.paddr_accessed = addr; |
278 | if (r < 0) { |
279 | kvmppc_inject_pf(vcpu, eaddr: addr, is_store: false); |
280 | goto done_load; |
281 | } else if ((r == EMULATE_DO_MMIO) && w) { |
282 | emulated = kvmppc_handle_load(vcpu, KVM_MMIO_REG_FPR | rs, |
283 | 4, 1); |
284 | vcpu->arch.qpr[rs] = tmp[1]; |
285 | goto done_load; |
286 | } else if (r == EMULATE_DO_MMIO) { |
287 | emulated = kvmppc_handle_load(vcpu, KVM_MMIO_REG_FQPR | rs, |
288 | 8, 1); |
289 | goto done_load; |
290 | } |
291 | |
292 | emulated = EMULATE_DONE; |
293 | |
294 | /* put in registers */ |
295 | kvm_cvt_fd(&tmp[0], &VCPU_FPR(vcpu, rs)); |
296 | vcpu->arch.qpr[rs] = tmp[1]; |
297 | |
298 | dprintk(KERN_INFO "KVM: PSQ_LD [0x%x, 0x%x] at 0x%lx (%d)\n" , tmp[0], |
299 | tmp[1], addr, w ? 4 : 8); |
300 | |
301 | done_load: |
302 | return emulated; |
303 | } |
304 | |
305 | static int kvmppc_emulate_psq_store(struct kvm_vcpu *vcpu, |
306 | int rs, ulong addr, bool w, int i) |
307 | { |
308 | int emulated = EMULATE_FAIL; |
309 | int r; |
310 | u32 tmp[2]; |
311 | int len = w ? sizeof(u32) : sizeof(u64); |
312 | |
313 | kvm_cvt_df(&VCPU_FPR(vcpu, rs), &tmp[0]); |
314 | tmp[1] = vcpu->arch.qpr[rs]; |
315 | |
316 | r = kvmppc_st(vcpu, &addr, len, tmp, true); |
317 | vcpu->arch.paddr_accessed = addr; |
318 | if (r < 0) { |
319 | kvmppc_inject_pf(vcpu, eaddr: addr, is_store: true); |
320 | } else if ((r == EMULATE_DO_MMIO) && w) { |
321 | emulated = kvmppc_handle_store(vcpu, tmp[0], 4, 1); |
322 | } else if (r == EMULATE_DO_MMIO) { |
323 | u64 val = ((u64)tmp[0] << 32) | tmp[1]; |
324 | emulated = kvmppc_handle_store(vcpu, val, 8, 1); |
325 | } else { |
326 | emulated = EMULATE_DONE; |
327 | } |
328 | |
329 | dprintk(KERN_INFO "KVM: PSQ_ST [0x%x, 0x%x] at 0x%lx (%d)\n" , |
330 | tmp[0], tmp[1], addr, len); |
331 | |
332 | return emulated; |
333 | } |
334 | |
335 | /* |
336 | * Cuts out inst bits with ordering according to spec. |
337 | * That means the leftmost bit is zero. All given bits are included. |
338 | */ |
339 | static inline u32 inst_get_field(u32 inst, int msb, int lsb) |
340 | { |
341 | return kvmppc_get_field(inst, msb + 32, lsb + 32); |
342 | } |
343 | |
344 | static bool kvmppc_inst_is_paired_single(struct kvm_vcpu *vcpu, u32 inst) |
345 | { |
346 | if (!(vcpu->arch.hflags & BOOK3S_HFLAG_PAIRED_SINGLE)) |
347 | return false; |
348 | |
349 | switch (get_op(inst)) { |
350 | case OP_PSQ_L: |
351 | case OP_PSQ_LU: |
352 | case OP_PSQ_ST: |
353 | case OP_PSQ_STU: |
354 | case OP_LFS: |
355 | case OP_LFSU: |
356 | case OP_LFD: |
357 | case OP_LFDU: |
358 | case OP_STFS: |
359 | case OP_STFSU: |
360 | case OP_STFD: |
361 | case OP_STFDU: |
362 | return true; |
363 | case 4: |
364 | /* X form */ |
365 | switch (inst_get_field(inst, msb: 21, lsb: 30)) { |
366 | case OP_4X_PS_CMPU0: |
367 | case OP_4X_PSQ_LX: |
368 | case OP_4X_PS_CMPO0: |
369 | case OP_4X_PSQ_LUX: |
370 | case OP_4X_PS_NEG: |
371 | case OP_4X_PS_CMPU1: |
372 | case OP_4X_PS_MR: |
373 | case OP_4X_PS_CMPO1: |
374 | case OP_4X_PS_NABS: |
375 | case OP_4X_PS_ABS: |
376 | case OP_4X_PS_MERGE00: |
377 | case OP_4X_PS_MERGE01: |
378 | case OP_4X_PS_MERGE10: |
379 | case OP_4X_PS_MERGE11: |
380 | return true; |
381 | } |
382 | /* XW form */ |
383 | switch (inst_get_field(inst, msb: 25, lsb: 30)) { |
384 | case OP_4XW_PSQ_STX: |
385 | case OP_4XW_PSQ_STUX: |
386 | return true; |
387 | } |
388 | /* A form */ |
389 | switch (inst_get_field(inst, msb: 26, lsb: 30)) { |
390 | case OP_4A_PS_SUM1: |
391 | case OP_4A_PS_SUM0: |
392 | case OP_4A_PS_MULS0: |
393 | case OP_4A_PS_MULS1: |
394 | case OP_4A_PS_MADDS0: |
395 | case OP_4A_PS_MADDS1: |
396 | case OP_4A_PS_DIV: |
397 | case OP_4A_PS_SUB: |
398 | case OP_4A_PS_ADD: |
399 | case OP_4A_PS_SEL: |
400 | case OP_4A_PS_RES: |
401 | case OP_4A_PS_MUL: |
402 | case OP_4A_PS_RSQRTE: |
403 | case OP_4A_PS_MSUB: |
404 | case OP_4A_PS_MADD: |
405 | case OP_4A_PS_NMSUB: |
406 | case OP_4A_PS_NMADD: |
407 | return true; |
408 | } |
409 | break; |
410 | case 59: |
411 | switch (inst_get_field(inst, msb: 21, lsb: 30)) { |
412 | case OP_59_FADDS: |
413 | case OP_59_FSUBS: |
414 | case OP_59_FDIVS: |
415 | case OP_59_FRES: |
416 | case OP_59_FRSQRTES: |
417 | return true; |
418 | } |
419 | switch (inst_get_field(inst, msb: 26, lsb: 30)) { |
420 | case OP_59_FMULS: |
421 | case OP_59_FMSUBS: |
422 | case OP_59_FMADDS: |
423 | case OP_59_FNMSUBS: |
424 | case OP_59_FNMADDS: |
425 | return true; |
426 | } |
427 | break; |
428 | case 63: |
429 | switch (inst_get_field(inst, msb: 21, lsb: 30)) { |
430 | case OP_63_MTFSB0: |
431 | case OP_63_MTFSB1: |
432 | case OP_63_MTFSF: |
433 | case OP_63_MTFSFI: |
434 | case OP_63_MCRFS: |
435 | case OP_63_MFFS: |
436 | case OP_63_FCMPU: |
437 | case OP_63_FCMPO: |
438 | case OP_63_FNEG: |
439 | case OP_63_FMR: |
440 | case OP_63_FABS: |
441 | case OP_63_FRSP: |
442 | case OP_63_FDIV: |
443 | case OP_63_FADD: |
444 | case OP_63_FSUB: |
445 | case OP_63_FCTIW: |
446 | case OP_63_FCTIWZ: |
447 | case OP_63_FRSQRTE: |
448 | case OP_63_FCPSGN: |
449 | return true; |
450 | } |
451 | switch (inst_get_field(inst, msb: 26, lsb: 30)) { |
452 | case OP_63_FMUL: |
453 | case OP_63_FSEL: |
454 | case OP_63_FMSUB: |
455 | case OP_63_FMADD: |
456 | case OP_63_FNMSUB: |
457 | case OP_63_FNMADD: |
458 | return true; |
459 | } |
460 | break; |
461 | case 31: |
462 | switch (inst_get_field(inst, msb: 21, lsb: 30)) { |
463 | case OP_31_LFSX: |
464 | case OP_31_LFSUX: |
465 | case OP_31_LFDX: |
466 | case OP_31_LFDUX: |
467 | case OP_31_STFSX: |
468 | case OP_31_STFSUX: |
469 | case OP_31_STFX: |
470 | case OP_31_STFUX: |
471 | case OP_31_STFIWX: |
472 | return true; |
473 | } |
474 | break; |
475 | } |
476 | |
477 | return false; |
478 | } |
479 | |
480 | static int get_d_signext(u32 inst) |
481 | { |
482 | int d = inst & 0x8ff; |
483 | |
484 | if (d & 0x800) |
485 | return -(d & 0x7ff); |
486 | |
487 | return (d & 0x7ff); |
488 | } |
489 | |
490 | static int kvmppc_ps_three_in(struct kvm_vcpu *vcpu, bool rc, |
491 | int reg_out, int reg_in1, int reg_in2, |
492 | int reg_in3, int scalar, |
493 | void (*func)(u64 *fpscr, |
494 | u32 *dst, u32 *src1, |
495 | u32 *src2, u32 *src3)) |
496 | { |
497 | u32 *qpr = vcpu->arch.qpr; |
498 | u32 ps0_out; |
499 | u32 ps0_in1, ps0_in2, ps0_in3; |
500 | u32 ps1_in1, ps1_in2, ps1_in3; |
501 | |
502 | /* RC */ |
503 | WARN_ON(rc); |
504 | |
505 | /* PS0 */ |
506 | kvm_cvt_df(&VCPU_FPR(vcpu, reg_in1), &ps0_in1); |
507 | kvm_cvt_df(&VCPU_FPR(vcpu, reg_in2), &ps0_in2); |
508 | kvm_cvt_df(&VCPU_FPR(vcpu, reg_in3), &ps0_in3); |
509 | |
510 | if (scalar & SCALAR_LOW) |
511 | ps0_in2 = qpr[reg_in2]; |
512 | |
513 | func(&vcpu->arch.fp.fpscr, &ps0_out, &ps0_in1, &ps0_in2, &ps0_in3); |
514 | |
515 | dprintk(KERN_INFO "PS3 ps0 -> f(0x%x, 0x%x, 0x%x) = 0x%x\n" , |
516 | ps0_in1, ps0_in2, ps0_in3, ps0_out); |
517 | |
518 | if (!(scalar & SCALAR_NO_PS0)) |
519 | kvm_cvt_fd(&ps0_out, &VCPU_FPR(vcpu, reg_out)); |
520 | |
521 | /* PS1 */ |
522 | ps1_in1 = qpr[reg_in1]; |
523 | ps1_in2 = qpr[reg_in2]; |
524 | ps1_in3 = qpr[reg_in3]; |
525 | |
526 | if (scalar & SCALAR_HIGH) |
527 | ps1_in2 = ps0_in2; |
528 | |
529 | if (!(scalar & SCALAR_NO_PS1)) |
530 | func(&vcpu->arch.fp.fpscr, &qpr[reg_out], &ps1_in1, &ps1_in2, &ps1_in3); |
531 | |
532 | dprintk(KERN_INFO "PS3 ps1 -> f(0x%x, 0x%x, 0x%x) = 0x%x\n" , |
533 | ps1_in1, ps1_in2, ps1_in3, qpr[reg_out]); |
534 | |
535 | return EMULATE_DONE; |
536 | } |
537 | |
538 | static int kvmppc_ps_two_in(struct kvm_vcpu *vcpu, bool rc, |
539 | int reg_out, int reg_in1, int reg_in2, |
540 | int scalar, |
541 | void (*func)(u64 *fpscr, |
542 | u32 *dst, u32 *src1, |
543 | u32 *src2)) |
544 | { |
545 | u32 *qpr = vcpu->arch.qpr; |
546 | u32 ps0_out; |
547 | u32 ps0_in1, ps0_in2; |
548 | u32 ps1_out; |
549 | u32 ps1_in1, ps1_in2; |
550 | |
551 | /* RC */ |
552 | WARN_ON(rc); |
553 | |
554 | /* PS0 */ |
555 | kvm_cvt_df(&VCPU_FPR(vcpu, reg_in1), &ps0_in1); |
556 | |
557 | if (scalar & SCALAR_LOW) |
558 | ps0_in2 = qpr[reg_in2]; |
559 | else |
560 | kvm_cvt_df(&VCPU_FPR(vcpu, reg_in2), &ps0_in2); |
561 | |
562 | func(&vcpu->arch.fp.fpscr, &ps0_out, &ps0_in1, &ps0_in2); |
563 | |
564 | if (!(scalar & SCALAR_NO_PS0)) { |
565 | dprintk(KERN_INFO "PS2 ps0 -> f(0x%x, 0x%x) = 0x%x\n" , |
566 | ps0_in1, ps0_in2, ps0_out); |
567 | |
568 | kvm_cvt_fd(&ps0_out, &VCPU_FPR(vcpu, reg_out)); |
569 | } |
570 | |
571 | /* PS1 */ |
572 | ps1_in1 = qpr[reg_in1]; |
573 | ps1_in2 = qpr[reg_in2]; |
574 | |
575 | if (scalar & SCALAR_HIGH) |
576 | ps1_in2 = ps0_in2; |
577 | |
578 | func(&vcpu->arch.fp.fpscr, &ps1_out, &ps1_in1, &ps1_in2); |
579 | |
580 | if (!(scalar & SCALAR_NO_PS1)) { |
581 | qpr[reg_out] = ps1_out; |
582 | |
583 | dprintk(KERN_INFO "PS2 ps1 -> f(0x%x, 0x%x) = 0x%x\n" , |
584 | ps1_in1, ps1_in2, qpr[reg_out]); |
585 | } |
586 | |
587 | return EMULATE_DONE; |
588 | } |
589 | |
590 | static int kvmppc_ps_one_in(struct kvm_vcpu *vcpu, bool rc, |
591 | int reg_out, int reg_in, |
592 | void (*func)(u64 *t, |
593 | u32 *dst, u32 *src1)) |
594 | { |
595 | u32 *qpr = vcpu->arch.qpr; |
596 | u32 ps0_out, ps0_in; |
597 | u32 ps1_in; |
598 | |
599 | /* RC */ |
600 | WARN_ON(rc); |
601 | |
602 | /* PS0 */ |
603 | kvm_cvt_df(&VCPU_FPR(vcpu, reg_in), &ps0_in); |
604 | func(&vcpu->arch.fp.fpscr, &ps0_out, &ps0_in); |
605 | |
606 | dprintk(KERN_INFO "PS1 ps0 -> f(0x%x) = 0x%x\n" , |
607 | ps0_in, ps0_out); |
608 | |
609 | kvm_cvt_fd(&ps0_out, &VCPU_FPR(vcpu, reg_out)); |
610 | |
611 | /* PS1 */ |
612 | ps1_in = qpr[reg_in]; |
613 | func(&vcpu->arch.fp.fpscr, &qpr[reg_out], &ps1_in); |
614 | |
615 | dprintk(KERN_INFO "PS1 ps1 -> f(0x%x) = 0x%x\n" , |
616 | ps1_in, qpr[reg_out]); |
617 | |
618 | return EMULATE_DONE; |
619 | } |
620 | |
621 | int kvmppc_emulate_paired_single(struct kvm_vcpu *vcpu) |
622 | { |
623 | u32 inst; |
624 | ppc_inst_t pinst; |
625 | enum emulation_result emulated = EMULATE_DONE; |
626 | int ax_rd, ax_ra, ax_rb, ax_rc; |
627 | short full_d; |
628 | u64 *fpr_d, *fpr_a, *fpr_b, *fpr_c; |
629 | |
630 | bool rcomp; |
631 | u32 cr; |
632 | #ifdef DEBUG |
633 | int i; |
634 | #endif |
635 | |
636 | emulated = kvmppc_get_last_inst(vcpu, INST_GENERIC, &pinst); |
637 | inst = ppc_inst_val(pinst); |
638 | if (emulated != EMULATE_DONE) |
639 | return emulated; |
640 | |
641 | ax_rd = inst_get_field(inst, msb: 6, lsb: 10); |
642 | ax_ra = inst_get_field(inst, msb: 11, lsb: 15); |
643 | ax_rb = inst_get_field(inst, msb: 16, lsb: 20); |
644 | ax_rc = inst_get_field(inst, msb: 21, lsb: 25); |
645 | full_d = inst_get_field(inst, msb: 16, lsb: 31); |
646 | |
647 | fpr_d = &VCPU_FPR(vcpu, ax_rd); |
648 | fpr_a = &VCPU_FPR(vcpu, ax_ra); |
649 | fpr_b = &VCPU_FPR(vcpu, ax_rb); |
650 | fpr_c = &VCPU_FPR(vcpu, ax_rc); |
651 | |
652 | rcomp = (inst & 1) ? true : false; |
653 | cr = kvmppc_get_cr(vcpu); |
654 | |
655 | if (!kvmppc_inst_is_paired_single(vcpu, inst)) |
656 | return EMULATE_FAIL; |
657 | |
658 | if (!(kvmppc_get_msr(vcpu) & MSR_FP)) { |
659 | kvmppc_book3s_queue_irqprio(vcpu, BOOK3S_INTERRUPT_FP_UNAVAIL); |
660 | return EMULATE_AGAIN; |
661 | } |
662 | |
663 | kvmppc_giveup_ext(vcpu, MSR_FP); |
664 | preempt_disable(); |
665 | enable_kernel_fp(); |
666 | /* Do we need to clear FE0 / FE1 here? Don't think so. */ |
667 | |
668 | #ifdef DEBUG |
669 | for (i = 0; i < ARRAY_SIZE(vcpu->arch.fp.fpr); i++) { |
670 | u32 f; |
671 | kvm_cvt_df(&VCPU_FPR(vcpu, i), &f); |
672 | dprintk(KERN_INFO "FPR[%d] = 0x%x / 0x%llx QPR[%d] = 0x%x\n" , |
673 | i, f, VCPU_FPR(vcpu, i), i, vcpu->arch.qpr[i]); |
674 | } |
675 | #endif |
676 | |
677 | switch (get_op(inst)) { |
678 | case OP_PSQ_L: |
679 | { |
680 | ulong addr = ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0; |
681 | bool w = inst_get_field(inst, msb: 16, lsb: 16) ? true : false; |
682 | int i = inst_get_field(inst, msb: 17, lsb: 19); |
683 | |
684 | addr += get_d_signext(inst); |
685 | emulated = kvmppc_emulate_psq_load(vcpu, rs: ax_rd, addr, w, i); |
686 | break; |
687 | } |
688 | case OP_PSQ_LU: |
689 | { |
690 | ulong addr = kvmppc_get_gpr(vcpu, ax_ra); |
691 | bool w = inst_get_field(inst, msb: 16, lsb: 16) ? true : false; |
692 | int i = inst_get_field(inst, msb: 17, lsb: 19); |
693 | |
694 | addr += get_d_signext(inst); |
695 | emulated = kvmppc_emulate_psq_load(vcpu, rs: ax_rd, addr, w, i); |
696 | |
697 | if (emulated == EMULATE_DONE) |
698 | kvmppc_set_gpr(vcpu, ax_ra, addr); |
699 | break; |
700 | } |
701 | case OP_PSQ_ST: |
702 | { |
703 | ulong addr = ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0; |
704 | bool w = inst_get_field(inst, msb: 16, lsb: 16) ? true : false; |
705 | int i = inst_get_field(inst, msb: 17, lsb: 19); |
706 | |
707 | addr += get_d_signext(inst); |
708 | emulated = kvmppc_emulate_psq_store(vcpu, rs: ax_rd, addr, w, i); |
709 | break; |
710 | } |
711 | case OP_PSQ_STU: |
712 | { |
713 | ulong addr = kvmppc_get_gpr(vcpu, ax_ra); |
714 | bool w = inst_get_field(inst, msb: 16, lsb: 16) ? true : false; |
715 | int i = inst_get_field(inst, msb: 17, lsb: 19); |
716 | |
717 | addr += get_d_signext(inst); |
718 | emulated = kvmppc_emulate_psq_store(vcpu, rs: ax_rd, addr, w, i); |
719 | |
720 | if (emulated == EMULATE_DONE) |
721 | kvmppc_set_gpr(vcpu, ax_ra, addr); |
722 | break; |
723 | } |
724 | case 4: |
725 | /* X form */ |
726 | switch (inst_get_field(inst, msb: 21, lsb: 30)) { |
727 | case OP_4X_PS_CMPU0: |
728 | /* XXX */ |
729 | emulated = EMULATE_FAIL; |
730 | break; |
731 | case OP_4X_PSQ_LX: |
732 | { |
733 | ulong addr = ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0; |
734 | bool w = inst_get_field(inst, msb: 21, lsb: 21) ? true : false; |
735 | int i = inst_get_field(inst, msb: 22, lsb: 24); |
736 | |
737 | addr += kvmppc_get_gpr(vcpu, ax_rb); |
738 | emulated = kvmppc_emulate_psq_load(vcpu, rs: ax_rd, addr, w, i); |
739 | break; |
740 | } |
741 | case OP_4X_PS_CMPO0: |
742 | /* XXX */ |
743 | emulated = EMULATE_FAIL; |
744 | break; |
745 | case OP_4X_PSQ_LUX: |
746 | { |
747 | ulong addr = kvmppc_get_gpr(vcpu, ax_ra); |
748 | bool w = inst_get_field(inst, msb: 21, lsb: 21) ? true : false; |
749 | int i = inst_get_field(inst, msb: 22, lsb: 24); |
750 | |
751 | addr += kvmppc_get_gpr(vcpu, ax_rb); |
752 | emulated = kvmppc_emulate_psq_load(vcpu, rs: ax_rd, addr, w, i); |
753 | |
754 | if (emulated == EMULATE_DONE) |
755 | kvmppc_set_gpr(vcpu, ax_ra, addr); |
756 | break; |
757 | } |
758 | case OP_4X_PS_NEG: |
759 | VCPU_FPR(vcpu, ax_rd) = VCPU_FPR(vcpu, ax_rb); |
760 | VCPU_FPR(vcpu, ax_rd) ^= 0x8000000000000000ULL; |
761 | vcpu->arch.qpr[ax_rd] = vcpu->arch.qpr[ax_rb]; |
762 | vcpu->arch.qpr[ax_rd] ^= 0x80000000; |
763 | break; |
764 | case OP_4X_PS_CMPU1: |
765 | /* XXX */ |
766 | emulated = EMULATE_FAIL; |
767 | break; |
768 | case OP_4X_PS_MR: |
769 | WARN_ON(rcomp); |
770 | VCPU_FPR(vcpu, ax_rd) = VCPU_FPR(vcpu, ax_rb); |
771 | vcpu->arch.qpr[ax_rd] = vcpu->arch.qpr[ax_rb]; |
772 | break; |
773 | case OP_4X_PS_CMPO1: |
774 | /* XXX */ |
775 | emulated = EMULATE_FAIL; |
776 | break; |
777 | case OP_4X_PS_NABS: |
778 | WARN_ON(rcomp); |
779 | VCPU_FPR(vcpu, ax_rd) = VCPU_FPR(vcpu, ax_rb); |
780 | VCPU_FPR(vcpu, ax_rd) |= 0x8000000000000000ULL; |
781 | vcpu->arch.qpr[ax_rd] = vcpu->arch.qpr[ax_rb]; |
782 | vcpu->arch.qpr[ax_rd] |= 0x80000000; |
783 | break; |
784 | case OP_4X_PS_ABS: |
785 | WARN_ON(rcomp); |
786 | VCPU_FPR(vcpu, ax_rd) = VCPU_FPR(vcpu, ax_rb); |
787 | VCPU_FPR(vcpu, ax_rd) &= ~0x8000000000000000ULL; |
788 | vcpu->arch.qpr[ax_rd] = vcpu->arch.qpr[ax_rb]; |
789 | vcpu->arch.qpr[ax_rd] &= ~0x80000000; |
790 | break; |
791 | case OP_4X_PS_MERGE00: |
792 | WARN_ON(rcomp); |
793 | VCPU_FPR(vcpu, ax_rd) = VCPU_FPR(vcpu, ax_ra); |
794 | /* vcpu->arch.qpr[ax_rd] = VCPU_FPR(vcpu, ax_rb); */ |
795 | kvm_cvt_df(&VCPU_FPR(vcpu, ax_rb), |
796 | &vcpu->arch.qpr[ax_rd]); |
797 | break; |
798 | case OP_4X_PS_MERGE01: |
799 | WARN_ON(rcomp); |
800 | VCPU_FPR(vcpu, ax_rd) = VCPU_FPR(vcpu, ax_ra); |
801 | vcpu->arch.qpr[ax_rd] = vcpu->arch.qpr[ax_rb]; |
802 | break; |
803 | case OP_4X_PS_MERGE10: |
804 | WARN_ON(rcomp); |
805 | /* VCPU_FPR(vcpu, ax_rd) = vcpu->arch.qpr[ax_ra]; */ |
806 | kvm_cvt_fd(&vcpu->arch.qpr[ax_ra], |
807 | &VCPU_FPR(vcpu, ax_rd)); |
808 | /* vcpu->arch.qpr[ax_rd] = VCPU_FPR(vcpu, ax_rb); */ |
809 | kvm_cvt_df(&VCPU_FPR(vcpu, ax_rb), |
810 | &vcpu->arch.qpr[ax_rd]); |
811 | break; |
812 | case OP_4X_PS_MERGE11: |
813 | WARN_ON(rcomp); |
814 | /* VCPU_FPR(vcpu, ax_rd) = vcpu->arch.qpr[ax_ra]; */ |
815 | kvm_cvt_fd(&vcpu->arch.qpr[ax_ra], |
816 | &VCPU_FPR(vcpu, ax_rd)); |
817 | vcpu->arch.qpr[ax_rd] = vcpu->arch.qpr[ax_rb]; |
818 | break; |
819 | } |
820 | /* XW form */ |
821 | switch (inst_get_field(inst, msb: 25, lsb: 30)) { |
822 | case OP_4XW_PSQ_STX: |
823 | { |
824 | ulong addr = ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0; |
825 | bool w = inst_get_field(inst, msb: 21, lsb: 21) ? true : false; |
826 | int i = inst_get_field(inst, msb: 22, lsb: 24); |
827 | |
828 | addr += kvmppc_get_gpr(vcpu, ax_rb); |
829 | emulated = kvmppc_emulate_psq_store(vcpu, rs: ax_rd, addr, w, i); |
830 | break; |
831 | } |
832 | case OP_4XW_PSQ_STUX: |
833 | { |
834 | ulong addr = kvmppc_get_gpr(vcpu, ax_ra); |
835 | bool w = inst_get_field(inst, msb: 21, lsb: 21) ? true : false; |
836 | int i = inst_get_field(inst, msb: 22, lsb: 24); |
837 | |
838 | addr += kvmppc_get_gpr(vcpu, ax_rb); |
839 | emulated = kvmppc_emulate_psq_store(vcpu, rs: ax_rd, addr, w, i); |
840 | |
841 | if (emulated == EMULATE_DONE) |
842 | kvmppc_set_gpr(vcpu, ax_ra, addr); |
843 | break; |
844 | } |
845 | } |
846 | /* A form */ |
847 | switch (inst_get_field(inst, msb: 26, lsb: 30)) { |
848 | case OP_4A_PS_SUM1: |
849 | emulated = kvmppc_ps_two_in(vcpu, rcomp, ax_rd, |
850 | ax_rb, ax_ra, SCALAR_NO_PS0 | SCALAR_HIGH, fps_fadds); |
851 | VCPU_FPR(vcpu, ax_rd) = VCPU_FPR(vcpu, ax_rc); |
852 | break; |
853 | case OP_4A_PS_SUM0: |
854 | emulated = kvmppc_ps_two_in(vcpu, rcomp, ax_rd, |
855 | ax_ra, ax_rb, SCALAR_NO_PS1 | SCALAR_LOW, fps_fadds); |
856 | vcpu->arch.qpr[ax_rd] = vcpu->arch.qpr[ax_rc]; |
857 | break; |
858 | case OP_4A_PS_MULS0: |
859 | emulated = kvmppc_ps_two_in(vcpu, rcomp, ax_rd, |
860 | ax_ra, ax_rc, SCALAR_HIGH, fps_fmuls); |
861 | break; |
862 | case OP_4A_PS_MULS1: |
863 | emulated = kvmppc_ps_two_in(vcpu, rcomp, ax_rd, |
864 | ax_ra, ax_rc, SCALAR_LOW, fps_fmuls); |
865 | break; |
866 | case OP_4A_PS_MADDS0: |
867 | emulated = kvmppc_ps_three_in(vcpu, rcomp, ax_rd, |
868 | ax_ra, ax_rc, ax_rb, SCALAR_HIGH, fps_fmadds); |
869 | break; |
870 | case OP_4A_PS_MADDS1: |
871 | emulated = kvmppc_ps_three_in(vcpu, rcomp, ax_rd, |
872 | ax_ra, ax_rc, ax_rb, SCALAR_LOW, fps_fmadds); |
873 | break; |
874 | case OP_4A_PS_DIV: |
875 | emulated = kvmppc_ps_two_in(vcpu, rcomp, ax_rd, |
876 | ax_ra, ax_rb, SCALAR_NONE, fps_fdivs); |
877 | break; |
878 | case OP_4A_PS_SUB: |
879 | emulated = kvmppc_ps_two_in(vcpu, rcomp, ax_rd, |
880 | ax_ra, ax_rb, SCALAR_NONE, fps_fsubs); |
881 | break; |
882 | case OP_4A_PS_ADD: |
883 | emulated = kvmppc_ps_two_in(vcpu, rcomp, ax_rd, |
884 | ax_ra, ax_rb, SCALAR_NONE, fps_fadds); |
885 | break; |
886 | case OP_4A_PS_SEL: |
887 | emulated = kvmppc_ps_three_in(vcpu, rcomp, ax_rd, |
888 | ax_ra, ax_rc, ax_rb, SCALAR_NONE, fps_fsel); |
889 | break; |
890 | case OP_4A_PS_RES: |
891 | emulated = kvmppc_ps_one_in(vcpu, rcomp, ax_rd, |
892 | ax_rb, fps_fres); |
893 | break; |
894 | case OP_4A_PS_MUL: |
895 | emulated = kvmppc_ps_two_in(vcpu, rcomp, ax_rd, |
896 | ax_ra, ax_rc, SCALAR_NONE, fps_fmuls); |
897 | break; |
898 | case OP_4A_PS_RSQRTE: |
899 | emulated = kvmppc_ps_one_in(vcpu, rcomp, ax_rd, |
900 | ax_rb, fps_frsqrte); |
901 | break; |
902 | case OP_4A_PS_MSUB: |
903 | emulated = kvmppc_ps_three_in(vcpu, rcomp, ax_rd, |
904 | ax_ra, ax_rc, ax_rb, SCALAR_NONE, fps_fmsubs); |
905 | break; |
906 | case OP_4A_PS_MADD: |
907 | emulated = kvmppc_ps_three_in(vcpu, rcomp, ax_rd, |
908 | ax_ra, ax_rc, ax_rb, SCALAR_NONE, fps_fmadds); |
909 | break; |
910 | case OP_4A_PS_NMSUB: |
911 | emulated = kvmppc_ps_three_in(vcpu, rcomp, ax_rd, |
912 | ax_ra, ax_rc, ax_rb, SCALAR_NONE, fps_fnmsubs); |
913 | break; |
914 | case OP_4A_PS_NMADD: |
915 | emulated = kvmppc_ps_three_in(vcpu, rcomp, ax_rd, |
916 | ax_ra, ax_rc, ax_rb, SCALAR_NONE, fps_fnmadds); |
917 | break; |
918 | } |
919 | break; |
920 | |
921 | /* Real FPU operations */ |
922 | |
923 | case OP_LFS: |
924 | { |
925 | ulong addr = (ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0) + full_d; |
926 | |
927 | emulated = kvmppc_emulate_fpr_load(vcpu, rs: ax_rd, addr, |
928 | FPU_LS_SINGLE); |
929 | break; |
930 | } |
931 | case OP_LFSU: |
932 | { |
933 | ulong addr = kvmppc_get_gpr(vcpu, ax_ra) + full_d; |
934 | |
935 | emulated = kvmppc_emulate_fpr_load(vcpu, rs: ax_rd, addr, |
936 | FPU_LS_SINGLE); |
937 | |
938 | if (emulated == EMULATE_DONE) |
939 | kvmppc_set_gpr(vcpu, ax_ra, addr); |
940 | break; |
941 | } |
942 | case OP_LFD: |
943 | { |
944 | ulong addr = (ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0) + full_d; |
945 | |
946 | emulated = kvmppc_emulate_fpr_load(vcpu, rs: ax_rd, addr, |
947 | FPU_LS_DOUBLE); |
948 | break; |
949 | } |
950 | case OP_LFDU: |
951 | { |
952 | ulong addr = kvmppc_get_gpr(vcpu, ax_ra) + full_d; |
953 | |
954 | emulated = kvmppc_emulate_fpr_load(vcpu, rs: ax_rd, addr, |
955 | FPU_LS_DOUBLE); |
956 | |
957 | if (emulated == EMULATE_DONE) |
958 | kvmppc_set_gpr(vcpu, ax_ra, addr); |
959 | break; |
960 | } |
961 | case OP_STFS: |
962 | { |
963 | ulong addr = (ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0) + full_d; |
964 | |
965 | emulated = kvmppc_emulate_fpr_store(vcpu, rs: ax_rd, addr, |
966 | FPU_LS_SINGLE); |
967 | break; |
968 | } |
969 | case OP_STFSU: |
970 | { |
971 | ulong addr = kvmppc_get_gpr(vcpu, ax_ra) + full_d; |
972 | |
973 | emulated = kvmppc_emulate_fpr_store(vcpu, rs: ax_rd, addr, |
974 | FPU_LS_SINGLE); |
975 | |
976 | if (emulated == EMULATE_DONE) |
977 | kvmppc_set_gpr(vcpu, ax_ra, addr); |
978 | break; |
979 | } |
980 | case OP_STFD: |
981 | { |
982 | ulong addr = (ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0) + full_d; |
983 | |
984 | emulated = kvmppc_emulate_fpr_store(vcpu, rs: ax_rd, addr, |
985 | FPU_LS_DOUBLE); |
986 | break; |
987 | } |
988 | case OP_STFDU: |
989 | { |
990 | ulong addr = kvmppc_get_gpr(vcpu, ax_ra) + full_d; |
991 | |
992 | emulated = kvmppc_emulate_fpr_store(vcpu, rs: ax_rd, addr, |
993 | FPU_LS_DOUBLE); |
994 | |
995 | if (emulated == EMULATE_DONE) |
996 | kvmppc_set_gpr(vcpu, ax_ra, addr); |
997 | break; |
998 | } |
999 | case 31: |
1000 | switch (inst_get_field(inst, msb: 21, lsb: 30)) { |
1001 | case OP_31_LFSX: |
1002 | { |
1003 | ulong addr = ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0; |
1004 | |
1005 | addr += kvmppc_get_gpr(vcpu, ax_rb); |
1006 | emulated = kvmppc_emulate_fpr_load(vcpu, rs: ax_rd, |
1007 | addr, FPU_LS_SINGLE); |
1008 | break; |
1009 | } |
1010 | case OP_31_LFSUX: |
1011 | { |
1012 | ulong addr = kvmppc_get_gpr(vcpu, ax_ra) + |
1013 | kvmppc_get_gpr(vcpu, ax_rb); |
1014 | |
1015 | emulated = kvmppc_emulate_fpr_load(vcpu, rs: ax_rd, |
1016 | addr, FPU_LS_SINGLE); |
1017 | |
1018 | if (emulated == EMULATE_DONE) |
1019 | kvmppc_set_gpr(vcpu, ax_ra, addr); |
1020 | break; |
1021 | } |
1022 | case OP_31_LFDX: |
1023 | { |
1024 | ulong addr = (ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0) + |
1025 | kvmppc_get_gpr(vcpu, ax_rb); |
1026 | |
1027 | emulated = kvmppc_emulate_fpr_load(vcpu, rs: ax_rd, |
1028 | addr, FPU_LS_DOUBLE); |
1029 | break; |
1030 | } |
1031 | case OP_31_LFDUX: |
1032 | { |
1033 | ulong addr = kvmppc_get_gpr(vcpu, ax_ra) + |
1034 | kvmppc_get_gpr(vcpu, ax_rb); |
1035 | |
1036 | emulated = kvmppc_emulate_fpr_load(vcpu, rs: ax_rd, |
1037 | addr, FPU_LS_DOUBLE); |
1038 | |
1039 | if (emulated == EMULATE_DONE) |
1040 | kvmppc_set_gpr(vcpu, ax_ra, addr); |
1041 | break; |
1042 | } |
1043 | case OP_31_STFSX: |
1044 | { |
1045 | ulong addr = (ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0) + |
1046 | kvmppc_get_gpr(vcpu, ax_rb); |
1047 | |
1048 | emulated = kvmppc_emulate_fpr_store(vcpu, rs: ax_rd, |
1049 | addr, FPU_LS_SINGLE); |
1050 | break; |
1051 | } |
1052 | case OP_31_STFSUX: |
1053 | { |
1054 | ulong addr = kvmppc_get_gpr(vcpu, ax_ra) + |
1055 | kvmppc_get_gpr(vcpu, ax_rb); |
1056 | |
1057 | emulated = kvmppc_emulate_fpr_store(vcpu, rs: ax_rd, |
1058 | addr, FPU_LS_SINGLE); |
1059 | |
1060 | if (emulated == EMULATE_DONE) |
1061 | kvmppc_set_gpr(vcpu, ax_ra, addr); |
1062 | break; |
1063 | } |
1064 | case OP_31_STFX: |
1065 | { |
1066 | ulong addr = (ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0) + |
1067 | kvmppc_get_gpr(vcpu, ax_rb); |
1068 | |
1069 | emulated = kvmppc_emulate_fpr_store(vcpu, rs: ax_rd, |
1070 | addr, FPU_LS_DOUBLE); |
1071 | break; |
1072 | } |
1073 | case OP_31_STFUX: |
1074 | { |
1075 | ulong addr = kvmppc_get_gpr(vcpu, ax_ra) + |
1076 | kvmppc_get_gpr(vcpu, ax_rb); |
1077 | |
1078 | emulated = kvmppc_emulate_fpr_store(vcpu, rs: ax_rd, |
1079 | addr, FPU_LS_DOUBLE); |
1080 | |
1081 | if (emulated == EMULATE_DONE) |
1082 | kvmppc_set_gpr(vcpu, ax_ra, addr); |
1083 | break; |
1084 | } |
1085 | case OP_31_STFIWX: |
1086 | { |
1087 | ulong addr = (ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0) + |
1088 | kvmppc_get_gpr(vcpu, ax_rb); |
1089 | |
1090 | emulated = kvmppc_emulate_fpr_store(vcpu, rs: ax_rd, |
1091 | addr, |
1092 | FPU_LS_SINGLE_LOW); |
1093 | break; |
1094 | } |
1095 | break; |
1096 | } |
1097 | break; |
1098 | case 59: |
1099 | switch (inst_get_field(inst, msb: 21, lsb: 30)) { |
1100 | case OP_59_FADDS: |
1101 | fpd_fadds(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_b); |
1102 | kvmppc_sync_qpr(vcpu, rt: ax_rd); |
1103 | break; |
1104 | case OP_59_FSUBS: |
1105 | fpd_fsubs(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_b); |
1106 | kvmppc_sync_qpr(vcpu, rt: ax_rd); |
1107 | break; |
1108 | case OP_59_FDIVS: |
1109 | fpd_fdivs(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_b); |
1110 | kvmppc_sync_qpr(vcpu, rt: ax_rd); |
1111 | break; |
1112 | case OP_59_FRES: |
1113 | fpd_fres(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_b); |
1114 | kvmppc_sync_qpr(vcpu, rt: ax_rd); |
1115 | break; |
1116 | case OP_59_FRSQRTES: |
1117 | fpd_frsqrtes(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_b); |
1118 | kvmppc_sync_qpr(vcpu, rt: ax_rd); |
1119 | break; |
1120 | } |
1121 | switch (inst_get_field(inst, msb: 26, lsb: 30)) { |
1122 | case OP_59_FMULS: |
1123 | fpd_fmuls(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_c); |
1124 | kvmppc_sync_qpr(vcpu, rt: ax_rd); |
1125 | break; |
1126 | case OP_59_FMSUBS: |
1127 | fpd_fmsubs(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b); |
1128 | kvmppc_sync_qpr(vcpu, rt: ax_rd); |
1129 | break; |
1130 | case OP_59_FMADDS: |
1131 | fpd_fmadds(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b); |
1132 | kvmppc_sync_qpr(vcpu, rt: ax_rd); |
1133 | break; |
1134 | case OP_59_FNMSUBS: |
1135 | fpd_fnmsubs(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b); |
1136 | kvmppc_sync_qpr(vcpu, rt: ax_rd); |
1137 | break; |
1138 | case OP_59_FNMADDS: |
1139 | fpd_fnmadds(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b); |
1140 | kvmppc_sync_qpr(vcpu, rt: ax_rd); |
1141 | break; |
1142 | } |
1143 | break; |
1144 | case 63: |
1145 | switch (inst_get_field(inst, msb: 21, lsb: 30)) { |
1146 | case OP_63_MTFSB0: |
1147 | case OP_63_MTFSB1: |
1148 | case OP_63_MCRFS: |
1149 | case OP_63_MTFSFI: |
1150 | /* XXX need to implement */ |
1151 | break; |
1152 | case OP_63_MFFS: |
1153 | /* XXX missing CR */ |
1154 | *fpr_d = vcpu->arch.fp.fpscr; |
1155 | break; |
1156 | case OP_63_MTFSF: |
1157 | /* XXX missing fm bits */ |
1158 | /* XXX missing CR */ |
1159 | vcpu->arch.fp.fpscr = *fpr_b; |
1160 | break; |
1161 | case OP_63_FCMPU: |
1162 | { |
1163 | u32 tmp_cr; |
1164 | u32 cr0_mask = 0xf0000000; |
1165 | u32 cr_shift = inst_get_field(inst, msb: 6, lsb: 8) * 4; |
1166 | |
1167 | fpd_fcmpu(&vcpu->arch.fp.fpscr, &tmp_cr, fpr_a, fpr_b); |
1168 | cr &= ~(cr0_mask >> cr_shift); |
1169 | cr |= (cr & cr0_mask) >> cr_shift; |
1170 | break; |
1171 | } |
1172 | case OP_63_FCMPO: |
1173 | { |
1174 | u32 tmp_cr; |
1175 | u32 cr0_mask = 0xf0000000; |
1176 | u32 cr_shift = inst_get_field(inst, msb: 6, lsb: 8) * 4; |
1177 | |
1178 | fpd_fcmpo(&vcpu->arch.fp.fpscr, &tmp_cr, fpr_a, fpr_b); |
1179 | cr &= ~(cr0_mask >> cr_shift); |
1180 | cr |= (cr & cr0_mask) >> cr_shift; |
1181 | break; |
1182 | } |
1183 | case OP_63_FNEG: |
1184 | fpd_fneg(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_b); |
1185 | break; |
1186 | case OP_63_FMR: |
1187 | *fpr_d = *fpr_b; |
1188 | break; |
1189 | case OP_63_FABS: |
1190 | fpd_fabs(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_b); |
1191 | break; |
1192 | case OP_63_FCPSGN: |
1193 | fpd_fcpsgn(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_b); |
1194 | break; |
1195 | case OP_63_FDIV: |
1196 | fpd_fdiv(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_b); |
1197 | break; |
1198 | case OP_63_FADD: |
1199 | fpd_fadd(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_b); |
1200 | break; |
1201 | case OP_63_FSUB: |
1202 | fpd_fsub(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_b); |
1203 | break; |
1204 | case OP_63_FCTIW: |
1205 | fpd_fctiw(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_b); |
1206 | break; |
1207 | case OP_63_FCTIWZ: |
1208 | fpd_fctiwz(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_b); |
1209 | break; |
1210 | case OP_63_FRSP: |
1211 | fpd_frsp(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_b); |
1212 | kvmppc_sync_qpr(vcpu, rt: ax_rd); |
1213 | break; |
1214 | case OP_63_FRSQRTE: |
1215 | { |
1216 | double one = 1.0f; |
1217 | |
1218 | /* fD = sqrt(fB) */ |
1219 | fpd_fsqrt(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_b); |
1220 | /* fD = 1.0f / fD */ |
1221 | fpd_fdiv(&vcpu->arch.fp.fpscr, &cr, fpr_d, (u64*)&one, fpr_d); |
1222 | break; |
1223 | } |
1224 | } |
1225 | switch (inst_get_field(inst, msb: 26, lsb: 30)) { |
1226 | case OP_63_FMUL: |
1227 | fpd_fmul(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_c); |
1228 | break; |
1229 | case OP_63_FSEL: |
1230 | fpd_fsel(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b); |
1231 | break; |
1232 | case OP_63_FMSUB: |
1233 | fpd_fmsub(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b); |
1234 | break; |
1235 | case OP_63_FMADD: |
1236 | fpd_fmadd(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b); |
1237 | break; |
1238 | case OP_63_FNMSUB: |
1239 | fpd_fnmsub(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b); |
1240 | break; |
1241 | case OP_63_FNMADD: |
1242 | fpd_fnmadd(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b); |
1243 | break; |
1244 | } |
1245 | break; |
1246 | } |
1247 | |
1248 | #ifdef DEBUG |
1249 | for (i = 0; i < ARRAY_SIZE(vcpu->arch.fp.fpr); i++) { |
1250 | u32 f; |
1251 | kvm_cvt_df(&VCPU_FPR(vcpu, i), &f); |
1252 | dprintk(KERN_INFO "FPR[%d] = 0x%x\n" , i, f); |
1253 | } |
1254 | #endif |
1255 | |
1256 | if (rcomp) |
1257 | kvmppc_set_cr(vcpu, cr); |
1258 | |
1259 | disable_kernel_fp(); |
1260 | preempt_enable(); |
1261 | |
1262 | return emulated; |
1263 | } |
1264 | |