static int cop1Emulate(int xcptno, struct pt_regs *xcp, struct mips_fpu_soft_struct *ctx) { mips_instruction ir; vaddr_t emulpc; vaddr_t contpc; unsigned int cond; int err = 0; ir = mips_get_word(xcp, REG_TO_VA xcp->cp0_epc, &err); if (err) { fpuemuprivate.stats.errors++; return SIGBUS; } /* XXX NEC Vr54xx bug workaround */ if ((xcp->cp0_cause & CAUSEF_BD) && !isBranchInstr(&ir)) xcp->cp0_cause &= ~CAUSEF_BD; if (xcp->cp0_cause & CAUSEF_BD) { /* * The instruction to be emulated is in a branch delay slot * which means that we have to emulate the branch instruction * BEFORE we do the cop1 instruction. * * This branch could be a COP1 branch, but in that case we * would have had a trap for that instruction, and would not * come through this route. * * Linux MIPS branch emulator operates on context, updating the * cp0_epc. */ emulpc = REG_TO_VA(xcp->cp0_epc + 4); /* Snapshot emulation target */ if (__compute_return_epc(xcp)) { #ifdef CP1DBG printk("failed to emulate branch at %p\n", REG_TO_VA(xcp->cp0_epc)); #endif return SIGILL;; } ir = mips_get_word(xcp, emulpc, &err); if (err) { fpuemuprivate.stats.errors++; return SIGBUS; } contpc = REG_TO_VA xcp->cp0_epc; } else { emulpc = REG_TO_VA xcp->cp0_epc; contpc = REG_TO_VA xcp->cp0_epc + 4; } emul: fpuemuprivate.stats.emulated++; switch (MIPSInst_OPCODE(ir)) { #ifdef CP0_STATUS_FR_SUPPORT /* R4000+ 64-bit fpu registers */ #ifndef SINGLE_ONLY_FPU case ldc1_op: { void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)]) + MIPSInst_SIMM(ir); int ft = MIPSInst_RT(ir); if (!(xcp->cp0_status & ST0_FR)) ft &= ~1; ctx->regs[ft] = mips_get_dword(xcp, va, &err); fpuemuprivate.stats.loads++; if (err) { fpuemuprivate.stats.errors++; return SIGBUS; } } break; case sdc1_op: { void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)]) + MIPSInst_SIMM(ir); int ft = MIPSInst_RT(ir); if (!(xcp->cp0_status & ST0_FR)) ft &= ~1; fpuemuprivate.stats.stores++; if (mips_put_dword(xcp, va, ctx->regs[ft])) { fpuemuprivate.stats.errors++; return SIGBUS; } } break; #endif case lwc1_op: { void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)]) + MIPSInst_SIMM(ir); fpureg_t val; int ft = MIPSInst_RT(ir); fpuemuprivate.stats.loads++; val = mips_get_word(xcp, va, &err); if (err) { fpuemuprivate.stats.errors++; return SIGBUS; } if (xcp->cp0_status & ST0_FR) { /* load whole register */ ctx->regs[ft] = val; } else if (ft & 1) { /* load to m.s. 32 bits */ #ifdef SINGLE_ONLY_FPU /* illegal register in single-float mode */ return SIGILL; #else ctx->regs[(ft & ~1)] &= 0xffffffff; ctx->regs[(ft & ~1)] |= val << 32; #endif } else { /* load to l.s. 32 bits */ ctx->regs[ft] &= ~0xffffffffLL; ctx->regs[ft] |= val; } } break; case swc1_op: { void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)]) + MIPSInst_SIMM(ir); unsigned int val; int ft = MIPSInst_RT(ir); fpuemuprivate.stats.stores++; if (xcp->cp0_status & ST0_FR) { /* store whole register */ val = ctx->regs[ft]; } else if (ft & 1) { #ifdef SINGLE_ONLY_FPU /* illegal register in single-float mode */ return SIGILL; #else /* store from m.s. 32 bits */ val = ctx->regs[(ft & ~1)] >> 32; #endif } else { /* store from l.s. 32 bits */ val = ctx->regs[ft]; } if (mips_put_word(xcp, va, val)) { fpuemuprivate.stats.errors++; return SIGBUS; } } break; #else /* old 32-bit fpu registers */ case lwc1_op: { void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)]) + MIPSInst_SIMM(ir); ctx->regs[MIPSInst_RT(ir)] = mips_get_word(xcp, va, &err); fpuemuprivate.stats.loads++; if (err) { fpuemuprivate.stats.errors++; return SIGBUS; } } break; case swc1_op: { void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)]) + MIPSInst_SIMM(ir); fpuemuprivate.stats.stores++; if (mips_put_word (xcp, va, ctx->regs[MIPSInst_RT(ir)])) { fpuemuprivate.stats.errors++; return SIGBUS; } } break; case ldc1_op: { void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)]) + MIPSInst_SIMM(ir); unsigned int rt = MIPSInst_RT(ir) & ~1; int errs = 0; fpuemuprivate.stats.loads++; #if (defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN) || defined(__MIPSEB__) ctx->regs[rt + 1] = mips_get_word(xcp, va + 0, &err); errs += err; ctx->regs[rt + 0] = mips_get_word(xcp, va + 4, &err); errs += err; #else ctx->regs[rt + 0] = mips_get_word(xcp, va + 0, &err); errs += err; ctx->regs[rt + 1] = mips_get_word(xcp, va + 4, &err); errs += err; #endif if (err) return SIGBUS; } break; case sdc1_op: { void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)]) + MIPSInst_SIMM(ir); unsigned int rt = MIPSInst_RT(ir) & ~1; fpuemuprivate.stats.stores++; #if (defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN) || defined(__MIPSEB__) if (mips_put_word(xcp, va + 0, ctx->regs[rt + 1])) return SIGBUS; if (mips_put_word(xcp, va + 4, ctx->regs[rt + 0])) return SIGBUS; #else if (mips_put_word(xcp, va + 0, ctx->regs[rt + 0])) return SIGBUS; if (mips_put_word(xcp, va + 4, ctx->regs[rt + 1])) return SIGBUS; #endif } break; #endif case cop1_op: switch (MIPSInst_RS(ir)) { #ifdef CP0_STATUS_FR_SUPPORT #if __mips64 && !defined(SINGLE_ONLY_FPU) case dmfc_op: /* copregister fs -> gpr[rt] */ if (MIPSInst_RT(ir) != 0) { int fs = MIPSInst_RD(ir); if (!(xcp->cp0_status & ST0_FR)) fs &= ~1; xcp->regs[MIPSInst_RT(ir)] = ctx->regs[fs]; } break; case dmtc_op: /* copregister fs <- rt */ { fpureg_t value; int fs = MIPSInst_RD(ir); if (!(xcp->cp0_status & ST0_FR)) fs &= ~1; value = (MIPSInst_RT(ir) == 0) ? 0 : xcp->regs[MIPSInst_RT(ir)]; ctx->regs[fs] = value; } break; #endif case mfc_op: /* copregister rd -> gpr[rt] */ if (MIPSInst_RT(ir) != 0) { /* default value from l.s. 32 bits */ int value = ctx->regs[MIPSInst_RD(ir)]; if (MIPSInst_RD(ir) & 1) { #ifdef SINGLE_ONLY_FPU /* illegal register in single-float mode */ return SIGILL; #else if (!(xcp->cp0_status & ST0_FR)) { /* move from m.s. 32 bits */ value = ctx-> regs[MIPSInst_RD(ir) & ~1] >> 32; } #endif } xcp->regs[MIPSInst_RT(ir)] = value; }
static int emu_lwl(struct pt_regs * regs,mips_instruction ir,vaddr_t_l emulpc) { int err = 0; /*the "ir" is the instruction causing the exception*/ /*get the real address,perhaps the address is not word aligned*/ void *va = REG_TO_VA_l (regs->regs[MIPSInst_RS(ir)])+ MIPSInst_SIMM(ir); unsigned long addr = 0; unsigned long emul_pc = (unsigned long)emulpc; unsigned long little_three_bits; unsigned long value,value_tmp; // printf("emu_lwl\r\n"); /*compute the correct position in the RT*/ /*note !!!!: we have supposed the CPU is little_Endianness and status regiester's RE bit =0 */ /*little Endianness*/ little_three_bits = (unsigned long)va&(0x7); value_tmp = regs->regs[MIPSInst_RT(ir)]; switch(little_three_bits) { case 0: case 4: /*must check lwl valid*/ addr = (unsigned long) va; check_axs(emul_pc,addr,4); value = mips_get_word_l(regs,va,&err); if(err){ return SIGBUS; } value<<=24; value_tmp &= 0xffffff; regs->regs[MIPSInst_RT(ir)] =value_tmp|value; break; case 1: case 5: addr = (unsigned long)va -1; check_axs(emul_pc,addr,4); value = mips_get_word_l(regs,(void *)((unsigned long) va-1),&err); if(err){ return SIGBUS; } value<<=16; value_tmp&=0xffff; regs->regs[MIPSInst_RT(ir)] =value_tmp|value; break; case 2: case 6: addr = (unsigned long)va - 2; check_axs(emul_pc,addr,4); value = mips_get_word_l(regs,(void *)((unsigned long)va-2),&err); if(err){ return SIGBUS; } value<<=8; value_tmp &= 0xff; regs->regs[MIPSInst_RT(ir)] =value_tmp|value; break; case 3: case 7: addr = (unsigned long)va - 3; check_axs(emul_pc,addr,4); value = mips_get_word_l(regs,(void *)((unsigned long)va-3),&err); if(err){ return SIGBUS; }; regs->regs[MIPSInst_RT(ir)] = value; break; } /*swith ended*/ return 0; }