Path: blob/master/arch/powerpc/kvm/book3s_paired_singles.c
26450 views
// SPDX-License-Identifier: GPL-2.0-only1/*2*3* Copyright Novell Inc 20104*5* Authors: Alexander Graf <[email protected]>6*/78#include <asm/kvm.h>9#include <asm/kvm_ppc.h>10#include <asm/disassemble.h>11#include <asm/kvm_book3s.h>12#include <asm/kvm_fpu.h>13#include <asm/reg.h>14#include <asm/cacheflush.h>15#include <asm/switch_to.h>16#include <linux/vmalloc.h>1718/* #define DEBUG */1920#ifdef DEBUG21#define dprintk printk22#else23#define dprintk(...) do { } while(0);24#endif2526#define OP_LFS 4827#define OP_LFSU 4928#define OP_LFD 5029#define OP_LFDU 5130#define OP_STFS 5231#define OP_STFSU 5332#define OP_STFD 5433#define OP_STFDU 5534#define OP_PSQ_L 5635#define OP_PSQ_LU 5736#define OP_PSQ_ST 6037#define OP_PSQ_STU 613839#define OP_31_LFSX 53540#define OP_31_LFSUX 56741#define OP_31_LFDX 59942#define OP_31_LFDUX 63143#define OP_31_STFSX 66344#define OP_31_STFSUX 69545#define OP_31_STFX 72746#define OP_31_STFUX 75947#define OP_31_LWIZX 88748#define OP_31_STFIWX 9834950#define OP_59_FADDS 2151#define OP_59_FSUBS 2052#define OP_59_FSQRTS 2253#define OP_59_FDIVS 1854#define OP_59_FRES 2455#define OP_59_FMULS 2556#define OP_59_FRSQRTES 2657#define OP_59_FMSUBS 2858#define OP_59_FMADDS 2959#define OP_59_FNMSUBS 3060#define OP_59_FNMADDS 316162#define OP_63_FCMPU 063#define OP_63_FCPSGN 864#define OP_63_FRSP 1265#define OP_63_FCTIW 1466#define OP_63_FCTIWZ 1567#define OP_63_FDIV 1868#define OP_63_FADD 2169#define OP_63_FSQRT 2270#define OP_63_FSEL 2371#define OP_63_FRE 2472#define OP_63_FMUL 2573#define OP_63_FRSQRTE 2674#define OP_63_FMSUB 2875#define OP_63_FMADD 2976#define OP_63_FNMSUB 3077#define OP_63_FNMADD 3178#define OP_63_FCMPO 3279#define OP_63_MTFSB1 38 // XXX80#define OP_63_FSUB 2081#define OP_63_FNEG 4082#define OP_63_MCRFS 6483#define OP_63_MTFSB0 7084#define OP_63_FMR 7285#define OP_63_MTFSFI 13486#define OP_63_FABS 26487#define OP_63_MFFS 58388#define OP_63_MTFSF 7118990#define OP_4X_PS_CMPU0 091#define OP_4X_PSQ_LX 692#define OP_4XW_PSQ_STX 793#define OP_4A_PS_SUM0 1094#define OP_4A_PS_SUM1 1195#define OP_4A_PS_MULS0 1296#define OP_4A_PS_MULS1 1397#define OP_4A_PS_MADDS0 1498#define OP_4A_PS_MADDS1 1599#define OP_4A_PS_DIV 18100#define OP_4A_PS_SUB 20101#define OP_4A_PS_ADD 21102#define OP_4A_PS_SEL 23103#define OP_4A_PS_RES 24104#define OP_4A_PS_MUL 25105#define OP_4A_PS_RSQRTE 26106#define OP_4A_PS_MSUB 28107#define OP_4A_PS_MADD 29108#define OP_4A_PS_NMSUB 30109#define OP_4A_PS_NMADD 31110#define OP_4X_PS_CMPO0 32111#define OP_4X_PSQ_LUX 38112#define OP_4XW_PSQ_STUX 39113#define OP_4X_PS_NEG 40114#define OP_4X_PS_CMPU1 64115#define OP_4X_PS_MR 72116#define OP_4X_PS_CMPO1 96117#define OP_4X_PS_NABS 136118#define OP_4X_PS_ABS 264119#define OP_4X_PS_MERGE00 528120#define OP_4X_PS_MERGE01 560121#define OP_4X_PS_MERGE10 592122#define OP_4X_PS_MERGE11 624123124#define SCALAR_NONE 0125#define SCALAR_HIGH (1 << 0)126#define SCALAR_LOW (1 << 1)127#define SCALAR_NO_PS0 (1 << 2)128#define SCALAR_NO_PS1 (1 << 3)129130#define GQR_ST_TYPE_MASK 0x00000007131#define GQR_ST_TYPE_SHIFT 0132#define GQR_ST_SCALE_MASK 0x00003f00133#define GQR_ST_SCALE_SHIFT 8134#define GQR_LD_TYPE_MASK 0x00070000135#define GQR_LD_TYPE_SHIFT 16136#define GQR_LD_SCALE_MASK 0x3f000000137#define GQR_LD_SCALE_SHIFT 24138139#define GQR_QUANTIZE_FLOAT 0140#define GQR_QUANTIZE_U8 4141#define GQR_QUANTIZE_U16 5142#define GQR_QUANTIZE_S8 6143#define GQR_QUANTIZE_S16 7144145#define FPU_LS_SINGLE 0146#define FPU_LS_DOUBLE 1147#define FPU_LS_SINGLE_LOW 2148149static inline void kvmppc_sync_qpr(struct kvm_vcpu *vcpu, int rt)150{151kvm_cvt_df(&VCPU_FPR(vcpu, rt), &vcpu->arch.qpr[rt]);152}153154static void kvmppc_inject_pf(struct kvm_vcpu *vcpu, ulong eaddr, bool is_store)155{156u32 dsisr;157u64 msr = kvmppc_get_msr(vcpu);158159msr = kvmppc_set_field(msr, 33, 36, 0);160msr = kvmppc_set_field(msr, 42, 47, 0);161kvmppc_set_msr(vcpu, msr);162kvmppc_set_dar(vcpu, eaddr);163/* Page Fault */164dsisr = kvmppc_set_field(0, 33, 33, 1);165if (is_store)166dsisr = kvmppc_set_field(dsisr, 38, 38, 1);167kvmppc_set_dsisr(vcpu, dsisr);168kvmppc_book3s_queue_irqprio(vcpu, BOOK3S_INTERRUPT_DATA_STORAGE);169}170171static int kvmppc_emulate_fpr_load(struct kvm_vcpu *vcpu,172int rs, ulong addr, int ls_type)173{174int emulated = EMULATE_FAIL;175int r;176char tmp[8];177int len = sizeof(u32);178179if (ls_type == FPU_LS_DOUBLE)180len = sizeof(u64);181182/* read from memory */183r = kvmppc_ld(vcpu, &addr, len, tmp, true);184vcpu->arch.paddr_accessed = addr;185186if (r < 0) {187kvmppc_inject_pf(vcpu, addr, false);188goto done_load;189} else if (r == EMULATE_DO_MMIO) {190emulated = kvmppc_handle_load(vcpu, KVM_MMIO_REG_FPR | rs,191len, 1);192goto done_load;193}194195emulated = EMULATE_DONE;196197/* put in registers */198switch (ls_type) {199case FPU_LS_SINGLE:200kvm_cvt_fd((u32*)tmp, &VCPU_FPR(vcpu, rs));201vcpu->arch.qpr[rs] = *((u32*)tmp);202break;203case FPU_LS_DOUBLE:204VCPU_FPR(vcpu, rs) = *((u64*)tmp);205break;206}207208dprintk(KERN_INFO "KVM: FPR_LD [0x%llx] at 0x%lx (%d)\n", *(u64*)tmp,209addr, len);210211done_load:212return emulated;213}214215static int kvmppc_emulate_fpr_store(struct kvm_vcpu *vcpu,216int rs, ulong addr, int ls_type)217{218int emulated = EMULATE_FAIL;219int r;220char tmp[8];221u64 val;222int len;223224switch (ls_type) {225case FPU_LS_SINGLE:226kvm_cvt_df(&VCPU_FPR(vcpu, rs), (u32*)tmp);227val = *((u32*)tmp);228len = sizeof(u32);229break;230case FPU_LS_SINGLE_LOW:231*((u32*)tmp) = VCPU_FPR(vcpu, rs);232val = VCPU_FPR(vcpu, rs) & 0xffffffff;233len = sizeof(u32);234break;235case FPU_LS_DOUBLE:236*((u64*)tmp) = VCPU_FPR(vcpu, rs);237val = VCPU_FPR(vcpu, rs);238len = sizeof(u64);239break;240default:241val = 0;242len = 0;243}244245r = kvmppc_st(vcpu, &addr, len, tmp, true);246vcpu->arch.paddr_accessed = addr;247if (r < 0) {248kvmppc_inject_pf(vcpu, addr, true);249} else if (r == EMULATE_DO_MMIO) {250emulated = kvmppc_handle_store(vcpu, val, len, 1);251} else {252emulated = EMULATE_DONE;253}254255dprintk(KERN_INFO "KVM: FPR_ST [0x%llx] at 0x%lx (%d)\n",256val, addr, len);257258return emulated;259}260261static int kvmppc_emulate_psq_load(struct kvm_vcpu *vcpu,262int rs, ulong addr, bool w, int i)263{264int emulated = EMULATE_FAIL;265int r;266float one = 1.0;267u32 tmp[2];268269/* read from memory */270if (w) {271r = kvmppc_ld(vcpu, &addr, sizeof(u32), tmp, true);272memcpy(&tmp[1], &one, sizeof(u32));273} else {274r = kvmppc_ld(vcpu, &addr, sizeof(u32) * 2, tmp, true);275}276vcpu->arch.paddr_accessed = addr;277if (r < 0) {278kvmppc_inject_pf(vcpu, addr, false);279goto done_load;280} else if ((r == EMULATE_DO_MMIO) && w) {281emulated = kvmppc_handle_load(vcpu, KVM_MMIO_REG_FPR | rs,2824, 1);283vcpu->arch.qpr[rs] = tmp[1];284goto done_load;285} else if (r == EMULATE_DO_MMIO) {286emulated = kvmppc_handle_load(vcpu, KVM_MMIO_REG_FQPR | rs,2878, 1);288goto done_load;289}290291emulated = EMULATE_DONE;292293/* put in registers */294kvm_cvt_fd(&tmp[0], &VCPU_FPR(vcpu, rs));295vcpu->arch.qpr[rs] = tmp[1];296297dprintk(KERN_INFO "KVM: PSQ_LD [0x%x, 0x%x] at 0x%lx (%d)\n", tmp[0],298tmp[1], addr, w ? 4 : 8);299300done_load:301return emulated;302}303304static int kvmppc_emulate_psq_store(struct kvm_vcpu *vcpu,305int rs, ulong addr, bool w, int i)306{307int emulated = EMULATE_FAIL;308int r;309u32 tmp[2];310int len = w ? sizeof(u32) : sizeof(u64);311312kvm_cvt_df(&VCPU_FPR(vcpu, rs), &tmp[0]);313tmp[1] = vcpu->arch.qpr[rs];314315r = kvmppc_st(vcpu, &addr, len, tmp, true);316vcpu->arch.paddr_accessed = addr;317if (r < 0) {318kvmppc_inject_pf(vcpu, addr, true);319} else if ((r == EMULATE_DO_MMIO) && w) {320emulated = kvmppc_handle_store(vcpu, tmp[0], 4, 1);321} else if (r == EMULATE_DO_MMIO) {322u64 val = ((u64)tmp[0] << 32) | tmp[1];323emulated = kvmppc_handle_store(vcpu, val, 8, 1);324} else {325emulated = EMULATE_DONE;326}327328dprintk(KERN_INFO "KVM: PSQ_ST [0x%x, 0x%x] at 0x%lx (%d)\n",329tmp[0], tmp[1], addr, len);330331return emulated;332}333334/*335* Cuts out inst bits with ordering according to spec.336* That means the leftmost bit is zero. All given bits are included.337*/338static inline u32 inst_get_field(u32 inst, int msb, int lsb)339{340return kvmppc_get_field(inst, msb + 32, lsb + 32);341}342343static bool kvmppc_inst_is_paired_single(struct kvm_vcpu *vcpu, u32 inst)344{345if (!(vcpu->arch.hflags & BOOK3S_HFLAG_PAIRED_SINGLE))346return false;347348switch (get_op(inst)) {349case OP_PSQ_L:350case OP_PSQ_LU:351case OP_PSQ_ST:352case OP_PSQ_STU:353case OP_LFS:354case OP_LFSU:355case OP_LFD:356case OP_LFDU:357case OP_STFS:358case OP_STFSU:359case OP_STFD:360case OP_STFDU:361return true;362case 4:363/* X form */364switch (inst_get_field(inst, 21, 30)) {365case OP_4X_PS_CMPU0:366case OP_4X_PSQ_LX:367case OP_4X_PS_CMPO0:368case OP_4X_PSQ_LUX:369case OP_4X_PS_NEG:370case OP_4X_PS_CMPU1:371case OP_4X_PS_MR:372case OP_4X_PS_CMPO1:373case OP_4X_PS_NABS:374case OP_4X_PS_ABS:375case OP_4X_PS_MERGE00:376case OP_4X_PS_MERGE01:377case OP_4X_PS_MERGE10:378case OP_4X_PS_MERGE11:379return true;380}381/* XW form */382switch (inst_get_field(inst, 25, 30)) {383case OP_4XW_PSQ_STX:384case OP_4XW_PSQ_STUX:385return true;386}387/* A form */388switch (inst_get_field(inst, 26, 30)) {389case OP_4A_PS_SUM1:390case OP_4A_PS_SUM0:391case OP_4A_PS_MULS0:392case OP_4A_PS_MULS1:393case OP_4A_PS_MADDS0:394case OP_4A_PS_MADDS1:395case OP_4A_PS_DIV:396case OP_4A_PS_SUB:397case OP_4A_PS_ADD:398case OP_4A_PS_SEL:399case OP_4A_PS_RES:400case OP_4A_PS_MUL:401case OP_4A_PS_RSQRTE:402case OP_4A_PS_MSUB:403case OP_4A_PS_MADD:404case OP_4A_PS_NMSUB:405case OP_4A_PS_NMADD:406return true;407}408break;409case 59:410switch (inst_get_field(inst, 21, 30)) {411case OP_59_FADDS:412case OP_59_FSUBS:413case OP_59_FDIVS:414case OP_59_FRES:415case OP_59_FRSQRTES:416return true;417}418switch (inst_get_field(inst, 26, 30)) {419case OP_59_FMULS:420case OP_59_FMSUBS:421case OP_59_FMADDS:422case OP_59_FNMSUBS:423case OP_59_FNMADDS:424return true;425}426break;427case 63:428switch (inst_get_field(inst, 21, 30)) {429case OP_63_MTFSB0:430case OP_63_MTFSB1:431case OP_63_MTFSF:432case OP_63_MTFSFI:433case OP_63_MCRFS:434case OP_63_MFFS:435case OP_63_FCMPU:436case OP_63_FCMPO:437case OP_63_FNEG:438case OP_63_FMR:439case OP_63_FABS:440case OP_63_FRSP:441case OP_63_FDIV:442case OP_63_FADD:443case OP_63_FSUB:444case OP_63_FCTIW:445case OP_63_FCTIWZ:446case OP_63_FRSQRTE:447case OP_63_FCPSGN:448return true;449}450switch (inst_get_field(inst, 26, 30)) {451case OP_63_FMUL:452case OP_63_FSEL:453case OP_63_FMSUB:454case OP_63_FMADD:455case OP_63_FNMSUB:456case OP_63_FNMADD:457return true;458}459break;460case 31:461switch (inst_get_field(inst, 21, 30)) {462case OP_31_LFSX:463case OP_31_LFSUX:464case OP_31_LFDX:465case OP_31_LFDUX:466case OP_31_STFSX:467case OP_31_STFSUX:468case OP_31_STFX:469case OP_31_STFUX:470case OP_31_STFIWX:471return true;472}473break;474}475476return false;477}478479static int get_d_signext(u32 inst)480{481int d = inst & 0x8ff;482483if (d & 0x800)484return -(d & 0x7ff);485486return (d & 0x7ff);487}488489static int kvmppc_ps_three_in(struct kvm_vcpu *vcpu, bool rc,490int reg_out, int reg_in1, int reg_in2,491int reg_in3, int scalar,492void (*func)(u64 *fpscr,493u32 *dst, u32 *src1,494u32 *src2, u32 *src3))495{496u32 *qpr = vcpu->arch.qpr;497u32 ps0_out;498u32 ps0_in1, ps0_in2, ps0_in3;499u32 ps1_in1, ps1_in2, ps1_in3;500501/* RC */502WARN_ON(rc);503504/* PS0 */505kvm_cvt_df(&VCPU_FPR(vcpu, reg_in1), &ps0_in1);506kvm_cvt_df(&VCPU_FPR(vcpu, reg_in2), &ps0_in2);507kvm_cvt_df(&VCPU_FPR(vcpu, reg_in3), &ps0_in3);508509if (scalar & SCALAR_LOW)510ps0_in2 = qpr[reg_in2];511512func(&vcpu->arch.fp.fpscr, &ps0_out, &ps0_in1, &ps0_in2, &ps0_in3);513514dprintk(KERN_INFO "PS3 ps0 -> f(0x%x, 0x%x, 0x%x) = 0x%x\n",515ps0_in1, ps0_in2, ps0_in3, ps0_out);516517if (!(scalar & SCALAR_NO_PS0))518kvm_cvt_fd(&ps0_out, &VCPU_FPR(vcpu, reg_out));519520/* PS1 */521ps1_in1 = qpr[reg_in1];522ps1_in2 = qpr[reg_in2];523ps1_in3 = qpr[reg_in3];524525if (scalar & SCALAR_HIGH)526ps1_in2 = ps0_in2;527528if (!(scalar & SCALAR_NO_PS1))529func(&vcpu->arch.fp.fpscr, &qpr[reg_out], &ps1_in1, &ps1_in2, &ps1_in3);530531dprintk(KERN_INFO "PS3 ps1 -> f(0x%x, 0x%x, 0x%x) = 0x%x\n",532ps1_in1, ps1_in2, ps1_in3, qpr[reg_out]);533534return EMULATE_DONE;535}536537static int kvmppc_ps_two_in(struct kvm_vcpu *vcpu, bool rc,538int reg_out, int reg_in1, int reg_in2,539int scalar,540void (*func)(u64 *fpscr,541u32 *dst, u32 *src1,542u32 *src2))543{544u32 *qpr = vcpu->arch.qpr;545u32 ps0_out;546u32 ps0_in1, ps0_in2;547u32 ps1_out;548u32 ps1_in1, ps1_in2;549550/* RC */551WARN_ON(rc);552553/* PS0 */554kvm_cvt_df(&VCPU_FPR(vcpu, reg_in1), &ps0_in1);555556if (scalar & SCALAR_LOW)557ps0_in2 = qpr[reg_in2];558else559kvm_cvt_df(&VCPU_FPR(vcpu, reg_in2), &ps0_in2);560561func(&vcpu->arch.fp.fpscr, &ps0_out, &ps0_in1, &ps0_in2);562563if (!(scalar & SCALAR_NO_PS0)) {564dprintk(KERN_INFO "PS2 ps0 -> f(0x%x, 0x%x) = 0x%x\n",565ps0_in1, ps0_in2, ps0_out);566567kvm_cvt_fd(&ps0_out, &VCPU_FPR(vcpu, reg_out));568}569570/* PS1 */571ps1_in1 = qpr[reg_in1];572ps1_in2 = qpr[reg_in2];573574if (scalar & SCALAR_HIGH)575ps1_in2 = ps0_in2;576577func(&vcpu->arch.fp.fpscr, &ps1_out, &ps1_in1, &ps1_in2);578579if (!(scalar & SCALAR_NO_PS1)) {580qpr[reg_out] = ps1_out;581582dprintk(KERN_INFO "PS2 ps1 -> f(0x%x, 0x%x) = 0x%x\n",583ps1_in1, ps1_in2, qpr[reg_out]);584}585586return EMULATE_DONE;587}588589static int kvmppc_ps_one_in(struct kvm_vcpu *vcpu, bool rc,590int reg_out, int reg_in,591void (*func)(u64 *t,592u32 *dst, u32 *src1))593{594u32 *qpr = vcpu->arch.qpr;595u32 ps0_out, ps0_in;596u32 ps1_in;597598/* RC */599WARN_ON(rc);600601/* PS0 */602kvm_cvt_df(&VCPU_FPR(vcpu, reg_in), &ps0_in);603func(&vcpu->arch.fp.fpscr, &ps0_out, &ps0_in);604605dprintk(KERN_INFO "PS1 ps0 -> f(0x%x) = 0x%x\n",606ps0_in, ps0_out);607608kvm_cvt_fd(&ps0_out, &VCPU_FPR(vcpu, reg_out));609610/* PS1 */611ps1_in = qpr[reg_in];612func(&vcpu->arch.fp.fpscr, &qpr[reg_out], &ps1_in);613614dprintk(KERN_INFO "PS1 ps1 -> f(0x%x) = 0x%x\n",615ps1_in, qpr[reg_out]);616617return EMULATE_DONE;618}619620int kvmppc_emulate_paired_single(struct kvm_vcpu *vcpu)621{622u32 inst;623ppc_inst_t pinst;624enum emulation_result emulated = EMULATE_DONE;625int ax_rd, ax_ra, ax_rb, ax_rc;626short full_d;627u64 *fpr_d, *fpr_a, *fpr_b, *fpr_c;628629bool rcomp;630u32 cr;631#ifdef DEBUG632int i;633#endif634635emulated = kvmppc_get_last_inst(vcpu, INST_GENERIC, &pinst);636inst = ppc_inst_val(pinst);637if (emulated != EMULATE_DONE)638return emulated;639640ax_rd = inst_get_field(inst, 6, 10);641ax_ra = inst_get_field(inst, 11, 15);642ax_rb = inst_get_field(inst, 16, 20);643ax_rc = inst_get_field(inst, 21, 25);644full_d = inst_get_field(inst, 16, 31);645646fpr_d = &VCPU_FPR(vcpu, ax_rd);647fpr_a = &VCPU_FPR(vcpu, ax_ra);648fpr_b = &VCPU_FPR(vcpu, ax_rb);649fpr_c = &VCPU_FPR(vcpu, ax_rc);650651rcomp = (inst & 1) ? true : false;652cr = kvmppc_get_cr(vcpu);653654if (!kvmppc_inst_is_paired_single(vcpu, inst))655return EMULATE_FAIL;656657if (!(kvmppc_get_msr(vcpu) & MSR_FP)) {658kvmppc_book3s_queue_irqprio(vcpu, BOOK3S_INTERRUPT_FP_UNAVAIL);659return EMULATE_AGAIN;660}661662kvmppc_giveup_ext(vcpu, MSR_FP);663preempt_disable();664enable_kernel_fp();665/* Do we need to clear FE0 / FE1 here? Don't think so. */666667#ifdef DEBUG668for (i = 0; i < ARRAY_SIZE(vcpu->arch.fp.fpr); i++) {669u32 f;670kvm_cvt_df(&VCPU_FPR(vcpu, i), &f);671dprintk(KERN_INFO "FPR[%d] = 0x%x / 0x%llx QPR[%d] = 0x%x\n",672i, f, VCPU_FPR(vcpu, i), i, vcpu->arch.qpr[i]);673}674#endif675676switch (get_op(inst)) {677case OP_PSQ_L:678{679ulong addr = ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0;680bool w = inst_get_field(inst, 16, 16) ? true : false;681int i = inst_get_field(inst, 17, 19);682683addr += get_d_signext(inst);684emulated = kvmppc_emulate_psq_load(vcpu, ax_rd, addr, w, i);685break;686}687case OP_PSQ_LU:688{689ulong addr = kvmppc_get_gpr(vcpu, ax_ra);690bool w = inst_get_field(inst, 16, 16) ? true : false;691int i = inst_get_field(inst, 17, 19);692693addr += get_d_signext(inst);694emulated = kvmppc_emulate_psq_load(vcpu, ax_rd, addr, w, i);695696if (emulated == EMULATE_DONE)697kvmppc_set_gpr(vcpu, ax_ra, addr);698break;699}700case OP_PSQ_ST:701{702ulong addr = ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0;703bool w = inst_get_field(inst, 16, 16) ? true : false;704int i = inst_get_field(inst, 17, 19);705706addr += get_d_signext(inst);707emulated = kvmppc_emulate_psq_store(vcpu, ax_rd, addr, w, i);708break;709}710case OP_PSQ_STU:711{712ulong addr = kvmppc_get_gpr(vcpu, ax_ra);713bool w = inst_get_field(inst, 16, 16) ? true : false;714int i = inst_get_field(inst, 17, 19);715716addr += get_d_signext(inst);717emulated = kvmppc_emulate_psq_store(vcpu, ax_rd, addr, w, i);718719if (emulated == EMULATE_DONE)720kvmppc_set_gpr(vcpu, ax_ra, addr);721break;722}723case 4:724/* X form */725switch (inst_get_field(inst, 21, 30)) {726case OP_4X_PS_CMPU0:727/* XXX */728emulated = EMULATE_FAIL;729break;730case OP_4X_PSQ_LX:731{732ulong addr = ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0;733bool w = inst_get_field(inst, 21, 21) ? true : false;734int i = inst_get_field(inst, 22, 24);735736addr += kvmppc_get_gpr(vcpu, ax_rb);737emulated = kvmppc_emulate_psq_load(vcpu, ax_rd, addr, w, i);738break;739}740case OP_4X_PS_CMPO0:741/* XXX */742emulated = EMULATE_FAIL;743break;744case OP_4X_PSQ_LUX:745{746ulong addr = kvmppc_get_gpr(vcpu, ax_ra);747bool w = inst_get_field(inst, 21, 21) ? true : false;748int i = inst_get_field(inst, 22, 24);749750addr += kvmppc_get_gpr(vcpu, ax_rb);751emulated = kvmppc_emulate_psq_load(vcpu, ax_rd, addr, w, i);752753if (emulated == EMULATE_DONE)754kvmppc_set_gpr(vcpu, ax_ra, addr);755break;756}757case OP_4X_PS_NEG:758VCPU_FPR(vcpu, ax_rd) = VCPU_FPR(vcpu, ax_rb);759VCPU_FPR(vcpu, ax_rd) ^= 0x8000000000000000ULL;760vcpu->arch.qpr[ax_rd] = vcpu->arch.qpr[ax_rb];761vcpu->arch.qpr[ax_rd] ^= 0x80000000;762break;763case OP_4X_PS_CMPU1:764/* XXX */765emulated = EMULATE_FAIL;766break;767case OP_4X_PS_MR:768WARN_ON(rcomp);769VCPU_FPR(vcpu, ax_rd) = VCPU_FPR(vcpu, ax_rb);770vcpu->arch.qpr[ax_rd] = vcpu->arch.qpr[ax_rb];771break;772case OP_4X_PS_CMPO1:773/* XXX */774emulated = EMULATE_FAIL;775break;776case OP_4X_PS_NABS:777WARN_ON(rcomp);778VCPU_FPR(vcpu, ax_rd) = VCPU_FPR(vcpu, ax_rb);779VCPU_FPR(vcpu, ax_rd) |= 0x8000000000000000ULL;780vcpu->arch.qpr[ax_rd] = vcpu->arch.qpr[ax_rb];781vcpu->arch.qpr[ax_rd] |= 0x80000000;782break;783case OP_4X_PS_ABS:784WARN_ON(rcomp);785VCPU_FPR(vcpu, ax_rd) = VCPU_FPR(vcpu, ax_rb);786VCPU_FPR(vcpu, ax_rd) &= ~0x8000000000000000ULL;787vcpu->arch.qpr[ax_rd] = vcpu->arch.qpr[ax_rb];788vcpu->arch.qpr[ax_rd] &= ~0x80000000;789break;790case OP_4X_PS_MERGE00:791WARN_ON(rcomp);792VCPU_FPR(vcpu, ax_rd) = VCPU_FPR(vcpu, ax_ra);793/* vcpu->arch.qpr[ax_rd] = VCPU_FPR(vcpu, ax_rb); */794kvm_cvt_df(&VCPU_FPR(vcpu, ax_rb),795&vcpu->arch.qpr[ax_rd]);796break;797case OP_4X_PS_MERGE01:798WARN_ON(rcomp);799VCPU_FPR(vcpu, ax_rd) = VCPU_FPR(vcpu, ax_ra);800vcpu->arch.qpr[ax_rd] = vcpu->arch.qpr[ax_rb];801break;802case OP_4X_PS_MERGE10:803WARN_ON(rcomp);804/* VCPU_FPR(vcpu, ax_rd) = vcpu->arch.qpr[ax_ra]; */805kvm_cvt_fd(&vcpu->arch.qpr[ax_ra],806&VCPU_FPR(vcpu, ax_rd));807/* vcpu->arch.qpr[ax_rd] = VCPU_FPR(vcpu, ax_rb); */808kvm_cvt_df(&VCPU_FPR(vcpu, ax_rb),809&vcpu->arch.qpr[ax_rd]);810break;811case OP_4X_PS_MERGE11:812WARN_ON(rcomp);813/* VCPU_FPR(vcpu, ax_rd) = vcpu->arch.qpr[ax_ra]; */814kvm_cvt_fd(&vcpu->arch.qpr[ax_ra],815&VCPU_FPR(vcpu, ax_rd));816vcpu->arch.qpr[ax_rd] = vcpu->arch.qpr[ax_rb];817break;818}819/* XW form */820switch (inst_get_field(inst, 25, 30)) {821case OP_4XW_PSQ_STX:822{823ulong addr = ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0;824bool w = inst_get_field(inst, 21, 21) ? true : false;825int i = inst_get_field(inst, 22, 24);826827addr += kvmppc_get_gpr(vcpu, ax_rb);828emulated = kvmppc_emulate_psq_store(vcpu, ax_rd, addr, w, i);829break;830}831case OP_4XW_PSQ_STUX:832{833ulong addr = kvmppc_get_gpr(vcpu, ax_ra);834bool w = inst_get_field(inst, 21, 21) ? true : false;835int i = inst_get_field(inst, 22, 24);836837addr += kvmppc_get_gpr(vcpu, ax_rb);838emulated = kvmppc_emulate_psq_store(vcpu, ax_rd, addr, w, i);839840if (emulated == EMULATE_DONE)841kvmppc_set_gpr(vcpu, ax_ra, addr);842break;843}844}845/* A form */846switch (inst_get_field(inst, 26, 30)) {847case OP_4A_PS_SUM1:848emulated = kvmppc_ps_two_in(vcpu, rcomp, ax_rd,849ax_rb, ax_ra, SCALAR_NO_PS0 | SCALAR_HIGH, fps_fadds);850VCPU_FPR(vcpu, ax_rd) = VCPU_FPR(vcpu, ax_rc);851break;852case OP_4A_PS_SUM0:853emulated = kvmppc_ps_two_in(vcpu, rcomp, ax_rd,854ax_ra, ax_rb, SCALAR_NO_PS1 | SCALAR_LOW, fps_fadds);855vcpu->arch.qpr[ax_rd] = vcpu->arch.qpr[ax_rc];856break;857case OP_4A_PS_MULS0:858emulated = kvmppc_ps_two_in(vcpu, rcomp, ax_rd,859ax_ra, ax_rc, SCALAR_HIGH, fps_fmuls);860break;861case OP_4A_PS_MULS1:862emulated = kvmppc_ps_two_in(vcpu, rcomp, ax_rd,863ax_ra, ax_rc, SCALAR_LOW, fps_fmuls);864break;865case OP_4A_PS_MADDS0:866emulated = kvmppc_ps_three_in(vcpu, rcomp, ax_rd,867ax_ra, ax_rc, ax_rb, SCALAR_HIGH, fps_fmadds);868break;869case OP_4A_PS_MADDS1:870emulated = kvmppc_ps_three_in(vcpu, rcomp, ax_rd,871ax_ra, ax_rc, ax_rb, SCALAR_LOW, fps_fmadds);872break;873case OP_4A_PS_DIV:874emulated = kvmppc_ps_two_in(vcpu, rcomp, ax_rd,875ax_ra, ax_rb, SCALAR_NONE, fps_fdivs);876break;877case OP_4A_PS_SUB:878emulated = kvmppc_ps_two_in(vcpu, rcomp, ax_rd,879ax_ra, ax_rb, SCALAR_NONE, fps_fsubs);880break;881case OP_4A_PS_ADD:882emulated = kvmppc_ps_two_in(vcpu, rcomp, ax_rd,883ax_ra, ax_rb, SCALAR_NONE, fps_fadds);884break;885case OP_4A_PS_SEL:886emulated = kvmppc_ps_three_in(vcpu, rcomp, ax_rd,887ax_ra, ax_rc, ax_rb, SCALAR_NONE, fps_fsel);888break;889case OP_4A_PS_RES:890emulated = kvmppc_ps_one_in(vcpu, rcomp, ax_rd,891ax_rb, fps_fres);892break;893case OP_4A_PS_MUL:894emulated = kvmppc_ps_two_in(vcpu, rcomp, ax_rd,895ax_ra, ax_rc, SCALAR_NONE, fps_fmuls);896break;897case OP_4A_PS_RSQRTE:898emulated = kvmppc_ps_one_in(vcpu, rcomp, ax_rd,899ax_rb, fps_frsqrte);900break;901case OP_4A_PS_MSUB:902emulated = kvmppc_ps_three_in(vcpu, rcomp, ax_rd,903ax_ra, ax_rc, ax_rb, SCALAR_NONE, fps_fmsubs);904break;905case OP_4A_PS_MADD:906emulated = kvmppc_ps_three_in(vcpu, rcomp, ax_rd,907ax_ra, ax_rc, ax_rb, SCALAR_NONE, fps_fmadds);908break;909case OP_4A_PS_NMSUB:910emulated = kvmppc_ps_three_in(vcpu, rcomp, ax_rd,911ax_ra, ax_rc, ax_rb, SCALAR_NONE, fps_fnmsubs);912break;913case OP_4A_PS_NMADD:914emulated = kvmppc_ps_three_in(vcpu, rcomp, ax_rd,915ax_ra, ax_rc, ax_rb, SCALAR_NONE, fps_fnmadds);916break;917}918break;919920/* Real FPU operations */921922case OP_LFS:923{924ulong addr = (ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0) + full_d;925926emulated = kvmppc_emulate_fpr_load(vcpu, ax_rd, addr,927FPU_LS_SINGLE);928break;929}930case OP_LFSU:931{932ulong addr = kvmppc_get_gpr(vcpu, ax_ra) + full_d;933934emulated = kvmppc_emulate_fpr_load(vcpu, ax_rd, addr,935FPU_LS_SINGLE);936937if (emulated == EMULATE_DONE)938kvmppc_set_gpr(vcpu, ax_ra, addr);939break;940}941case OP_LFD:942{943ulong addr = (ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0) + full_d;944945emulated = kvmppc_emulate_fpr_load(vcpu, ax_rd, addr,946FPU_LS_DOUBLE);947break;948}949case OP_LFDU:950{951ulong addr = kvmppc_get_gpr(vcpu, ax_ra) + full_d;952953emulated = kvmppc_emulate_fpr_load(vcpu, ax_rd, addr,954FPU_LS_DOUBLE);955956if (emulated == EMULATE_DONE)957kvmppc_set_gpr(vcpu, ax_ra, addr);958break;959}960case OP_STFS:961{962ulong addr = (ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0) + full_d;963964emulated = kvmppc_emulate_fpr_store(vcpu, ax_rd, addr,965FPU_LS_SINGLE);966break;967}968case OP_STFSU:969{970ulong addr = kvmppc_get_gpr(vcpu, ax_ra) + full_d;971972emulated = kvmppc_emulate_fpr_store(vcpu, ax_rd, addr,973FPU_LS_SINGLE);974975if (emulated == EMULATE_DONE)976kvmppc_set_gpr(vcpu, ax_ra, addr);977break;978}979case OP_STFD:980{981ulong addr = (ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0) + full_d;982983emulated = kvmppc_emulate_fpr_store(vcpu, ax_rd, addr,984FPU_LS_DOUBLE);985break;986}987case OP_STFDU:988{989ulong addr = kvmppc_get_gpr(vcpu, ax_ra) + full_d;990991emulated = kvmppc_emulate_fpr_store(vcpu, ax_rd, addr,992FPU_LS_DOUBLE);993994if (emulated == EMULATE_DONE)995kvmppc_set_gpr(vcpu, ax_ra, addr);996break;997}998case 31:999switch (inst_get_field(inst, 21, 30)) {1000case OP_31_LFSX:1001{1002ulong addr = ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0;10031004addr += kvmppc_get_gpr(vcpu, ax_rb);1005emulated = kvmppc_emulate_fpr_load(vcpu, ax_rd,1006addr, FPU_LS_SINGLE);1007break;1008}1009case OP_31_LFSUX:1010{1011ulong addr = kvmppc_get_gpr(vcpu, ax_ra) +1012kvmppc_get_gpr(vcpu, ax_rb);10131014emulated = kvmppc_emulate_fpr_load(vcpu, ax_rd,1015addr, FPU_LS_SINGLE);10161017if (emulated == EMULATE_DONE)1018kvmppc_set_gpr(vcpu, ax_ra, addr);1019break;1020}1021case OP_31_LFDX:1022{1023ulong addr = (ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0) +1024kvmppc_get_gpr(vcpu, ax_rb);10251026emulated = kvmppc_emulate_fpr_load(vcpu, ax_rd,1027addr, FPU_LS_DOUBLE);1028break;1029}1030case OP_31_LFDUX:1031{1032ulong addr = kvmppc_get_gpr(vcpu, ax_ra) +1033kvmppc_get_gpr(vcpu, ax_rb);10341035emulated = kvmppc_emulate_fpr_load(vcpu, ax_rd,1036addr, FPU_LS_DOUBLE);10371038if (emulated == EMULATE_DONE)1039kvmppc_set_gpr(vcpu, ax_ra, addr);1040break;1041}1042case OP_31_STFSX:1043{1044ulong addr = (ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0) +1045kvmppc_get_gpr(vcpu, ax_rb);10461047emulated = kvmppc_emulate_fpr_store(vcpu, ax_rd,1048addr, FPU_LS_SINGLE);1049break;1050}1051case OP_31_STFSUX:1052{1053ulong addr = kvmppc_get_gpr(vcpu, ax_ra) +1054kvmppc_get_gpr(vcpu, ax_rb);10551056emulated = kvmppc_emulate_fpr_store(vcpu, ax_rd,1057addr, FPU_LS_SINGLE);10581059if (emulated == EMULATE_DONE)1060kvmppc_set_gpr(vcpu, ax_ra, addr);1061break;1062}1063case OP_31_STFX:1064{1065ulong addr = (ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0) +1066kvmppc_get_gpr(vcpu, ax_rb);10671068emulated = kvmppc_emulate_fpr_store(vcpu, ax_rd,1069addr, FPU_LS_DOUBLE);1070break;1071}1072case OP_31_STFUX:1073{1074ulong addr = kvmppc_get_gpr(vcpu, ax_ra) +1075kvmppc_get_gpr(vcpu, ax_rb);10761077emulated = kvmppc_emulate_fpr_store(vcpu, ax_rd,1078addr, FPU_LS_DOUBLE);10791080if (emulated == EMULATE_DONE)1081kvmppc_set_gpr(vcpu, ax_ra, addr);1082break;1083}1084case OP_31_STFIWX:1085{1086ulong addr = (ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0) +1087kvmppc_get_gpr(vcpu, ax_rb);10881089emulated = kvmppc_emulate_fpr_store(vcpu, ax_rd,1090addr,1091FPU_LS_SINGLE_LOW);1092break;1093}1094break;1095}1096break;1097case 59:1098switch (inst_get_field(inst, 21, 30)) {1099case OP_59_FADDS:1100fpd_fadds(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_b);1101kvmppc_sync_qpr(vcpu, ax_rd);1102break;1103case OP_59_FSUBS:1104fpd_fsubs(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_b);1105kvmppc_sync_qpr(vcpu, ax_rd);1106break;1107case OP_59_FDIVS:1108fpd_fdivs(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_b);1109kvmppc_sync_qpr(vcpu, ax_rd);1110break;1111case OP_59_FRES:1112fpd_fres(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_b);1113kvmppc_sync_qpr(vcpu, ax_rd);1114break;1115case OP_59_FRSQRTES:1116fpd_frsqrtes(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_b);1117kvmppc_sync_qpr(vcpu, ax_rd);1118break;1119}1120switch (inst_get_field(inst, 26, 30)) {1121case OP_59_FMULS:1122fpd_fmuls(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_c);1123kvmppc_sync_qpr(vcpu, ax_rd);1124break;1125case OP_59_FMSUBS:1126fpd_fmsubs(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b);1127kvmppc_sync_qpr(vcpu, ax_rd);1128break;1129case OP_59_FMADDS:1130fpd_fmadds(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b);1131kvmppc_sync_qpr(vcpu, ax_rd);1132break;1133case OP_59_FNMSUBS:1134fpd_fnmsubs(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b);1135kvmppc_sync_qpr(vcpu, ax_rd);1136break;1137case OP_59_FNMADDS:1138fpd_fnmadds(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b);1139kvmppc_sync_qpr(vcpu, ax_rd);1140break;1141}1142break;1143case 63:1144switch (inst_get_field(inst, 21, 30)) {1145case OP_63_MTFSB0:1146case OP_63_MTFSB1:1147case OP_63_MCRFS:1148case OP_63_MTFSFI:1149/* XXX need to implement */1150break;1151case OP_63_MFFS:1152/* XXX missing CR */1153*fpr_d = vcpu->arch.fp.fpscr;1154break;1155case OP_63_MTFSF:1156/* XXX missing fm bits */1157/* XXX missing CR */1158vcpu->arch.fp.fpscr = *fpr_b;1159break;1160case OP_63_FCMPU:1161{1162u32 tmp_cr;1163u32 cr0_mask = 0xf0000000;1164u32 cr_shift = inst_get_field(inst, 6, 8) * 4;11651166fpd_fcmpu(&vcpu->arch.fp.fpscr, &tmp_cr, fpr_a, fpr_b);1167cr &= ~(cr0_mask >> cr_shift);1168cr |= (cr & cr0_mask) >> cr_shift;1169break;1170}1171case OP_63_FCMPO:1172{1173u32 tmp_cr;1174u32 cr0_mask = 0xf0000000;1175u32 cr_shift = inst_get_field(inst, 6, 8) * 4;11761177fpd_fcmpo(&vcpu->arch.fp.fpscr, &tmp_cr, fpr_a, fpr_b);1178cr &= ~(cr0_mask >> cr_shift);1179cr |= (cr & cr0_mask) >> cr_shift;1180break;1181}1182case OP_63_FNEG:1183fpd_fneg(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_b);1184break;1185case OP_63_FMR:1186*fpr_d = *fpr_b;1187break;1188case OP_63_FABS:1189fpd_fabs(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_b);1190break;1191case OP_63_FCPSGN:1192fpd_fcpsgn(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_b);1193break;1194case OP_63_FDIV:1195fpd_fdiv(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_b);1196break;1197case OP_63_FADD:1198fpd_fadd(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_b);1199break;1200case OP_63_FSUB:1201fpd_fsub(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_b);1202break;1203case OP_63_FCTIW:1204fpd_fctiw(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_b);1205break;1206case OP_63_FCTIWZ:1207fpd_fctiwz(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_b);1208break;1209case OP_63_FRSP:1210fpd_frsp(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_b);1211kvmppc_sync_qpr(vcpu, ax_rd);1212break;1213case OP_63_FRSQRTE:1214{1215double one = 1.0f;12161217/* fD = sqrt(fB) */1218fpd_fsqrt(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_b);1219/* fD = 1.0f / fD */1220fpd_fdiv(&vcpu->arch.fp.fpscr, &cr, fpr_d, (u64*)&one, fpr_d);1221break;1222}1223}1224switch (inst_get_field(inst, 26, 30)) {1225case OP_63_FMUL:1226fpd_fmul(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_c);1227break;1228case OP_63_FSEL:1229fpd_fsel(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b);1230break;1231case OP_63_FMSUB:1232fpd_fmsub(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b);1233break;1234case OP_63_FMADD:1235fpd_fmadd(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b);1236break;1237case OP_63_FNMSUB:1238fpd_fnmsub(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b);1239break;1240case OP_63_FNMADD:1241fpd_fnmadd(&vcpu->arch.fp.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b);1242break;1243}1244break;1245}12461247#ifdef DEBUG1248for (i = 0; i < ARRAY_SIZE(vcpu->arch.fp.fpr); i++) {1249u32 f;1250kvm_cvt_df(&VCPU_FPR(vcpu, i), &f);1251dprintk(KERN_INFO "FPR[%d] = 0x%x\n", i, f);1252}1253#endif12541255if (rcomp)1256kvmppc_set_cr(vcpu, cr);12571258disable_kernel_fp();1259preempt_enable();12601261return emulated;1262}126312641265