📄 r4k_cp0.c
字号:
case CBIT_UPDATE: if (IS_R10000(P)) { break; /* fall thru to error, doesn't work on R10000 */ } case CBIT_NONCOHERENT: case CBIT_EXCLUSIVE: case CBIT_EXCLUSIVE_WRITE: { Reg64 offset = XKPHYS_ONE_PAGE_OFFSET(vAddr); if (XKPHYS_INVALID_OFFSET(offset)) goto addrErr; *pAddr = offset; /* XXX - NEED TO ACCESS FLAVOR */ return SUCCESS; } case CBIT_UNCACHED: case CBIT_UNCACHED_ACCEL: { uint flavor = XKPHYS_UNCACHED_FLAVOR(vAddr); Reg64 offset = XKPHYS_FOUR_PAGE_OFFSET(vAddr); if (XKPHYS_INVALID_OFFSET(offset)) goto addrErr; if (!IS_R10000(P) && (flavor != 0)) goto addrErr; *pAddr = offset; *tlbFlavor = TLB_UNCACHED; if (cache_algorithm == CBIT_UNCACHED_ACCEL) { *tlbFlavor |= TLB_ACCELERATED; } return SUCCESS; } default: /* invalid cache alogrithm fall thru to error */ break; } goto addrErr; } else if (region == 1) { /* Supervisor region - only available in 64bit mode */ if (IN_32BIT_MODE(P) || ((sr_reg & SR_KSU_USR) && !IS_KERNEL_MODE(P)) || HAS_BAD_VADDR_BITS(vAddr)) goto addrErr; if (IS_R10000(P) && !(sr_reg & SR_SX)) goto addrErr; /* Fall thru to TLB lookup */#endif } /* Check TLB */ VPN2 = GET_VPN2(vAddr); tlbIndex = TLBLookup(P, region, VPN2, myASID); if (tlbIndex) { int szEntry; /* We have a matching VPN and ASID - see if it is valid */ tlbIndex--; szEntry = P->tlbEntrySize[tlbIndex]; /* Which lo register? */ if (vAddr & PgSz[szEntry].loBit) lo_reg = P->tlbEntry[tlbIndex].Lo1; else lo_reg = P->tlbEntry[tlbIndex].Lo0; if (IS_VALID(lo_reg)) { /* Check if the page is dirty or we are reading */ if ( IS_DIRTY(lo_reg) || !(writing)) { /* Everything is cool - form the address */#ifdef notdef /* For performance, disable this check by default */ int cache_algorithm = GET_CACHE_ALGOR(lo_reg); if (!((cache_algorithm == CBIT_EXCLUSIVE) || (cache_algorithm == CBIT_EXCLUSIVE_WRITE))) { CPUWarning("Unsupported TLB cache algorithm (%d) for address 0x%llx by cpu %d at PC 0x%llx\n", cache_algorithm, (uint64)vAddr, P->myNum, (uint64)P->PC); }#endif /* * XXX TO_DO non-cacheable support eventualy support table * which has 8 modes. * if (!IS_CACHEABLE(lo_reg){ * *tlbFlavor = TLB_UNCACHED; * } */ *pAddr = (((GET_PFN(lo_reg)&SZ2MASK(szEntry))*4*1024) | (vAddr & PgSz[szEntry].offset_mask)); return SUCCESS; } else { /* TLB MODIFICATION */ /* Page is not dirty and we want to write */ /* Set up the CTxt register !!! */ /* EPC set up in EXCEPTION, badVaddr and context */ /* stored in macro */ if (isPrefetch) return FAILURE; contextReg.tc_data = P->CP0[C0_CTXT]; contextReg.s32.tc_badvpn = VPN2; xcontextReg.tc_data = P->CP0[C0_XCTXT]; xcontextReg.s64.tc_region = region;#ifndef BIG_BIT_FIELD_BROKEN xcontextReg.s64.tc_badvpn = VPN2;#else xcontextReg.s64.tc_badvpn_hi3 = VPN2>>28; xcontextReg.s64.tc_badvpn_lo28 = VPN2;#endif tlbhi = (((Reg)region << TLBHI_REGIONSHIFT) | (VPN2 << TLBHI_VPN2SHIFT) | ((Reg)myASID << TLBHI_PIDSHIFT)) ; RECORD_EXCEPTION(P, EXC_MOD, E_VEC, vAddr, tlbhi,contextReg.tc_data, xcontextReg.tc_data); return FAILURE; } } else { /* TLB INVALID */ if (isPrefetch) return FAILURE; contextReg.tc_data = P->CP0[C0_CTXT]; contextReg.s32.tc_badvpn = VPN2; xcontextReg.tc_data = P->CP0[C0_XCTXT]; xcontextReg.s64.tc_region = region;#ifndef BIG_BIT_FIELD_BROKEN xcontextReg.s64.tc_badvpn = VPN2;#else xcontextReg.s64.tc_badvpn_hi3 = VPN2>>28; xcontextReg.s64.tc_badvpn_lo28 = VPN2;#endif tlbhi = (((Reg)region << TLBHI_REGIONSHIFT) | (VPN2 << TLBHI_VPN2SHIFT) | ((Reg)myASID << TLBHI_PIDSHIFT)) ; RECORD_EXCEPTION(P, (writing ? EXC_WMISS : EXC_RMISS),E_VEC, vAddr,tlbhi,contextReg.tc_data, xcontextReg.tc_data); return FAILURE; } } /* TLB REFILL * Since there were no matching VPN2s, there is a TLB refill exception. * First put the VPN2 and ASID of the non-matching address in Hi. * The BadVAddr and Context registers also need to be set. * 1.) set TLBL or TLBS(store only) code in cause register * use EPC and BD bit in cause reg, inst or load, or store. * 2.) BadVAddr, Context, XContect and EntryHi hold the vAddr * that failed. Entry HI also has ASID. EPC pts to last instruction * take care of branches. */ if (isPrefetch) return FAILURE; contextReg.tc_data = P->CP0[C0_CTXT]; contextReg.s32.tc_badvpn = VPN2; xcontextReg.tc_data = P->CP0[C0_XCTXT]; xcontextReg.s64.tc_region = region;#ifndef BIG_BIT_FIELD_BROKEN xcontextReg.s64.tc_badvpn = VPN2;#else xcontextReg.s64.tc_badvpn_hi3 = VPN2>>28; xcontextReg.s64.tc_badvpn_lo28 = VPN2;#endif tlbhi = (((Reg)region << TLBHI_REGIONSHIFT) | (VPN2 << TLBHI_VPN2SHIFT) | ((Reg)myASID << TLBHI_PIDSHIFT)) ; if (isIfetch) { ITLB_MISS_EVENT(MipsyReadTime(P->myNum), P->myNum, vAddr); } else { DTLB_MISS_EVENT(MipsyReadTime(P->myNum), P->myNum, P->PC, vAddr); } if (sr_reg & SR_EXL) { RECORD_EXCEPTION(P, (writing ? EXC_WMISS : EXC_RMISS),E_VEC,vAddr, tlbhi,contextReg.tc_data, xcontextReg.tc_data); } else { if (((region == 0) && (sr_reg & SR_UX)) || ((region == 3) && (sr_reg & SR_KX)) || ((region == 1) && (sr_reg & SR_SX))) { /* Should pass XUT_VEC to RECORD below, but this isn't currently used. */ } RECORD_UTLBEXCEPTION(P, writing ? EXC_WMISS : EXC_RMISS, UT_VEC, vAddr, tlbhi, contextReg.tc_data, xcontextReg.tc_data); } return FAILURE; addrErr: if (isPrefetch) return FAILURE; /* Illegal address - generate an address error */ RECORD_EXCEPTION(P, (writing ? EXC_WADE : EXC_RADE), E_VEC, vAddr,P->CP0[C0_TLBHI], P->CP0[C0_CTXT], P->CP0[C0_XCTXT]); return FAILURE; bdoor: { SimMagic_accesstype a; if (!IS_KERNEL_MODE(P)) { CPUWarning("Accessing bdoor while not in kernel mode (PC %#x RA %#x)\n", P->PC, P->R[REG_RA]); } a = SimMagic_kseg1_accesstype(vAddr); if (a == SIMMAGIC_UNCACHED) { *tlbFlavor = TLB_UNCACHED; *pAddr = vAddr; } else if (a == SIMMAGIC_UNCACHED_ACCELERATED) { *tlbFlavor = TLB_UNCACHED|TLB_ACCELERATED; *pAddr = vAddr; } else { uint flag; /* a == SIMMAGIC_DIRECT */ if (!RegistryIsInRange(vAddr, bdoorAddr, &flag)) { /* Raise an address error on a bad backdoor address. */ if (!isPrefetch) { CPUWarning("MIPSY: Bad bdoor reference to 0x%x from %#x RA %#x\n", vAddr, P->PC, P->R[31]); /* ASSERT(0); */ RECORD_EXCEPTION(P, (writing ? EXC_WADE : EXC_RADE), E_VEC, vAddr, P->CP0[C0_TLBHI], P->CP0[C0_CTXT], P->CP0[C0_CTXT]); } return FAILURE; } if (flag & REG_DATA) { *tlbFlavor = TLB_BDOOR_DATA; } else { ASSERT(flag & REG_FUNC); *tlbFlavor = TLB_BDOOR_FUNC; } SIM_DEBUG(('b', "MIPSY: CPU %d Translated %#x to %#x (%s) at pc %#x\n", P->myNum, vAddr, pAddr, (flag & REG_DATA)?"data":"func", P->PC)); } } return SUCCESS;}/* a version of TranslateVirtual intended to be called from the debugger. */Result TranslateVirtualNoSideeffect(CPUState *P, VA vAddr, PA *pAddr){ unsigned tlbIndex; int myASID; Reg64 lo_reg; int region = GET_REGION(vAddr); if (region==2) { *pAddr = XKPHYS_FOUR_PAGE_OFFSET(vAddr); return SUCCESS; } if (region==3 && vAddr >= CKSEG0_START_ADDR) { if (IS_KSEG0(vAddr)) { *pAddr = K0_TO_PHYS(vAddr); return SUCCESS; } } /* fall through and cover 32-bit cases. */ if (!IS_KUSEG(vAddr)) { if (IS_KSEG1(vAddr)) { #ifdef __alpha return FAILURE;#else void *dat; uint flag; *pAddr = (PA)RegistryGetSimFunction(vAddr); if (*pAddr) { SIM_DEBUG(('b', "BDOOR: CPU %d found func %#x at 0x%x (PC %#x RA %#x\n", P->myNum, *pAddr, vAddr, P->PC, P->R[REG_RA])); return SUCCESS; } /* backdoor access */ if (!RegistryIsInRange(vAddr,&dat, &flag) || !(flag & REG_DATA)) { /* Raise an address error on a bad backdoor address. */ return FAILURE; } *pAddr = (PA) dat; return SUCCESS;#endif /* __alpha */ } else if (IS_KSEG0(vAddr)) { *pAddr = K0_TO_PHYS(vAddr); return SUCCESS; } /* KSEG2 references will fall through and be translated normally */ } /* Check TLB */ myASID = GET_ASID(P->CP0[C0_TLBHI]); tlbIndex = TLBLookup(P, region, GET_VPN2(vAddr) , myASID); if (tlbIndex) { int sz; /* We have a matching VPN and ASID - see if it is valid */ tlbIndex--; sz = P->tlbEntrySize[tlbIndex]; /* Which lo register? */ if (vAddr & PgSz[sz].loBit) lo_reg = P->tlbEntry[tlbIndex].Lo1; else lo_reg = P->tlbEntry[tlbIndex].Lo0; if (IS_VALID(lo_reg)) { *pAddr = (((GET_PFN(lo_reg)&SZ2MASK(sz))*4*1024) | (vAddr & PgSz[sz].offset_mask)); return SUCCESS; } /* fall through to failure case if not valid */ } /* OK, let's resort to calling tcl for help */ *pAddr = TclTranslateVirtual(P->myNum, vAddr); if (*pAddr) { return SUCCESS; } return FAILURE;} /***************************************************************** * ProbeTLB - * Probe TLB for a matching entry. The Index register is loaded * with the address of the TLB entry whose contents match the * contents of the Hi register. If no TLB entry matches, the * high order bit of the Index register is set. * * What to do when there are multiple matches was not specified, * so I'm leaving after the first match. * R4000 CHANGED >> No changes. *****************************************************************/static voidProbeTLB(CPUState *P){ int idx; idx = TLBLookup(P, GET_REGION(P->CP0[C0_TLBHI]), GET_VPN2(P->CP0[C0_TLBHI]), GET_ASID(P->CP0[C0_TLBHI])); if (idx) { /* We matched */ P->CP0[C0_INX] = (idx - 1) << TLBINX_INXSHIFT; return; } /* Probe Miss */ P->CP0[C0_INX] = 0x80000000; return;}/***************************************************************** * ReadTLBEntry - * The Hi, Lo0, and Lo1 registers are loaded with the contents * of the TLB entry pointed at by the contents of the TLB Index * register. The results are unspecified if the contents or Index * are greater than the number of TLB entries. * R4000 CHANGED >> G(global) bit from TLB is written into both the lo0 and lo1 * registers. Entry HI and EntryLo registers are loaded with contents * of the TLB entry pointedt at by the index register. As is the * PgMsk register. if index > numtlbentries, undefined behavior. *****************************************************************/static void ReadTLBEntry(CPUState *P){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -