You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

769 lines
16 KiB

//extern onintr(int a,int *b);
//#include "mips/cpu.h"
#include "mips/prid.h"
#include "mips.h"
#include "pmon.h"
#ifdef R3081
#include "r3081.h"
#endif
#ifdef R3041
#include "r3041.h"
#endif
#include "ri.h"
static unsigned long
mips_get_word_l(struct pt_regs *xcp, void *va, int *perr)
{
*perr = 0;
return(*(unsigned long *)va);
}
static int
mips_put_word_l(struct pt_regs *xcp, void *va, unsigned long val)
{
*(unsigned long *)va = val;
return 0;
}
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;
}
static int emu_lwr(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;
unsigned long emul_pc = (unsigned long)emulpc;
unsigned long little_three_bits;
unsigned long value,value_tmp;
// printf("emu_lwr\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_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;
}
regs->regs[MIPSInst_RT(ir)] =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>>=8;
value_tmp&=0xff000000;
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>>=16;
value_tmp &= 0xffff0000;
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;
};
value>>=24;
value_tmp &= 0xffffff00;
regs->regs[MIPSInst_RT(ir)] = value_tmp|value;
break;
} /*swith ended*/
return 0;
}
static int emu_swl(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;
unsigned long emul_pc = (unsigned long)emulpc;
unsigned long little_three_bits;
unsigned long value,value_tmp;
// printf("emu_swl\r\n");
/*compute the correct position in the RT*/
/*note !!!!: we have supposed the CPU is little_Endianness and status re
* giester's RE bit =0 */
little_three_bits = (unsigned long)va&(0x7);
value_tmp = regs->regs[MIPSInst_RT(ir)];
switch(little_three_bits) {
case 0:
case 4:
addr = (unsigned long)va;
check_axs(emul_pc,addr,4);
value_tmp >>= 24;
value = mips_get_word_l(regs,va,&err);
if(err){
return SIGBUS;
}
value &=0xffffff00;
value |= value_tmp;
if(mips_put_word_l(regs,va,value)){
return SIGBUS;
}
break;
case 1:
case 5:
addr = (unsigned long)va -1;
check_axs(emul_pc,addr,4);
value_tmp >>= 16;
value = mips_get_word_l(regs,(void *)((unsigned long)va-1),&err);
if(err){
return SIGBUS;
}
value &=0xffff0000;
value |= value_tmp;
if(mips_put_word_l(regs,(void *)((unsigned long)va-1),value)){
return SIGBUS;
}
break;
case 2:
case 6:
addr = (unsigned long)va - 2;
check_axs(emul_pc,addr,4);
value_tmp >>= 8;
value = mips_get_word_l(regs,(void *)((unsigned long)va-2),&err);
if(err){
return SIGBUS;
}
value &=0xff000000;
value |= value_tmp;
if(mips_put_word_l(regs,(void *)((unsigned long)va-2),value)){
return SIGBUS;
}
break;
case 3:
case 7:
addr = (unsigned long)va - 3;
check_axs(emul_pc,addr,4);
value = value_tmp;
if(mips_put_word_l(regs,(void *)((unsigned long)va-3),value)){
return SIGBUS;
}
break;
}
return 0;
}
static int emu_swr(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;
unsigned long emul_pc = (unsigned long)emulpc;
unsigned long little_three_bits;
unsigned long value,value_tmp;
// printf("emu_swr\r\n");
/*compute the correct position in the RT*/
/*note !!!!: we have supposed the CPU is little_Endianness and status re
* giester's RE bit =0 */
little_three_bits = (unsigned long)va&(0x7);
value_tmp = regs->regs[MIPSInst_RT(ir)];
switch(little_three_bits) {
case 0:
case 4:
addr = (unsigned long) va;
check_axs(emul_pc,addr,4);
value = value_tmp;
if(mips_put_word_l(regs,va,value)){
return SIGBUS;
}
break;
case 1:
case 5:
addr = (unsigned long)va -1;
check_axs(emul_pc,addr,4);
value_tmp <<= 8;
value = mips_get_word_l(regs,(void *)((unsigned long)va-1),&err);
if(err){
return SIGBUS;
}
value &=0xff;
value |= value_tmp;
if(mips_put_word_l(regs,(void *)((unsigned long)va-1),value)){
return SIGBUS;
}
break;
case 2:
case 6:
addr = (unsigned long)va - 2;
check_axs(emul_pc,addr,4);
value_tmp <<= 16;
value = mips_get_word_l(regs,(void *)((unsigned long)va-2),&err);
if(err){
return SIGBUS;
}
value &=0xffff;
value |= value_tmp;
if(mips_put_word_l(regs,(void *)((unsigned long)va-2),value)){
return SIGBUS;
}
break;
case 3:
case 7:
addr = (unsigned long)va -3;
check_axs(emul_pc,addr,4);
value_tmp <<= 24;
value = mips_get_word_l(regs,(void *)((unsigned long)va-3),&err);
if(err){
return SIGBUS;
}
value &= 0xffffff;
value |= value_tmp;
if(mips_put_word_l(regs,(void *)((unsigned long)va-3),value)){
return SIGBUS;
}
break;
}
return 0;
}
static int emu_div(struct pt_regs *regs,mips_instruction ir)
{
int x,y;
int flag = 0;
int quotient = 0,remainder = 0;
unsigned int absx,absy,absquotient = 0,absremainder = 0,bm = 1;
/*the "ir" is the instruction causing the exception*/
x = regs->regs[MIPSInst_RS(ir)];
y = regs->regs[MIPSInst_RT(ir)];
#ifdef __test_ri__
//printf("now in function:emu_div().\r\n");
#endif
if( y == 0 ) {/*overflow*/
return SIGABRT;
}
/*x and y ?????Ƿ???ͬ*/
flag = (x&0x80000000)^(y&0x80000000);
/*get the abs(x)*/
if(x<0){
absx = (unsigned int)-x;
}else {
absx = (unsigned int)x;
}
/*get the abs(y)*/
if(y<0){
absy = (unsigned int) -y;
}else {
absy = (unsigned int)y;
}
/*caculate the absx/absy*/
if(absx<absy) {/*don't need to calculate*/
absquotient = 0;
absremainder = absx;
goto end;
}
while(!(absy&0x80000000))
{ absy<<=1;
if(absx<absy){
absy>>= 1;
break;
}
bm<<=1;
}
for(;bm;bm>>=1){
if(absx>=absy){
absx -= absy;
absquotient |= bm;
if(absx == 0)
break;
}
absy >>= 1;
}
absremainder = absx;
end:
if( flag ){/*????????*/
quotient = -absquotient;
remainder = x-quotient*y;
}else {
quotient = absquotient;
remainder = x - quotient*y;
}
regs->lo =(unsigned long)quotient;
regs->hi = (unsigned long)remainder;
#ifdef __test_ri__
// printf("x is: %d\r\n",x);
// printf("y is: %d\r\n",y);
// printf("result is: %d (:\r\n",quotient);
#endif
return 0;
}
static int emu_divu(struct pt_regs *regs,mips_instruction ir)
{
unsigned int x,y,bm=1;
unsigned int quotient = 0,remainder = 0;
/*the "ir" is the instruction causing the exception*/
x = regs->regs[MIPSInst_RS(ir)];
y = regs->regs[MIPSInst_RT(ir)];
if( y == 0 ) {/*overflow*/
return SIGABRT;
}
if(x<y) {/*don't need to calculate*/
quotient = 0;
remainder = x;
goto end;
}
while(!(y&0x80000000))
{ y<<=1;
if(x<y){
y>>= 1;
break;
}
bm<<=1;
}
for(;bm;bm>>=1){
if(x>=y){
x -= y;
quotient |= bm;
if(x == 0)
break;
}
y >>= 1;
}
remainder = x;
end:
regs->lo = quotient;
regs->hi = remainder;
return 0;
}
/*
* Compute the return address and do emulate branch simulation, if required.
*/
#define EFAULT 1
int __compute_return_epc(struct pt_regs *regs)
{
unsigned int *addr, bit, fcr31;
long epc;
mips_instruction insn;
epc = regs->cp0_epc;
if (epc & 3) {
printf("%s: unaligned epc - sending SIGBUS.\n");
// force_sig(SIGBUS, current);
return -EFAULT;
}
/*
* Read the instruction
*/
addr = (unsigned int *) (unsigned long) epc;
#if 0
if (__get_user(insn, addr)) {
printf("%s: bad epc value - sending SIGSEGV.\n");
// force_sig(SIGSEGV, current);
return -EFAULT;
}
#endif
//bjzheng add __get_user is prevent page_fault exception,if this occurs,load from disk,but now my whole code is in ram.
insn=*addr;
// printf("instruction is %x",insn);
regs->regs[0] = 0;
switch (MIPSInst_OPCODE(insn)) {
/*
* jr and jalr are in r_format format.
*/
case spec_op:
switch (MIPSInst_FUNC(insn)) {
case jalr_op:
regs->regs[MIPSInst_RD(insn)] = epc + 8;
/* Fall through */
case jr_op:
regs->cp0_epc = regs->regs[MIPSInst_RS(insn)];
break;
}
break;
/*
* This group contains:
* bltz_op, bgez_op, bltzl_op, bgezl_op,
* bltzal_op, bgezal_op, bltzall_op, bgezall_op.
*/
case bcond_op:
switch (MIPSInst_RT(insn)) {
case bltz_op:
case bltzl_op:
if ((long)regs->regs[MIPSInst_RS(insn)] < 0)
epc = epc + 4 + (MIPSInst_SIMM(insn) << 2);
else
epc += 8;
regs->cp0_epc = epc;
break;
case bgez_op:
case bgezl_op:
if ((long)regs->regs[MIPSInst_RS(insn)] >= 0)
epc = epc + 4 + (MIPSInst_SIMM(insn) << 2);
else
epc += 8;
regs->cp0_epc = epc;
break;
case bltzal_op:
case bltzall_op:
regs->regs[31] = epc + 8;
if ((long)regs->regs[MIPSInst_RS(insn)] < 0)
epc = epc + 4 + (MIPSInst_SIMM(insn) << 2);
else
epc += 8;
regs->cp0_epc = epc;
break;
case bgezal_op:
case bgezall_op:
regs->regs[31] = epc + 8;
if ((long)regs->regs[MIPSInst_RS(insn)] >= 0)
epc = epc + 4 + (MIPSInst_SIMM(insn) << 2);
else
epc += 8;
regs->cp0_epc = epc;
break;
}
break;
/*
* These are unconditional and in j_format.
*/
case jal_op:
regs->regs[31] = regs->cp0_epc + 8;
case j_op:
epc += 4;
epc >>= 28;
epc <<= 28;
epc |= (MIPSInst_JTARGET(insn) << 2);
regs->cp0_epc = epc;
break;
/*
* These are conditional and in i_format.
*/
case beq_op:
case beql_op:
if (regs->regs[MIPSInst_RS(insn)] ==
regs->regs[MIPSInst_RT(insn)])
epc = epc + 4 + (MIPSInst_SIMM(insn) << 2);
else
epc += 8;
regs->cp0_epc = epc;
break;
case bne_op:
case bnel_op:
if (regs->regs[MIPSInst_RS(insn)] !=
regs->regs[MIPSInst_RT(insn)])
epc = epc + 4 + (MIPSInst_SIMM(insn) << 2);
else
epc += 8;
regs->cp0_epc = epc;
break;
case blez_op: /* not really i_format */
case blezl_op:
/* rt field assumed to be zero */
if ((long)regs->regs[MIPSInst_RS(insn)] <= 0)
epc = epc + 4 + (MIPSInst_SIMM(insn) << 2);
else
epc += 8;
regs->cp0_epc = epc;
break;
case bgtz_op:
case bgtzl_op:
/* rt field assumed to be zero */
if ((long)regs->regs[MIPSInst_RS(insn)] > 0)
epc = epc + 4 + (MIPSInst_SIMM(insn) << 2);
else
epc += 8;
regs->cp0_epc = epc;
break;
/*
* And now the FPA/cp1 branch instructions.
*/
case cop1_op:
#ifdef CONFIG_MIPS_FPU_EMULATOR
if(!(mips_cpu.options & MIPS_CPU_FPU))
fcr31 = current->tss.fpu.soft.sr;
else
#endif
asm ("cfc1\t%0,$31":"=r" (fcr31));
bit = (MIPSInst_RT(insn) >> 2);
bit += (bit != 0);
bit += 23;
switch (MIPSInst_RT(insn)) {
case 0: /* bc1f */
case 2: /* bc1fl */
if (~fcr31 & (1 << bit))
epc = epc + 4 + (MIPSInst_SIMM(insn) << 2);
else
epc += 8;
regs->cp0_epc = epc;
break;
case 1: /* bc1t */
case 3: /* bc1tl */
if (fcr31 & (1 << bit))
epc = epc + 4 + (MIPSInst_SIMM(insn) << 2);
else
epc += 8;
regs->cp0_epc = epc;
break;
}
break;
}
return 0;
}
int do_ri (struct pt_regs *xcp)
{
mips_instruction ir;
vaddr_t_l emulpc;
vaddr_t_l contpc;
int err = 0;
int sig;
ir = mips_get_word_l(xcp, REG_TO_VA_l xcp->cp0_epc, &err);
if (err) {
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 emulating instruction.
* This branch could be a COP1 branch
*/
emulpc = REG_TO_VA_l(xcp->cp0_epc + 4); /* Snapshot emulation target */
#ifndef __bjzheng__
if( __compute_return_epc(xcp)) {/*compute the return address*/
#ifdef DBG
_mon_printf ("failed to emulate branch at %p\n",
REG_TO_VA_l (xcp->cp0_epc));
#endif
return -1;;
}
#endif
ir = mips_get_word_l(xcp, emulpc, &err);
if (err) {
return SIGBUS;
}
contpc = REG_TO_VA_l xcp->cp0_epc;
} else { /* not in the Branch delay slot*/
emulpc = REG_TO_VA_l xcp->cp0_epc;
contpc = REG_TO_VA_l xcp->cp0_epc + 4;
}
switch(MIPSInst_OPCODE(ir)) {
case lwl_op: /*lwl instruction*/
sig = emu_lwl(xcp,ir,emulpc);
if( sig!=0) { /*emul has failed*/
return sig;
}
break;
case lwr_op:/*lwr instruction*/
sig = emu_lwr(xcp,ir,emulpc);
if ( sig != 0){
/*emulate has failed!\n");*/
return sig;
}
break;
case swl_op:
sig = emu_swl(xcp,ir,emulpc);
if( sig!=0 ) { /*emul has failed!*/
printf("emu_swl error\r\n");
return sig;
}
break;
case swr_op:
sig = emu_swr(xcp,ir,emulpc);
if( sig!=0 ) { /*emul has failed!*/
printf("emu_swr error\r\n");
return sig;
}
break;
case spec_op:
switch (MIPSInst_FUNC(ir)){
case div_op:/*div_op*/
sig = emu_div(xcp,ir);
if(sig) {
return sig;
}
break;
case divu_op:/* divu_op:*/
sig = emu_divu(xcp,ir);
if(sig) {
return sig;
}
break;
default:;
}
default:;
}
/*we do it*/
xcp->cp0_epc = VA_TO_REG_l(contpc);
xcp->cp0_cause &= ~CAUSEF_BD;
return sig;
}