Path: blob/master/arch/powerpc/kvm/book3s_paired_singles.c
10817 views
/*1* This program is free software; you can redistribute it and/or modify2* it under the terms of the GNU General Public License, version 2, as3* published by the Free Software Foundation.4*5* This program is distributed in the hope that it will be useful,6* but WITHOUT ANY WARRANTY; without even the implied warranty of7* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the8* GNU General Public License for more details.9*10* You should have received a copy of the GNU General Public License11* along with this program; if not, write to the Free Software12* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.13*14* Copyright Novell Inc 201015*16* Authors: Alexander Graf <[email protected]>17*/1819#include <asm/kvm.h>20#include <asm/kvm_ppc.h>21#include <asm/disassemble.h>22#include <asm/kvm_book3s.h>23#include <asm/kvm_fpu.h>24#include <asm/reg.h>25#include <asm/cacheflush.h>26#include <linux/vmalloc.h>2728/* #define DEBUG */2930#ifdef DEBUG31#define dprintk printk32#else33#define dprintk(...) do { } while(0);34#endif3536#define OP_LFS 4837#define OP_LFSU 4938#define OP_LFD 5039#define OP_LFDU 5140#define OP_STFS 5241#define OP_STFSU 5342#define OP_STFD 5443#define OP_STFDU 5544#define OP_PSQ_L 5645#define OP_PSQ_LU 5746#define OP_PSQ_ST 6047#define OP_PSQ_STU 614849#define OP_31_LFSX 53550#define OP_31_LFSUX 56751#define OP_31_LFDX 59952#define OP_31_LFDUX 63153#define OP_31_STFSX 66354#define OP_31_STFSUX 69555#define OP_31_STFX 72756#define OP_31_STFUX 75957#define OP_31_LWIZX 88758#define OP_31_STFIWX 9835960#define OP_59_FADDS 2161#define OP_59_FSUBS 2062#define OP_59_FSQRTS 2263#define OP_59_FDIVS 1864#define OP_59_FRES 2465#define OP_59_FMULS 2566#define OP_59_FRSQRTES 2667#define OP_59_FMSUBS 2868#define OP_59_FMADDS 2969#define OP_59_FNMSUBS 3070#define OP_59_FNMADDS 317172#define OP_63_FCMPU 073#define OP_63_FCPSGN 874#define OP_63_FRSP 1275#define OP_63_FCTIW 1476#define OP_63_FCTIWZ 1577#define OP_63_FDIV 1878#define OP_63_FADD 2179#define OP_63_FSQRT 2280#define OP_63_FSEL 2381#define OP_63_FRE 2482#define OP_63_FMUL 2583#define OP_63_FRSQRTE 2684#define OP_63_FMSUB 2885#define OP_63_FMADD 2986#define OP_63_FNMSUB 3087#define OP_63_FNMADD 3188#define OP_63_FCMPO 3289#define OP_63_MTFSB1 38 // XXX90#define OP_63_FSUB 2091#define OP_63_FNEG 4092#define OP_63_MCRFS 6493#define OP_63_MTFSB0 7094#define OP_63_FMR 7295#define OP_63_MTFSFI 13496#define OP_63_FABS 26497#define OP_63_MFFS 58398#define OP_63_MTFSF 71199100#define OP_4X_PS_CMPU0 0101#define OP_4X_PSQ_LX 6102#define OP_4XW_PSQ_STX 7103#define OP_4A_PS_SUM0 10104#define OP_4A_PS_SUM1 11105#define OP_4A_PS_MULS0 12106#define OP_4A_PS_MULS1 13107#define OP_4A_PS_MADDS0 14108#define OP_4A_PS_MADDS1 15109#define OP_4A_PS_DIV 18110#define OP_4A_PS_SUB 20111#define OP_4A_PS_ADD 21112#define OP_4A_PS_SEL 23113#define OP_4A_PS_RES 24114#define OP_4A_PS_MUL 25115#define OP_4A_PS_RSQRTE 26116#define OP_4A_PS_MSUB 28117#define OP_4A_PS_MADD 29118#define OP_4A_PS_NMSUB 30119#define OP_4A_PS_NMADD 31120#define OP_4X_PS_CMPO0 32121#define OP_4X_PSQ_LUX 38122#define OP_4XW_PSQ_STUX 39123#define OP_4X_PS_NEG 40124#define OP_4X_PS_CMPU1 64125#define OP_4X_PS_MR 72126#define OP_4X_PS_CMPO1 96127#define OP_4X_PS_NABS 136128#define OP_4X_PS_ABS 264129#define OP_4X_PS_MERGE00 528130#define OP_4X_PS_MERGE01 560131#define OP_4X_PS_MERGE10 592132#define OP_4X_PS_MERGE11 624133134#define SCALAR_NONE 0135#define SCALAR_HIGH (1 << 0)136#define SCALAR_LOW (1 << 1)137#define SCALAR_NO_PS0 (1 << 2)138#define SCALAR_NO_PS1 (1 << 3)139140#define GQR_ST_TYPE_MASK 0x00000007141#define GQR_ST_TYPE_SHIFT 0142#define GQR_ST_SCALE_MASK 0x00003f00143#define GQR_ST_SCALE_SHIFT 8144#define GQR_LD_TYPE_MASK 0x00070000145#define GQR_LD_TYPE_SHIFT 16146#define GQR_LD_SCALE_MASK 0x3f000000147#define GQR_LD_SCALE_SHIFT 24148149#define GQR_QUANTIZE_FLOAT 0150#define GQR_QUANTIZE_U8 4151#define GQR_QUANTIZE_U16 5152#define GQR_QUANTIZE_S8 6153#define GQR_QUANTIZE_S16 7154155#define FPU_LS_SINGLE 0156#define FPU_LS_DOUBLE 1157#define FPU_LS_SINGLE_LOW 2158159static inline void kvmppc_sync_qpr(struct kvm_vcpu *vcpu, int rt)160{161kvm_cvt_df(&vcpu->arch.fpr[rt], &vcpu->arch.qpr[rt]);162}163164static void kvmppc_inject_pf(struct kvm_vcpu *vcpu, ulong eaddr, bool is_store)165{166u64 dsisr;167struct kvm_vcpu_arch_shared *shared = vcpu->arch.shared;168169shared->msr = kvmppc_set_field(shared->msr, 33, 36, 0);170shared->msr = kvmppc_set_field(shared->msr, 42, 47, 0);171shared->dar = eaddr;172/* Page Fault */173dsisr = kvmppc_set_field(0, 33, 33, 1);174if (is_store)175shared->dsisr = kvmppc_set_field(dsisr, 38, 38, 1);176kvmppc_book3s_queue_irqprio(vcpu, BOOK3S_INTERRUPT_DATA_STORAGE);177}178179static int kvmppc_emulate_fpr_load(struct kvm_run *run, struct kvm_vcpu *vcpu,180int rs, ulong addr, int ls_type)181{182int emulated = EMULATE_FAIL;183int r;184char tmp[8];185int len = sizeof(u32);186187if (ls_type == FPU_LS_DOUBLE)188len = sizeof(u64);189190/* read from memory */191r = kvmppc_ld(vcpu, &addr, len, tmp, true);192vcpu->arch.paddr_accessed = addr;193194if (r < 0) {195kvmppc_inject_pf(vcpu, addr, false);196goto done_load;197} else if (r == EMULATE_DO_MMIO) {198emulated = kvmppc_handle_load(run, vcpu, KVM_REG_FPR | rs, len, 1);199goto done_load;200}201202emulated = EMULATE_DONE;203204/* put in registers */205switch (ls_type) {206case FPU_LS_SINGLE:207kvm_cvt_fd((u32*)tmp, &vcpu->arch.fpr[rs]);208vcpu->arch.qpr[rs] = *((u32*)tmp);209break;210case FPU_LS_DOUBLE:211vcpu->arch.fpr[rs] = *((u64*)tmp);212break;213}214215dprintk(KERN_INFO "KVM: FPR_LD [0x%llx] at 0x%lx (%d)\n", *(u64*)tmp,216addr, len);217218done_load:219return emulated;220}221222static int kvmppc_emulate_fpr_store(struct kvm_run *run, struct kvm_vcpu *vcpu,223int rs, ulong addr, int ls_type)224{225int emulated = EMULATE_FAIL;226int r;227char tmp[8];228u64 val;229int len;230231switch (ls_type) {232case FPU_LS_SINGLE:233kvm_cvt_df(&vcpu->arch.fpr[rs], (u32*)tmp);234val = *((u32*)tmp);235len = sizeof(u32);236break;237case FPU_LS_SINGLE_LOW:238*((u32*)tmp) = vcpu->arch.fpr[rs];239val = vcpu->arch.fpr[rs] & 0xffffffff;240len = sizeof(u32);241break;242case FPU_LS_DOUBLE:243*((u64*)tmp) = vcpu->arch.fpr[rs];244val = vcpu->arch.fpr[rs];245len = sizeof(u64);246break;247default:248val = 0;249len = 0;250}251252r = kvmppc_st(vcpu, &addr, len, tmp, true);253vcpu->arch.paddr_accessed = addr;254if (r < 0) {255kvmppc_inject_pf(vcpu, addr, true);256} else if (r == EMULATE_DO_MMIO) {257emulated = kvmppc_handle_store(run, vcpu, val, len, 1);258} else {259emulated = EMULATE_DONE;260}261262dprintk(KERN_INFO "KVM: FPR_ST [0x%llx] at 0x%lx (%d)\n",263val, addr, len);264265return emulated;266}267268static int kvmppc_emulate_psq_load(struct kvm_run *run, struct kvm_vcpu *vcpu,269int rs, ulong addr, bool w, int i)270{271int emulated = EMULATE_FAIL;272int r;273float one = 1.0;274u32 tmp[2];275276/* read from memory */277if (w) {278r = kvmppc_ld(vcpu, &addr, sizeof(u32), tmp, true);279memcpy(&tmp[1], &one, sizeof(u32));280} else {281r = kvmppc_ld(vcpu, &addr, sizeof(u32) * 2, tmp, true);282}283vcpu->arch.paddr_accessed = addr;284if (r < 0) {285kvmppc_inject_pf(vcpu, addr, false);286goto done_load;287} else if ((r == EMULATE_DO_MMIO) && w) {288emulated = kvmppc_handle_load(run, vcpu, KVM_REG_FPR | rs, 4, 1);289vcpu->arch.qpr[rs] = tmp[1];290goto done_load;291} else if (r == EMULATE_DO_MMIO) {292emulated = kvmppc_handle_load(run, vcpu, KVM_REG_FQPR | rs, 8, 1);293goto done_load;294}295296emulated = EMULATE_DONE;297298/* put in registers */299kvm_cvt_fd(&tmp[0], &vcpu->arch.fpr[rs]);300vcpu->arch.qpr[rs] = tmp[1];301302dprintk(KERN_INFO "KVM: PSQ_LD [0x%x, 0x%x] at 0x%lx (%d)\n", tmp[0],303tmp[1], addr, w ? 4 : 8);304305done_load:306return emulated;307}308309static int kvmppc_emulate_psq_store(struct kvm_run *run, struct kvm_vcpu *vcpu,310int rs, ulong addr, bool w, int i)311{312int emulated = EMULATE_FAIL;313int r;314u32 tmp[2];315int len = w ? sizeof(u32) : sizeof(u64);316317kvm_cvt_df(&vcpu->arch.fpr[rs], &tmp[0]);318tmp[1] = vcpu->arch.qpr[rs];319320r = kvmppc_st(vcpu, &addr, len, tmp, true);321vcpu->arch.paddr_accessed = addr;322if (r < 0) {323kvmppc_inject_pf(vcpu, addr, true);324} else if ((r == EMULATE_DO_MMIO) && w) {325emulated = kvmppc_handle_store(run, vcpu, tmp[0], 4, 1);326} else if (r == EMULATE_DO_MMIO) {327u64 val = ((u64)tmp[0] << 32) | tmp[1];328emulated = kvmppc_handle_store(run, vcpu, val, 8, 1);329} else {330emulated = EMULATE_DONE;331}332333dprintk(KERN_INFO "KVM: PSQ_ST [0x%x, 0x%x] at 0x%lx (%d)\n",334tmp[0], tmp[1], addr, len);335336return emulated;337}338339/*340* Cuts out inst bits with ordering according to spec.341* That means the leftmost bit is zero. All given bits are included.342*/343static inline u32 inst_get_field(u32 inst, int msb, int lsb)344{345return kvmppc_get_field(inst, msb + 32, lsb + 32);346}347348/*349* Replaces inst bits with ordering according to spec.350*/351static inline u32 inst_set_field(u32 inst, int msb, int lsb, int value)352{353return kvmppc_set_field(inst, msb + 32, lsb + 32, value);354}355356bool kvmppc_inst_is_paired_single(struct kvm_vcpu *vcpu, u32 inst)357{358if (!(vcpu->arch.hflags & BOOK3S_HFLAG_PAIRED_SINGLE))359return false;360361switch (get_op(inst)) {362case OP_PSQ_L:363case OP_PSQ_LU:364case OP_PSQ_ST:365case OP_PSQ_STU:366case OP_LFS:367case OP_LFSU:368case OP_LFD:369case OP_LFDU:370case OP_STFS:371case OP_STFSU:372case OP_STFD:373case OP_STFDU:374return true;375case 4:376/* X form */377switch (inst_get_field(inst, 21, 30)) {378case OP_4X_PS_CMPU0:379case OP_4X_PSQ_LX:380case OP_4X_PS_CMPO0:381case OP_4X_PSQ_LUX:382case OP_4X_PS_NEG:383case OP_4X_PS_CMPU1:384case OP_4X_PS_MR:385case OP_4X_PS_CMPO1:386case OP_4X_PS_NABS:387case OP_4X_PS_ABS:388case OP_4X_PS_MERGE00:389case OP_4X_PS_MERGE01:390case OP_4X_PS_MERGE10:391case OP_4X_PS_MERGE11:392return true;393}394/* XW form */395switch (inst_get_field(inst, 25, 30)) {396case OP_4XW_PSQ_STX:397case OP_4XW_PSQ_STUX:398return true;399}400/* A form */401switch (inst_get_field(inst, 26, 30)) {402case OP_4A_PS_SUM1:403case OP_4A_PS_SUM0:404case OP_4A_PS_MULS0:405case OP_4A_PS_MULS1:406case OP_4A_PS_MADDS0:407case OP_4A_PS_MADDS1:408case OP_4A_PS_DIV:409case OP_4A_PS_SUB:410case OP_4A_PS_ADD:411case OP_4A_PS_SEL:412case OP_4A_PS_RES:413case OP_4A_PS_MUL:414case OP_4A_PS_RSQRTE:415case OP_4A_PS_MSUB:416case OP_4A_PS_MADD:417case OP_4A_PS_NMSUB:418case OP_4A_PS_NMADD:419return true;420}421break;422case 59:423switch (inst_get_field(inst, 21, 30)) {424case OP_59_FADDS:425case OP_59_FSUBS:426case OP_59_FDIVS:427case OP_59_FRES:428case OP_59_FRSQRTES:429return true;430}431switch (inst_get_field(inst, 26, 30)) {432case OP_59_FMULS:433case OP_59_FMSUBS:434case OP_59_FMADDS:435case OP_59_FNMSUBS:436case OP_59_FNMADDS:437return true;438}439break;440case 63:441switch (inst_get_field(inst, 21, 30)) {442case OP_63_MTFSB0:443case OP_63_MTFSB1:444case OP_63_MTFSF:445case OP_63_MTFSFI:446case OP_63_MCRFS:447case OP_63_MFFS:448case OP_63_FCMPU:449case OP_63_FCMPO:450case OP_63_FNEG:451case OP_63_FMR:452case OP_63_FABS:453case OP_63_FRSP:454case OP_63_FDIV:455case OP_63_FADD:456case OP_63_FSUB:457case OP_63_FCTIW:458case OP_63_FCTIWZ:459case OP_63_FRSQRTE:460case OP_63_FCPSGN:461return true;462}463switch (inst_get_field(inst, 26, 30)) {464case OP_63_FMUL:465case OP_63_FSEL:466case OP_63_FMSUB:467case OP_63_FMADD:468case OP_63_FNMSUB:469case OP_63_FNMADD:470return true;471}472break;473case 31:474switch (inst_get_field(inst, 21, 30)) {475case OP_31_LFSX:476case OP_31_LFSUX:477case OP_31_LFDX:478case OP_31_LFDUX:479case OP_31_STFSX:480case OP_31_STFSUX:481case OP_31_STFX:482case OP_31_STFUX:483case OP_31_STFIWX:484return true;485}486break;487}488489return false;490}491492static int get_d_signext(u32 inst)493{494int d = inst & 0x8ff;495496if (d & 0x800)497return -(d & 0x7ff);498499return (d & 0x7ff);500}501502static int kvmppc_ps_three_in(struct kvm_vcpu *vcpu, bool rc,503int reg_out, int reg_in1, int reg_in2,504int reg_in3, int scalar,505void (*func)(u64 *fpscr,506u32 *dst, u32 *src1,507u32 *src2, u32 *src3))508{509u32 *qpr = vcpu->arch.qpr;510u64 *fpr = vcpu->arch.fpr;511u32 ps0_out;512u32 ps0_in1, ps0_in2, ps0_in3;513u32 ps1_in1, ps1_in2, ps1_in3;514515/* RC */516WARN_ON(rc);517518/* PS0 */519kvm_cvt_df(&fpr[reg_in1], &ps0_in1);520kvm_cvt_df(&fpr[reg_in2], &ps0_in2);521kvm_cvt_df(&fpr[reg_in3], &ps0_in3);522523if (scalar & SCALAR_LOW)524ps0_in2 = qpr[reg_in2];525526func(&vcpu->arch.fpscr, &ps0_out, &ps0_in1, &ps0_in2, &ps0_in3);527528dprintk(KERN_INFO "PS3 ps0 -> f(0x%x, 0x%x, 0x%x) = 0x%x\n",529ps0_in1, ps0_in2, ps0_in3, ps0_out);530531if (!(scalar & SCALAR_NO_PS0))532kvm_cvt_fd(&ps0_out, &fpr[reg_out]);533534/* PS1 */535ps1_in1 = qpr[reg_in1];536ps1_in2 = qpr[reg_in2];537ps1_in3 = qpr[reg_in3];538539if (scalar & SCALAR_HIGH)540ps1_in2 = ps0_in2;541542if (!(scalar & SCALAR_NO_PS1))543func(&vcpu->arch.fpscr, &qpr[reg_out], &ps1_in1, &ps1_in2, &ps1_in3);544545dprintk(KERN_INFO "PS3 ps1 -> f(0x%x, 0x%x, 0x%x) = 0x%x\n",546ps1_in1, ps1_in2, ps1_in3, qpr[reg_out]);547548return EMULATE_DONE;549}550551static int kvmppc_ps_two_in(struct kvm_vcpu *vcpu, bool rc,552int reg_out, int reg_in1, int reg_in2,553int scalar,554void (*func)(u64 *fpscr,555u32 *dst, u32 *src1,556u32 *src2))557{558u32 *qpr = vcpu->arch.qpr;559u64 *fpr = vcpu->arch.fpr;560u32 ps0_out;561u32 ps0_in1, ps0_in2;562u32 ps1_out;563u32 ps1_in1, ps1_in2;564565/* RC */566WARN_ON(rc);567568/* PS0 */569kvm_cvt_df(&fpr[reg_in1], &ps0_in1);570571if (scalar & SCALAR_LOW)572ps0_in2 = qpr[reg_in2];573else574kvm_cvt_df(&fpr[reg_in2], &ps0_in2);575576func(&vcpu->arch.fpscr, &ps0_out, &ps0_in1, &ps0_in2);577578if (!(scalar & SCALAR_NO_PS0)) {579dprintk(KERN_INFO "PS2 ps0 -> f(0x%x, 0x%x) = 0x%x\n",580ps0_in1, ps0_in2, ps0_out);581582kvm_cvt_fd(&ps0_out, &fpr[reg_out]);583}584585/* PS1 */586ps1_in1 = qpr[reg_in1];587ps1_in2 = qpr[reg_in2];588589if (scalar & SCALAR_HIGH)590ps1_in2 = ps0_in2;591592func(&vcpu->arch.fpscr, &ps1_out, &ps1_in1, &ps1_in2);593594if (!(scalar & SCALAR_NO_PS1)) {595qpr[reg_out] = ps1_out;596597dprintk(KERN_INFO "PS2 ps1 -> f(0x%x, 0x%x) = 0x%x\n",598ps1_in1, ps1_in2, qpr[reg_out]);599}600601return EMULATE_DONE;602}603604static int kvmppc_ps_one_in(struct kvm_vcpu *vcpu, bool rc,605int reg_out, int reg_in,606void (*func)(u64 *t,607u32 *dst, u32 *src1))608{609u32 *qpr = vcpu->arch.qpr;610u64 *fpr = vcpu->arch.fpr;611u32 ps0_out, ps0_in;612u32 ps1_in;613614/* RC */615WARN_ON(rc);616617/* PS0 */618kvm_cvt_df(&fpr[reg_in], &ps0_in);619func(&vcpu->arch.fpscr, &ps0_out, &ps0_in);620621dprintk(KERN_INFO "PS1 ps0 -> f(0x%x) = 0x%x\n",622ps0_in, ps0_out);623624kvm_cvt_fd(&ps0_out, &fpr[reg_out]);625626/* PS1 */627ps1_in = qpr[reg_in];628func(&vcpu->arch.fpscr, &qpr[reg_out], &ps1_in);629630dprintk(KERN_INFO "PS1 ps1 -> f(0x%x) = 0x%x\n",631ps1_in, qpr[reg_out]);632633return EMULATE_DONE;634}635636int kvmppc_emulate_paired_single(struct kvm_run *run, struct kvm_vcpu *vcpu)637{638u32 inst = kvmppc_get_last_inst(vcpu);639enum emulation_result emulated = EMULATE_DONE;640641int ax_rd = inst_get_field(inst, 6, 10);642int ax_ra = inst_get_field(inst, 11, 15);643int ax_rb = inst_get_field(inst, 16, 20);644int ax_rc = inst_get_field(inst, 21, 25);645short full_d = inst_get_field(inst, 16, 31);646647u64 *fpr_d = &vcpu->arch.fpr[ax_rd];648u64 *fpr_a = &vcpu->arch.fpr[ax_ra];649u64 *fpr_b = &vcpu->arch.fpr[ax_rb];650u64 *fpr_c = &vcpu->arch.fpr[ax_rc];651652bool rcomp = (inst & 1) ? true : false;653u32 cr = kvmppc_get_cr(vcpu);654#ifdef DEBUG655int i;656#endif657658if (!kvmppc_inst_is_paired_single(vcpu, inst))659return EMULATE_FAIL;660661if (!(vcpu->arch.shared->msr & MSR_FP)) {662kvmppc_book3s_queue_irqprio(vcpu, BOOK3S_INTERRUPT_FP_UNAVAIL);663return EMULATE_AGAIN;664}665666kvmppc_giveup_ext(vcpu, MSR_FP);667preempt_disable();668enable_kernel_fp();669/* Do we need to clear FE0 / FE1 here? Don't think so. */670671#ifdef DEBUG672for (i = 0; i < ARRAY_SIZE(vcpu->arch.fpr); i++) {673u32 f;674kvm_cvt_df(&vcpu->arch.fpr[i], &f);675dprintk(KERN_INFO "FPR[%d] = 0x%x / 0x%llx QPR[%d] = 0x%x\n",676i, f, vcpu->arch.fpr[i], i, vcpu->arch.qpr[i]);677}678#endif679680switch (get_op(inst)) {681case OP_PSQ_L:682{683ulong addr = ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0;684bool w = inst_get_field(inst, 16, 16) ? true : false;685int i = inst_get_field(inst, 17, 19);686687addr += get_d_signext(inst);688emulated = kvmppc_emulate_psq_load(run, vcpu, ax_rd, addr, w, i);689break;690}691case OP_PSQ_LU:692{693ulong addr = kvmppc_get_gpr(vcpu, ax_ra);694bool w = inst_get_field(inst, 16, 16) ? true : false;695int i = inst_get_field(inst, 17, 19);696697addr += get_d_signext(inst);698emulated = kvmppc_emulate_psq_load(run, vcpu, ax_rd, addr, w, i);699700if (emulated == EMULATE_DONE)701kvmppc_set_gpr(vcpu, ax_ra, addr);702break;703}704case OP_PSQ_ST:705{706ulong addr = ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0;707bool w = inst_get_field(inst, 16, 16) ? true : false;708int i = inst_get_field(inst, 17, 19);709710addr += get_d_signext(inst);711emulated = kvmppc_emulate_psq_store(run, vcpu, ax_rd, addr, w, i);712break;713}714case OP_PSQ_STU:715{716ulong addr = kvmppc_get_gpr(vcpu, ax_ra);717bool w = inst_get_field(inst, 16, 16) ? true : false;718int i = inst_get_field(inst, 17, 19);719720addr += get_d_signext(inst);721emulated = kvmppc_emulate_psq_store(run, vcpu, ax_rd, addr, w, i);722723if (emulated == EMULATE_DONE)724kvmppc_set_gpr(vcpu, ax_ra, addr);725break;726}727case 4:728/* X form */729switch (inst_get_field(inst, 21, 30)) {730case OP_4X_PS_CMPU0:731/* XXX */732emulated = EMULATE_FAIL;733break;734case OP_4X_PSQ_LX:735{736ulong addr = ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0;737bool w = inst_get_field(inst, 21, 21) ? true : false;738int i = inst_get_field(inst, 22, 24);739740addr += kvmppc_get_gpr(vcpu, ax_rb);741emulated = kvmppc_emulate_psq_load(run, vcpu, ax_rd, addr, w, i);742break;743}744case OP_4X_PS_CMPO0:745/* XXX */746emulated = EMULATE_FAIL;747break;748case OP_4X_PSQ_LUX:749{750ulong addr = kvmppc_get_gpr(vcpu, ax_ra);751bool w = inst_get_field(inst, 21, 21) ? true : false;752int i = inst_get_field(inst, 22, 24);753754addr += kvmppc_get_gpr(vcpu, ax_rb);755emulated = kvmppc_emulate_psq_load(run, vcpu, ax_rd, addr, w, i);756757if (emulated == EMULATE_DONE)758kvmppc_set_gpr(vcpu, ax_ra, addr);759break;760}761case OP_4X_PS_NEG:762vcpu->arch.fpr[ax_rd] = vcpu->arch.fpr[ax_rb];763vcpu->arch.fpr[ax_rd] ^= 0x8000000000000000ULL;764vcpu->arch.qpr[ax_rd] = vcpu->arch.qpr[ax_rb];765vcpu->arch.qpr[ax_rd] ^= 0x80000000;766break;767case OP_4X_PS_CMPU1:768/* XXX */769emulated = EMULATE_FAIL;770break;771case OP_4X_PS_MR:772WARN_ON(rcomp);773vcpu->arch.fpr[ax_rd] = vcpu->arch.fpr[ax_rb];774vcpu->arch.qpr[ax_rd] = vcpu->arch.qpr[ax_rb];775break;776case OP_4X_PS_CMPO1:777/* XXX */778emulated = EMULATE_FAIL;779break;780case OP_4X_PS_NABS:781WARN_ON(rcomp);782vcpu->arch.fpr[ax_rd] = vcpu->arch.fpr[ax_rb];783vcpu->arch.fpr[ax_rd] |= 0x8000000000000000ULL;784vcpu->arch.qpr[ax_rd] = vcpu->arch.qpr[ax_rb];785vcpu->arch.qpr[ax_rd] |= 0x80000000;786break;787case OP_4X_PS_ABS:788WARN_ON(rcomp);789vcpu->arch.fpr[ax_rd] = vcpu->arch.fpr[ax_rb];790vcpu->arch.fpr[ax_rd] &= ~0x8000000000000000ULL;791vcpu->arch.qpr[ax_rd] = vcpu->arch.qpr[ax_rb];792vcpu->arch.qpr[ax_rd] &= ~0x80000000;793break;794case OP_4X_PS_MERGE00:795WARN_ON(rcomp);796vcpu->arch.fpr[ax_rd] = vcpu->arch.fpr[ax_ra];797/* vcpu->arch.qpr[ax_rd] = vcpu->arch.fpr[ax_rb]; */798kvm_cvt_df(&vcpu->arch.fpr[ax_rb],799&vcpu->arch.qpr[ax_rd]);800break;801case OP_4X_PS_MERGE01:802WARN_ON(rcomp);803vcpu->arch.fpr[ax_rd] = vcpu->arch.fpr[ax_ra];804vcpu->arch.qpr[ax_rd] = vcpu->arch.qpr[ax_rb];805break;806case OP_4X_PS_MERGE10:807WARN_ON(rcomp);808/* vcpu->arch.fpr[ax_rd] = vcpu->arch.qpr[ax_ra]; */809kvm_cvt_fd(&vcpu->arch.qpr[ax_ra],810&vcpu->arch.fpr[ax_rd]);811/* vcpu->arch.qpr[ax_rd] = vcpu->arch.fpr[ax_rb]; */812kvm_cvt_df(&vcpu->arch.fpr[ax_rb],813&vcpu->arch.qpr[ax_rd]);814break;815case OP_4X_PS_MERGE11:816WARN_ON(rcomp);817/* vcpu->arch.fpr[ax_rd] = vcpu->arch.qpr[ax_ra]; */818kvm_cvt_fd(&vcpu->arch.qpr[ax_ra],819&vcpu->arch.fpr[ax_rd]);820vcpu->arch.qpr[ax_rd] = vcpu->arch.qpr[ax_rb];821break;822}823/* XW form */824switch (inst_get_field(inst, 25, 30)) {825case OP_4XW_PSQ_STX:826{827ulong addr = ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0;828bool w = inst_get_field(inst, 21, 21) ? true : false;829int i = inst_get_field(inst, 22, 24);830831addr += kvmppc_get_gpr(vcpu, ax_rb);832emulated = kvmppc_emulate_psq_store(run, vcpu, ax_rd, addr, w, i);833break;834}835case OP_4XW_PSQ_STUX:836{837ulong addr = kvmppc_get_gpr(vcpu, ax_ra);838bool w = inst_get_field(inst, 21, 21) ? true : false;839int i = inst_get_field(inst, 22, 24);840841addr += kvmppc_get_gpr(vcpu, ax_rb);842emulated = kvmppc_emulate_psq_store(run, vcpu, ax_rd, addr, w, i);843844if (emulated == EMULATE_DONE)845kvmppc_set_gpr(vcpu, ax_ra, addr);846break;847}848}849/* A form */850switch (inst_get_field(inst, 26, 30)) {851case OP_4A_PS_SUM1:852emulated = kvmppc_ps_two_in(vcpu, rcomp, ax_rd,853ax_rb, ax_ra, SCALAR_NO_PS0 | SCALAR_HIGH, fps_fadds);854vcpu->arch.fpr[ax_rd] = vcpu->arch.fpr[ax_rc];855break;856case OP_4A_PS_SUM0:857emulated = kvmppc_ps_two_in(vcpu, rcomp, ax_rd,858ax_ra, ax_rb, SCALAR_NO_PS1 | SCALAR_LOW, fps_fadds);859vcpu->arch.qpr[ax_rd] = vcpu->arch.qpr[ax_rc];860break;861case OP_4A_PS_MULS0:862emulated = kvmppc_ps_two_in(vcpu, rcomp, ax_rd,863ax_ra, ax_rc, SCALAR_HIGH, fps_fmuls);864break;865case OP_4A_PS_MULS1:866emulated = kvmppc_ps_two_in(vcpu, rcomp, ax_rd,867ax_ra, ax_rc, SCALAR_LOW, fps_fmuls);868break;869case OP_4A_PS_MADDS0:870emulated = kvmppc_ps_three_in(vcpu, rcomp, ax_rd,871ax_ra, ax_rc, ax_rb, SCALAR_HIGH, fps_fmadds);872break;873case OP_4A_PS_MADDS1:874emulated = kvmppc_ps_three_in(vcpu, rcomp, ax_rd,875ax_ra, ax_rc, ax_rb, SCALAR_LOW, fps_fmadds);876break;877case OP_4A_PS_DIV:878emulated = kvmppc_ps_two_in(vcpu, rcomp, ax_rd,879ax_ra, ax_rb, SCALAR_NONE, fps_fdivs);880break;881case OP_4A_PS_SUB:882emulated = kvmppc_ps_two_in(vcpu, rcomp, ax_rd,883ax_ra, ax_rb, SCALAR_NONE, fps_fsubs);884break;885case OP_4A_PS_ADD:886emulated = kvmppc_ps_two_in(vcpu, rcomp, ax_rd,887ax_ra, ax_rb, SCALAR_NONE, fps_fadds);888break;889case OP_4A_PS_SEL:890emulated = kvmppc_ps_three_in(vcpu, rcomp, ax_rd,891ax_ra, ax_rc, ax_rb, SCALAR_NONE, fps_fsel);892break;893case OP_4A_PS_RES:894emulated = kvmppc_ps_one_in(vcpu, rcomp, ax_rd,895ax_rb, fps_fres);896break;897case OP_4A_PS_MUL:898emulated = kvmppc_ps_two_in(vcpu, rcomp, ax_rd,899ax_ra, ax_rc, SCALAR_NONE, fps_fmuls);900break;901case OP_4A_PS_RSQRTE:902emulated = kvmppc_ps_one_in(vcpu, rcomp, ax_rd,903ax_rb, fps_frsqrte);904break;905case OP_4A_PS_MSUB:906emulated = kvmppc_ps_three_in(vcpu, rcomp, ax_rd,907ax_ra, ax_rc, ax_rb, SCALAR_NONE, fps_fmsubs);908break;909case OP_4A_PS_MADD:910emulated = kvmppc_ps_three_in(vcpu, rcomp, ax_rd,911ax_ra, ax_rc, ax_rb, SCALAR_NONE, fps_fmadds);912break;913case OP_4A_PS_NMSUB:914emulated = kvmppc_ps_three_in(vcpu, rcomp, ax_rd,915ax_ra, ax_rc, ax_rb, SCALAR_NONE, fps_fnmsubs);916break;917case OP_4A_PS_NMADD:918emulated = kvmppc_ps_three_in(vcpu, rcomp, ax_rd,919ax_ra, ax_rc, ax_rb, SCALAR_NONE, fps_fnmadds);920break;921}922break;923924/* Real FPU operations */925926case OP_LFS:927{928ulong addr = (ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0) + full_d;929930emulated = kvmppc_emulate_fpr_load(run, vcpu, ax_rd, addr,931FPU_LS_SINGLE);932break;933}934case OP_LFSU:935{936ulong addr = kvmppc_get_gpr(vcpu, ax_ra) + full_d;937938emulated = kvmppc_emulate_fpr_load(run, vcpu, ax_rd, addr,939FPU_LS_SINGLE);940941if (emulated == EMULATE_DONE)942kvmppc_set_gpr(vcpu, ax_ra, addr);943break;944}945case OP_LFD:946{947ulong addr = (ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0) + full_d;948949emulated = kvmppc_emulate_fpr_load(run, vcpu, ax_rd, addr,950FPU_LS_DOUBLE);951break;952}953case OP_LFDU:954{955ulong addr = kvmppc_get_gpr(vcpu, ax_ra) + full_d;956957emulated = kvmppc_emulate_fpr_load(run, vcpu, ax_rd, addr,958FPU_LS_DOUBLE);959960if (emulated == EMULATE_DONE)961kvmppc_set_gpr(vcpu, ax_ra, addr);962break;963}964case OP_STFS:965{966ulong addr = (ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0) + full_d;967968emulated = kvmppc_emulate_fpr_store(run, vcpu, ax_rd, addr,969FPU_LS_SINGLE);970break;971}972case OP_STFSU:973{974ulong addr = kvmppc_get_gpr(vcpu, ax_ra) + full_d;975976emulated = kvmppc_emulate_fpr_store(run, vcpu, ax_rd, addr,977FPU_LS_SINGLE);978979if (emulated == EMULATE_DONE)980kvmppc_set_gpr(vcpu, ax_ra, addr);981break;982}983case OP_STFD:984{985ulong addr = (ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0) + full_d;986987emulated = kvmppc_emulate_fpr_store(run, vcpu, ax_rd, addr,988FPU_LS_DOUBLE);989break;990}991case OP_STFDU:992{993ulong addr = kvmppc_get_gpr(vcpu, ax_ra) + full_d;994995emulated = kvmppc_emulate_fpr_store(run, vcpu, ax_rd, addr,996FPU_LS_DOUBLE);997998if (emulated == EMULATE_DONE)999kvmppc_set_gpr(vcpu, ax_ra, addr);1000break;1001}1002case 31:1003switch (inst_get_field(inst, 21, 30)) {1004case OP_31_LFSX:1005{1006ulong addr = ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0;10071008addr += kvmppc_get_gpr(vcpu, ax_rb);1009emulated = kvmppc_emulate_fpr_load(run, vcpu, ax_rd,1010addr, FPU_LS_SINGLE);1011break;1012}1013case OP_31_LFSUX:1014{1015ulong addr = kvmppc_get_gpr(vcpu, ax_ra) +1016kvmppc_get_gpr(vcpu, ax_rb);10171018emulated = kvmppc_emulate_fpr_load(run, vcpu, ax_rd,1019addr, FPU_LS_SINGLE);10201021if (emulated == EMULATE_DONE)1022kvmppc_set_gpr(vcpu, ax_ra, addr);1023break;1024}1025case OP_31_LFDX:1026{1027ulong addr = (ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0) +1028kvmppc_get_gpr(vcpu, ax_rb);10291030emulated = kvmppc_emulate_fpr_load(run, vcpu, ax_rd,1031addr, FPU_LS_DOUBLE);1032break;1033}1034case OP_31_LFDUX:1035{1036ulong addr = kvmppc_get_gpr(vcpu, ax_ra) +1037kvmppc_get_gpr(vcpu, ax_rb);10381039emulated = kvmppc_emulate_fpr_load(run, vcpu, ax_rd,1040addr, FPU_LS_DOUBLE);10411042if (emulated == EMULATE_DONE)1043kvmppc_set_gpr(vcpu, ax_ra, addr);1044break;1045}1046case OP_31_STFSX:1047{1048ulong addr = (ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0) +1049kvmppc_get_gpr(vcpu, ax_rb);10501051emulated = kvmppc_emulate_fpr_store(run, vcpu, ax_rd,1052addr, FPU_LS_SINGLE);1053break;1054}1055case OP_31_STFSUX:1056{1057ulong addr = kvmppc_get_gpr(vcpu, ax_ra) +1058kvmppc_get_gpr(vcpu, ax_rb);10591060emulated = kvmppc_emulate_fpr_store(run, vcpu, ax_rd,1061addr, FPU_LS_SINGLE);10621063if (emulated == EMULATE_DONE)1064kvmppc_set_gpr(vcpu, ax_ra, addr);1065break;1066}1067case OP_31_STFX:1068{1069ulong addr = (ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0) +1070kvmppc_get_gpr(vcpu, ax_rb);10711072emulated = kvmppc_emulate_fpr_store(run, vcpu, ax_rd,1073addr, FPU_LS_DOUBLE);1074break;1075}1076case OP_31_STFUX:1077{1078ulong addr = kvmppc_get_gpr(vcpu, ax_ra) +1079kvmppc_get_gpr(vcpu, ax_rb);10801081emulated = kvmppc_emulate_fpr_store(run, vcpu, ax_rd,1082addr, FPU_LS_DOUBLE);10831084if (emulated == EMULATE_DONE)1085kvmppc_set_gpr(vcpu, ax_ra, addr);1086break;1087}1088case OP_31_STFIWX:1089{1090ulong addr = (ax_ra ? kvmppc_get_gpr(vcpu, ax_ra) : 0) +1091kvmppc_get_gpr(vcpu, ax_rb);10921093emulated = kvmppc_emulate_fpr_store(run, vcpu, ax_rd,1094addr,1095FPU_LS_SINGLE_LOW);1096break;1097}1098break;1099}1100break;1101case 59:1102switch (inst_get_field(inst, 21, 30)) {1103case OP_59_FADDS:1104fpd_fadds(&vcpu->arch.fpscr, &cr, fpr_d, fpr_a, fpr_b);1105kvmppc_sync_qpr(vcpu, ax_rd);1106break;1107case OP_59_FSUBS:1108fpd_fsubs(&vcpu->arch.fpscr, &cr, fpr_d, fpr_a, fpr_b);1109kvmppc_sync_qpr(vcpu, ax_rd);1110break;1111case OP_59_FDIVS:1112fpd_fdivs(&vcpu->arch.fpscr, &cr, fpr_d, fpr_a, fpr_b);1113kvmppc_sync_qpr(vcpu, ax_rd);1114break;1115case OP_59_FRES:1116fpd_fres(&vcpu->arch.fpscr, &cr, fpr_d, fpr_b);1117kvmppc_sync_qpr(vcpu, ax_rd);1118break;1119case OP_59_FRSQRTES:1120fpd_frsqrtes(&vcpu->arch.fpscr, &cr, fpr_d, fpr_b);1121kvmppc_sync_qpr(vcpu, ax_rd);1122break;1123}1124switch (inst_get_field(inst, 26, 30)) {1125case OP_59_FMULS:1126fpd_fmuls(&vcpu->arch.fpscr, &cr, fpr_d, fpr_a, fpr_c);1127kvmppc_sync_qpr(vcpu, ax_rd);1128break;1129case OP_59_FMSUBS:1130fpd_fmsubs(&vcpu->arch.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b);1131kvmppc_sync_qpr(vcpu, ax_rd);1132break;1133case OP_59_FMADDS:1134fpd_fmadds(&vcpu->arch.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b);1135kvmppc_sync_qpr(vcpu, ax_rd);1136break;1137case OP_59_FNMSUBS:1138fpd_fnmsubs(&vcpu->arch.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b);1139kvmppc_sync_qpr(vcpu, ax_rd);1140break;1141case OP_59_FNMADDS:1142fpd_fnmadds(&vcpu->arch.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b);1143kvmppc_sync_qpr(vcpu, ax_rd);1144break;1145}1146break;1147case 63:1148switch (inst_get_field(inst, 21, 30)) {1149case OP_63_MTFSB0:1150case OP_63_MTFSB1:1151case OP_63_MCRFS:1152case OP_63_MTFSFI:1153/* XXX need to implement */1154break;1155case OP_63_MFFS:1156/* XXX missing CR */1157*fpr_d = vcpu->arch.fpscr;1158break;1159case OP_63_MTFSF:1160/* XXX missing fm bits */1161/* XXX missing CR */1162vcpu->arch.fpscr = *fpr_b;1163break;1164case OP_63_FCMPU:1165{1166u32 tmp_cr;1167u32 cr0_mask = 0xf0000000;1168u32 cr_shift = inst_get_field(inst, 6, 8) * 4;11691170fpd_fcmpu(&vcpu->arch.fpscr, &tmp_cr, fpr_a, fpr_b);1171cr &= ~(cr0_mask >> cr_shift);1172cr |= (cr & cr0_mask) >> cr_shift;1173break;1174}1175case OP_63_FCMPO:1176{1177u32 tmp_cr;1178u32 cr0_mask = 0xf0000000;1179u32 cr_shift = inst_get_field(inst, 6, 8) * 4;11801181fpd_fcmpo(&vcpu->arch.fpscr, &tmp_cr, fpr_a, fpr_b);1182cr &= ~(cr0_mask >> cr_shift);1183cr |= (cr & cr0_mask) >> cr_shift;1184break;1185}1186case OP_63_FNEG:1187fpd_fneg(&vcpu->arch.fpscr, &cr, fpr_d, fpr_b);1188break;1189case OP_63_FMR:1190*fpr_d = *fpr_b;1191break;1192case OP_63_FABS:1193fpd_fabs(&vcpu->arch.fpscr, &cr, fpr_d, fpr_b);1194break;1195case OP_63_FCPSGN:1196fpd_fcpsgn(&vcpu->arch.fpscr, &cr, fpr_d, fpr_a, fpr_b);1197break;1198case OP_63_FDIV:1199fpd_fdiv(&vcpu->arch.fpscr, &cr, fpr_d, fpr_a, fpr_b);1200break;1201case OP_63_FADD:1202fpd_fadd(&vcpu->arch.fpscr, &cr, fpr_d, fpr_a, fpr_b);1203break;1204case OP_63_FSUB:1205fpd_fsub(&vcpu->arch.fpscr, &cr, fpr_d, fpr_a, fpr_b);1206break;1207case OP_63_FCTIW:1208fpd_fctiw(&vcpu->arch.fpscr, &cr, fpr_d, fpr_b);1209break;1210case OP_63_FCTIWZ:1211fpd_fctiwz(&vcpu->arch.fpscr, &cr, fpr_d, fpr_b);1212break;1213case OP_63_FRSP:1214fpd_frsp(&vcpu->arch.fpscr, &cr, fpr_d, fpr_b);1215kvmppc_sync_qpr(vcpu, ax_rd);1216break;1217case OP_63_FRSQRTE:1218{1219double one = 1.0f;12201221/* fD = sqrt(fB) */1222fpd_fsqrt(&vcpu->arch.fpscr, &cr, fpr_d, fpr_b);1223/* fD = 1.0f / fD */1224fpd_fdiv(&vcpu->arch.fpscr, &cr, fpr_d, (u64*)&one, fpr_d);1225break;1226}1227}1228switch (inst_get_field(inst, 26, 30)) {1229case OP_63_FMUL:1230fpd_fmul(&vcpu->arch.fpscr, &cr, fpr_d, fpr_a, fpr_c);1231break;1232case OP_63_FSEL:1233fpd_fsel(&vcpu->arch.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b);1234break;1235case OP_63_FMSUB:1236fpd_fmsub(&vcpu->arch.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b);1237break;1238case OP_63_FMADD:1239fpd_fmadd(&vcpu->arch.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b);1240break;1241case OP_63_FNMSUB:1242fpd_fnmsub(&vcpu->arch.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b);1243break;1244case OP_63_FNMADD:1245fpd_fnmadd(&vcpu->arch.fpscr, &cr, fpr_d, fpr_a, fpr_c, fpr_b);1246break;1247}1248break;1249}12501251#ifdef DEBUG1252for (i = 0; i < ARRAY_SIZE(vcpu->arch.fpr); i++) {1253u32 f;1254kvm_cvt_df(&vcpu->arch.fpr[i], &f);1255dprintk(KERN_INFO "FPR[%d] = 0x%x\n", i, f);1256}1257#endif12581259if (rcomp)1260kvmppc_set_cr(vcpu, cr);12611262preempt_enable();12631264return emulated;1265}126612671268