📄 r4k_cp0.c
字号:
if( GET_ASID( EMP[cpuNum].tlbEntry[idx].Hi ) == CURRENT_ASID(cpuNum) || IS_GLOBAL_HI( EMP[cpuNum].tlbEntry[idx].Hi)) { qc_map_page( cpuNum, idx); }#ifndef EMBRA_USE_QC64 qc_CheckForDuplicate(curEmp, idx);#endif return NORMAL_CODE;}/***************************************************************** * WriteTLBEntry - * Write the indexed TLB entry. The TLB entry pointed at by the * contents of the TLB index register is loaded with the contents * of the EntryHi and EntryLo registers. *****************************************************************/uint Em_WriteTLBEntry(int cpuNum){ IndexReg index; uint res; ASSERT (curEmp->myNum == cpuNum); if (!IS_KERNEL_MODE(curEmp)) { Em_EXCEPTION(cpuNum,EXC_CPU,0); ReenterTC(curEmp); } /* I'm going to assume that the index register is in the correct range. */ index = EMP[cpuNum].CP0[C0_INX]; res = Do_TLB_Write( cpuNum, GET_IDX(index) ); if( res != NORMAL_CODE ) { ReenterTC(&EMP[cpuNum]); /* NOT REACHED */ } return res;}/***************************************************************** * WriteRandomTLBEntry - * Write random TLB entry. The TLB entry pointed at by the contents * of the TLB Random register is loaded with the contents of EntryHi * and EntryLo. * * Technically, the value of the Random register is decremented on * each machine clock cycle and ranges between NWIREDENTRIES * and NTLBENTRIES. * *****************************************************************/uint Em_WriteRandomTLBEntry(int cpuNum){ uint res; unsigned numRandEntries = EMP[cpuNum].numTlbEntries - EMP[cpuNum].CP0[C0_TLBWIRED]; ASSERT (curEmp == &EMP[cpuNum]); if (!IS_KERNEL_MODE(curEmp)) { Em_EXCEPTION(cpuNum,EXC_CPU,0); ReenterTC(curEmp); } res = Do_TLB_Write( cpuNum, (EmbraCpuCycleCount(cpuNum) % numRandEntries) + EMP[cpuNum].CP0[C0_TLBWIRED] ); if( res != NORMAL_CODE ) { ReenterTC(&EMP[cpuNum]); /* NOT REACHED */ } return res;}/***************************************************************** * REFILL_EXCEPTION * ****************************************************************/static void REFILL_EXCEPTION(int cpuNum, int code, int isInstr, VA pc, VA addr, int isXRefill){ if (isInstr) { ITLB_MISS_EVENT(EmbraCpuCycleCount(cpuNum),cpuNum,addr); } else { DTLB_MISS_EVENT(EmbraCpuCycleCount(cpuNum),cpuNum,pc,addr); } Em_EXCEPTION(cpuNum, (isXRefill? XREFILL_FLAG : 0)|REFILL_FLAG|code,0); }/***************************************************************** * EXCEPTION - * This is sort of a major routine. * The CPU should abort the instruction that caused the exception * along with any others in the pipeline. * * The CPU loads the Exception Program Counter (EPC) with a * restart location - either the instruction, or it's predecessor * if it's in the branch delay slot. I check if it's in a branch * delay slot by seeing if the difference between PC and nPC is * greater than one instruction. * * R4000 STATUS REGISTER UPDATE * 1.) don't put in kernel mode is in kernel mode when KSU = 0 or EXL = 1 * or ERL = 1, strictly speaking this is saved/restored by the software b/c * it could either by user or supervisor modes. * 2.) disable interrupts * 3.) goto vector. * 4.) on start cpu loads EPC with restart location... * * * checks BEV in sr. * but need new vector for bootstrap this is currently unused. * * This currently is set as the General Exception Handler(hardware) * Also Watch and FP Control Status Registers not set (would be set * only when their exc occurs) ****************************************************************/void Em_EXCEPTION(int cpuNum, int code, int ce){ StatusReg statusReg; Reg causeReg; VA exceptionBase; uint prettyCode; int refill_flag = code & REFILL_FLAG; int xrefill_flag = code & XREFILL_FLAG; if (!EMP[cpuNum].outOfSlaveLoop) return; /* * To make sure that we never return to the TC from here, * we increment the tcGenCounter. */ if (cpuNum == curEmp->myNum) { tcGenNumber++; exceptionDuringBackdoor = TRUE; } code &= ~(REFILL_FLAG); code &= ~(XREFILL_FLAG); prettyCode = code >> CAUSE_EXCSHIFT; if (DEBUG_INTR()) { LogEntry("EXCEPTION", cpuNum, "code: 0x%x\tintrbits: 0x%x\n", code, EMP[cpuNum].intrBitsPtr[0]); } STAT_TIMER_STOP( trans_timer );/* One more spot where we must set current cpu, because the timers (and perhaps other things) will cause exceptions on processors other than the "current" one. */ /* Get rid of curr_cpu soon.. *//* curr_cpu = cpuNum; *//* curEmp = &EMP[cpuNum];*/#ifdef DEBUG_CP0 { /* Yes more than one bit can be set at once */ char* typeOfInt = ""; int hwIntrBits =(EMP[cpuNum].CP0[C0_CAUSE] & CAUSE_IPMASK)>>CAUSE_IPSHIFT; /* XXX - Hand Copied from kern/ml/SIMMP.c */ if( hwIntrBits & 0x10 ) { typeOfInt = "CLOCK"; } if( hwIntrBits & 0x04 ) { typeOfInt = "DISK or ETHER"; } if( hwIntrBits & 0x20 ) { typeOfInt = "IPI"; } if( code == EXC_CPU ) { CPUPrint("HW_EX %lld %d EXC %d %s CE %d PC 0x%x\n", EmbraCpuCycleCount(cpuNum), cpuNum, code>>2, typeOfInt, ce, EMP[cpuNum].PC); } else { /* Exclude UTLB misses */ if( code == EXC_RMISS || code == EXC_WMISS ) {} else CPUPrint("HW_EX %lld %d EXC %d HWBits 0x%x %-5s %-5s PC 0x%x\n", EmbraCpuCycleCount(cpuNum), cpuNum, code>>2, hwIntrBits, (refill_flag?"U":" "), typeOfInt, EMP[cpuNum].PC); } }#endif STAT_INC( exceptions ); STAT_EXC( exception_type, code>>2, refill_flag?1:0 ); statusReg.ts_data = EMP[cpuNum].CP0[C0_SR]; /* * Set the BD bit, CE, IP, and ExcCode fields of the Cause * register. Along with the BD bit, set the Exception Program * Counter (either to current PC or PC - 4 if BD slot). */ causeReg = EMP[cpuNum].CP0[C0_CAUSE]; causeReg = CAUSE_SET_EXC_NOSHIFT( causeReg, code ); /* Read the branch delay indicator bit from the PC, */ causeReg = CAUSE_SET_BD( causeReg, IN_BD(EMP[cpuNum].PC) ); EMP[cpuNum].CP0[C0_CAUSE] = causeReg; /*Is this exception the first? (we're not in another exception) */ if (statusReg.s32.ts_exl == 0){ if( IN_BD( EMP[cpuNum].PC ) ) { /* Clear the indicator bit to make the PC usable */ EMP[cpuNum].CP0[C0_EPC] = CLEAR_BD(EMP[cpuNum].PC) - INST_SIZE; } else { EMP[cpuNum].CP0[C0_EPC] = EMP[cpuNum].PC; } statusReg.s32.ts_exl = 1; } EMP[cpuNum].CP0[C0_SR] = statusReg.ts_data; /*The cause register is read-only. The hardware is responsible for */ /*clearing it */ if( code == EXC_CPU ) { EMP[cpuNum].CP0[C0_CAUSE] = CAUSE_SET_CE( EMP[cpuNum].CP0[C0_CAUSE], ce ); } else { EMP[cpuNum].CP0[C0_CAUSE] = CAUSE_SET_CE( EMP[cpuNum].CP0[C0_CAUSE], 0 ); } VASSERT( *(unsigned*)K0_TO_MEMADDR(M_FROM_CPU(cpuNum), E_VEC), ("\n%d Took exception before system exception vector written\n" "PC = 0x%08x\n", cpuNum, EMP[cpuNum].PC) ); ASSERT( statusReg.s32.ts_bev == 0); /* are we bootstrapping? */ if (statusReg.s32.ts_bev == 0) { exceptionBase = EXC_VEC_BASE_0; } else { CPUError("Hello Beth and Scott. Is Emmett insane ? \n"); exceptionBase = EXC_VEC_BASE_1; } /* I'm not even checking if it was a RESET EXCEPTION */ /* Exception can be called from places where fp regs are saved (cp0 */ /* and clock callouts) and from places they are not saved (cache */ /* callouts) */ if( refill_flag ) { ASSERT( embra.emode == EMBRA_PAGE || embra.sequential|| EMP[cpuNum].outTC ); EMP[cpuNum].PC = exceptionBase; if (xrefill_flag) { EMP[cpuNum].PC = exceptionBase + XUT_VEC_OFFSET; } } else { ASSERT( embra.emode == EMBRA_PAGE || embra.sequential || EMP[cpuNum].outTC ); EMP[cpuNum].PC = exceptionBase + E_VEC_OFFSET; } UpdateCPUMode(&EMP[cpuNum]); ASSERT (EMP[cpuNum].cpuMode == KERNEL_MODE); { /* * really important: * In the case of an interrupt, ipi, ... an exception * can be raised on cpuNum while it is not curEmp!!! */ EmbraState *backup = curEmp; curEmp = &EMP[cpuNum]; if (refill_flag) { UTLB_EVENT(); } else { EXC_EVENT(prettyCode); } curEmp = backup; } /* * Bill 1 cycle for the exception itself. This is really * important as it matches MIPSY's handling of exceptions */ EMP[cpuNum].cycleCountdown--; }/* Called from emitted code to raise breakpoint and syscall (others?) *//* exceptions */void Em_RaiseEXCEPTION(int cpuNum, int code, int ce){ Em_EXCEPTION(cpuNum, code, ce); ReenterTC(&EMP[cpuNum]); /* NOT REACHED */}/*R4000*********************************************************** * 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 Em_ExceptionReturn(int cpuNum){ StatusReg statusReg; VA restoreAddr; ASSERT( curEmp == &EMP[cpuNum]); if (!IS_KERNEL_MODE(curEmp)) { Em_EXCEPTION(cpuNum,EXC_CPU,0); ReenterTC(curEmp); } if (DEBUG_INTR()) { LogEntry("CPUMode", cpuNum, " ERET at %x \n",curEmp->PC); }/* Annotations:*/ ASSERT (curEmp->myNum == cpuNum); RFE_EVENT(); statusReg.ts_data = EMP[cpuNum].CP0[C0_SR]; if (statusReg.s32.ts_erl){ restoreAddr = EMP[cpuNum].CP0[C0_ERROR_EPC]; EMP[cpuNum].PC = restoreAddr; statusReg.s32.ts_erl = 0; } else { restoreAddr = EMP[cpuNum].CP0[C0_EPC]; EMP[cpuNum].PC = restoreAddr; statusReg.s32.ts_exl = 0; } EMP[cpuNum].CP0[C0_SR] = statusReg.ts_data; UpdateCPUMode(&EMP[cpuNum]); EMP[cpuNum].LLAddr = 0; EMP[cpuNum].LLBit = 0; /* * Increment the cycle count, since the ERET * instruction actually takes one cycle */ EMP[cpuNum].cycleCountdown--;/* check for interrupts before we leave...*/ if (Update_And_Check_Interrupts(cpuNum,0)){ } ReenterTC(&EMP[cpuNum]);}/***************************************************************** * RestoreFromException * Restore contol to a process that an exception preempted. This * restores the previous interrumpt mask and kernel/user mode bits * in the status register, moves the old to the previous, and keeps * the old unchanged. ****************************************************************/void Em_RestoreFromException( int cpuNum , VA restoreAddr ){ CPUError("Embra:cp0 rfe on cpu %i, curEmp->myNum=%i, ~ERT on R4000!\n", cpuNum,curEmp->myNum); }/***************************************************************** * Em_MoveFromC0 ****************************************************************/uintEm_MoveFromC0(int cpuNum, Inst instr){ unsigned gp_reg = rt(instr); /* dest */ unsigned co_reg = rd(instr); /* source */ /* ASSERT( gp_reg < 32 && co_reg < 32 );*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -