📄 r4k_cp0.c
字号:
P->CP0[C0_TLBHI] = P->exception[exnum].hireg; if (P->exception[exnum].cause == EXC_CPU) { CauseReg causeReg; causeReg.tc_data = P->CP0[C0_CAUSE]; causeReg.s32.tc_ce = P->exception[exnum].badaddr; P->CP0[C0_CAUSE] = causeReg.tc_data; } else { /* * MIPS r4k manual is WRONG on this one. * Always latch badvaddr */ P->CP0[C0_BADVADDR] = P->exception[exnum].badaddr; } P->CP0[C0_CTXT] = P->exception[exnum].ctxtreg; if (P->exception[exnum].vec == UT_VEC) { UTLB_EXCEPTION(P, P->exception[exnum].cause); } else { EXCEPTION(P, P->exception[exnum].cause); } P->PC = P->exception[exnum].vec; P->nPC = P->exception[exnum].vec+4; if (P->exception[exnum].cause == EXC_SYSCALL) {#define SYSVoffset 1000 int syscallNum = P->R[REG_V0] - SYSVoffset; ASSERT((syscallNum < MAX_SYSCALL) && (syscallNum >= 0)); STATS_INC(P->myNum, numSyscalls, 1); STATS_INC(P->myNum, syscallCount[syscallNum], 1); } CopyToMXS (P); /* Resets the MXS instruction window */ return;}intFPusable(struct s_cpu_state *st){ CPUState *P = (CPUState *) (st->mipsyPtr); StatusReg statusReg; statusReg.ts_data = P->CP0[C0_SR]; if (!(statusReg.s32.ts_cu1)) { /* CE bits of the cause register are set in badvaddr */ RECORD_EXCEPTION(P, EXC_CPU, E_VEC, 1, P->CP0[C0_TLBHI], P->CP0[C0_CTXT],P->CP0[C0_XCTXT]); return 0; } return 1;}#endif /* MIPSY_MXS *//* END UNTOUCHED MXS *//***************************************************************** * ExceptionReturn * instruction for returning from an interrupt, exception, or * error trap. if error trap load pc from the ErrorEPC and clear * ERL bit of the status register. else load pc from EPC and clear * EXL bit of the status register. * PC gets set in R4, not in R3. * Handler must save pc, op mode, status or interrupts enable, * and restore. *****************************************************************/void ExceptionReturn(CPUState *P){ StatusReg statusReg; statusReg.ts_data = P->CP0[C0_SR]; RFE_EVENT(); if (statusReg.s32.ts_erl){ P->PC = P->CP0[C0_ERROR_EPC]; statusReg.s32.ts_erl = 0; } else { P->PC = P->CP0[C0_EPC]; statusReg.s32.ts_exl = 0; } P->nPC = P->PC + INST_SIZE; P->CP0[C0_SR] = statusReg.ts_data; UpdateCPUMode(P); MipsyCheckForInterrupts(P);}/***************************************************************** * MipsyCheckForInterrupts * The cause register needs to have its interrupt pending (IP) bits * before we determine if we have to take an interrupts. * MipsyCheckForInterrupts is called every time the cpu simulator * sets an interrupt, on every mtc_op to CO_SR and periodically to * catch TTY and net interrupts. * For an interrupt exception to take place, a few things have to be true: * (1) an interrupt bit has to be set in the cause register * (2) that interrupt must be enabled in the SR's IM field * (3) the SR must have the current interrupt enabled field set * (4) Can't be running at exception level. ****************************************************************/void MipsyCheckForInterrupts(CPUState *P){ P->CP0[C0_CAUSE] = (P->CP0[C0_CAUSE] & ~CAUSE_EXTINTBITS) | ((P->intrBitsPtr[0] << CAUSE_IPSHIFT) & CAUSE_EXTINTBITS); if (((P->CP0[C0_CAUSE] & P->CP0[C0_SR]) & SR_IMASK) && (P->CP0[C0_SR] & SR_IEC) && !(P->CP0[C0_SR] & (SR_EXL|SR_ERL))) { STATS_SET(P->myNum, syncStallStart, 0); P->takeInterrupt = TRUE; }}/***************************************************************** * MipsyCacheError * * Flashlite calls this for Cache Errors * * 1) Set ERL * 2) Save the current PC as ERROR_EPC * 3) Set the PC to CACHE_ERROR_VEC (addr_layout.h) * * CAVEAT user: * This doesn't yet implement the full R10K functionality. In the * R10K, a second cache error may be posted, but will not be taken * while the ERL status bit is set. To be done. * *****************************************************************/voidMipsyCacheError(int cpuNum, bool isAsync){ StatusReg statusReg; CPUState *P = &PE[cpuNum]; /* * Shift current mode and interrupt status to previous, * previous to old, and old can be discarded. This lets the CPU * respond to three levels of exceptions before software must * save the contents of the Status register. */ statusReg.ts_data = P->CP0[C0_SR]; statusReg.s32.ts_erl = 1; P->CP0[C0_SR] = statusReg.ts_data; P->cpuMode = KERNEL_MODE; /* This is kind of messy. The R100000 manual says one thing and the R4000 manual (and sbd.h) says another, so using the R4000 settings for now... */ if (isAsync) { /* from sysAD: setting ED and EE */ P->CP0[C0_CACHE_ERR] = 0x24000000; } else { /* a real cache error: setting ED */ P->CP0[C0_CACHE_ERR] = 0x20000000; } /* for uncached stalled cpus, wait for the stalled instruction to complete first */ if (P->cpuStatus == cpu_stalled_uncached) { P->CP0[C0_ERROR_EPC] = P->nPC; if (statusReg.s32.ts_bev == 0) { P->nPC = CACHE_ERR_VEC_BEV0; } else { P->nPC = CACHE_ERR_VEC_BEV1; } } else { /* if cpu is running, take cache error right now */ /* Save the current pc as C0_ERROR_EPC, DONT set Branch delay bit */ if (P->nPC != (P->PC + INST_SIZE)) { /* The inst that was preempted is in a BD slot */ P->CP0[C0_ERROR_EPC] = P->PC - INST_SIZE; } else { P->CP0[C0_ERROR_EPC] = P->PC; } if (statusReg.s32.ts_bev == 0) { P->PC = CACHE_ERR_VEC_BEV0; P->nPC = CACHE_ERR_VEC_BEV0 + 4; } else { P->PC = CACHE_ERR_VEC_BEV1; P->nPC = CACHE_ERR_VEC_BEV1 + 4; } } P->stalledInst = FALSE; /* Force inst refetch from exc vect */}/***************************************************************** * UpdateCPUMode * * This function is shared between mipsy and embra * Update with care. Do not remove the embra specific * stuff. *****************************************************************/static void UpdateCPUMode(CPUState *P){ StatusReg statusReg; statusReg.ts_data = P->CP0[C0_SR]; P->cpuMode = KERNEL_MODE; if (!statusReg.s32.ts_erl && !statusReg.s32.ts_exl) { ASSERT(statusReg.s32.ts_ksu != 3); if (statusReg.s32.ts_ksu == 2) { P->cpuMode = USER_MODE; } else if (statusReg.s32.ts_ksu == 1) { P->cpuMode = SUPERVISOR_MODE; } } P->notFRbit = (statusReg.s32.ts_fr == 0); switch (P->cpuMode) { case KERNEL_MODE: P->is32bitMode = (statusReg.s32.ts_kx == 0); break; case SUPERVISOR_MODE: P->is32bitMode = (statusReg.s32.ts_sx == 0);#if defined(SIM_MIPS32) ASSERT(IS_SUPERV_SEG(P->PC));#endif break; case USER_MODE: P->is32bitMode = (statusReg.s32.ts_ux == 0);#if defined(SIM_MIPS32) ASSERT( IS_KUSEG(P->PC));#endif break; default: ASSERT(0); } SIM_DEBUG_DETAIL(('i', "CPUMode" , P->myNum, " going to %d PC=0x%llx SR=%x erl=%d exl=%d ksu=%d 32bit=%d\n", P->cpuMode, (uint64)P->PC, P->CP0[C0_SR], statusReg.s32.ts_erl,statusReg.s32.ts_exl, statusReg.s32.ts_ksu,P->is32bitMode));}extern int numLLactive;extern PA LLAddrs[MIPSY_MAX_CPUS];/***************************************************************** * All cop0 instructions are handled here. *****************************************************************/intExecuteC0Instruction(CPUState *P, Inst instr){ uint rs = RS(instr); uint rt = RT(instr); uint rd = RD(instr); /* Handle the rs instructions */ if (!IS_KERNEL_MODE(P)) { /* CP0 is unusable only in kernel and or when the cu0 bit is set. */ CauseReg causeReg; StatusReg statusReg; statusReg.ts_data = P->CP0[C0_SR]; if (!statusReg.s32.ts_cu0) { causeReg.tc_data = P->CP0[C0_CAUSE]; causeReg.s32.tc_ce = 0; P->CP0[C0_CAUSE] = causeReg.tc_data; EXCEPTION(P, EXC_CPU); return C0_CONTINUE; } } switch(rs) { case bc_op: CPUError("BCC0 is not valid\n"); return C0_ILLEGAL_INST; case cfc_op: CPUError("CFC0 is not valid\n"); return C0_ILLEGAL_INST; case ctc_op: CPUError("CTC0 is not valid\n"); return C0_ILLEGAL_INST; case dmfc_op: if (P->is32bitMode) { return C0_ILLEGAL_INST; } /* Fall thru */ case mfc_op: /* update the count on a read */ if (rd == C0_COUNT){ /* If someone writes to count we need to watch the timer. */ P->CP0[C0_COUNT] += (MipsyReadTime(P->myNum) - P->timerCycleCount)/ COUNTER_FREQUENCY_DIVIDER; P->CP0[C0_COUNT] &= 0xffffffff; P->timerCycleCount = MipsyReadTime(P->myNum); } else if (rd == C0_RAND) { unsigned numRandEntries = P->numTlbEntries - P->CP0[C0_TLBWIRED]; P->CP0[C0_RAND] = P->CP0[C0_TLBWIRED] + ((uint32)MipsyReadTime(P->myNum)) % numRandEntries; } if (rs == mfc_op) { #if defined(SIM_MIPS64) if (P->CP0[rd] == 0x0000000080000000LL) { /* WARNING! This is a hack to fix a compiler bug in 7.2. It assigns 0x0000000080000000 rather than 0xffffffff80000000 */ P->R[rt] = 0xffffffff80000000LL; } else { P->R[rt] = (Reg)(Reg32_s)P->CP0[rd]; }#else P->R[rt] = (Reg)(Reg32_s)P->CP0[rd];#endif } else { P->R[rt] = P->CP0[rd]; } break; case dmtc_op: if (P->is32bitMode) { return C0_ILLEGAL_INST; } /* Fall thru */ case mtc_op: if (rs == dmtc_op) { WriteC0Register(P, rd, P->R[rt], 1); } else { WriteC0Register(P, rd, (Reg32_s)(P->R[rt]), 0); } break; default: /* It wasn't any of these, so it's a CO function */ switch(FUNC(instr)) { case rfe_op: /* Treat rfe as an eret for NachOS */ case eret_op: if (UNCACHED_LL_SC) { if (P->LLbit) { numLLactive--; LLAddrs[P->myNum] = (PA) -1; } } /* Note that this is now check for >= instead of just >. This is so we can capture the instruction that causes the transistion in the sample */ if (STATS_VALUE(P->myNum, numInstructions) >= STATS_VALUE(P->myNum, nextInstrSample)) { INST_SAMPLE_EVENT(MipsyReadTime(P->myNum), P->myNum, P->PC); STATS_INC(P->myNum, nextInstrSample, MS_SAMPLE_INSTR_INTERVAL); } P->LLbit = 0; /* This is bad... this is the one case where flow control gets messed up. */ TraceInstruction(P, instr); ExceptionReturn(P); return C0_CONTINUE; case tlbp_op: ProbeTLB(P); break; case tlbr_op: ReadTLBEntry(P); break; case tlbwi_op: if (WriteTLBEntry(P) != SUCCESS) { CPUWarning("WriteTLB failed at %#x on %d\n", P->PC, P->myNum); } else break; case tlbwr_op: if (WriteRandomTLBEntry(P) != SUCCESS) { CPUWarning("WriteRandomTLB failed at %#x on %d\n", P->PC, P->myNum); } else break; default: ASSERT(0); return C0_ILLEGAL_INST; } } return C0_SUCCESS;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -