#include "stdafx.h" #include "TLB.h" #include #include #include #include #include CTLB::CTLB(CMipsMemoryVM & MMU, CRegisters & Reg, CRecompiler *& Recomp) : m_MMU(MMU), m_Reg(Reg), m_Recomp(Recomp), m_PrivilegeMode(PrivilegeMode_Kernel), m_AddressSize32bit(true) { WriteTrace(TraceTLB, TraceDebug, "Start"); memset(m_tlb, 0, sizeof(m_tlb)); memset(m_FastTlb, 0, sizeof(m_FastTlb)); Reset(true); WriteTrace(TraceTLB, TraceDebug, "Done"); } CTLB::~CTLB() { WriteTrace(TraceTLB, TraceDebug, "Start"); WriteTrace(TraceTLB, TraceDebug, "Done"); } void CTLB::Reset(bool InvalidateTLB) { uint32_t count; for (count = 0; count < 64; count++) { m_FastTlb[count].ValidEntry = false; } if (InvalidateTLB) { for (count = 0; count < 32; count++) { m_tlb[count].EntryDefined = false; } } else { for (count = 0; count < 32; count++) { SetupTLB_Entry(count, false); } } COP0StatusChanged(); } bool CTLB::AddressDefined(uint64_t VAddr, bool & Dirty) { Dirty = true; MemorySegment Segment = VAddrMemorySegment(VAddr); if (Segment == MemorySegment_Mapped) { for (uint32_t i = 0; i < 64; i++) { if (!m_FastTlb[i].GLOBAL || !m_FastTlb[i].ValidEntry || VAddr < m_FastTlb[i].VSTART || VAddr > m_FastTlb[i].VEND + 1) { continue; } if (!m_FastTlb[i].VALID) { return true; } Dirty = m_FastTlb[i].DIRTY; return true; } return false; } return false; } TLB_ENTRY & CTLB::TlbEntry(int32_t Entry) { return m_tlb[Entry]; } void CTLB::Probe() { WriteTrace(TraceTLB, TraceDebug, "Start"); m_Reg.INDEX_REGISTER = 0x80000000; for (uint32_t i = 0; i < 32; i++) { if (!m_tlb[i].EntryDefined) { continue; } const COP0EntryHi & TlbEntryHiValue = m_tlb[i].EntryHi; uint64_t Mask = ~m_tlb[i].PageMask.Mask << 13; uint64_t TlbValueMasked = TlbEntryHiValue.Value & Mask; uint64_t EntryHiMasked = m_Reg.ENTRYHI_REGISTER.Value & Mask; if (TlbValueMasked != EntryHiMasked || TlbEntryHiValue.R() != m_Reg.ENTRYHI_REGISTER.R() || (m_tlb[i].EntryLo0.GLOBAL == 0 || m_tlb[i].EntryLo1.GLOBAL == 0) && TlbEntryHiValue.ASID() != m_Reg.ENTRYHI_REGISTER.ASID()) { continue; } m_Reg.INDEX_REGISTER = i; uint32_t FastIndx = i << 1; m_FastTlb[FastIndx].Probed = true; m_FastTlb[FastIndx + 1].Probed = true; break; } WriteTrace(TraceTLB, TraceDebug, "Done"); } void CTLB::ReadEntry() { uint32_t Index = m_Reg.INDEX_REGISTER & 0x1F; m_Reg.PAGE_MASK_REGISTER.Value = m_tlb[Index].PageMask.Value; m_Reg.ENTRYHI_REGISTER.Value = (m_tlb[Index].EntryHi.Value & ~m_tlb[Index].PageMask.Value); m_Reg.ENTRYLO0_REGISTER.Value = m_tlb[Index].EntryLo0.Value; m_Reg.ENTRYLO1_REGISTER.Value = m_tlb[Index].EntryLo1.Value; } void CTLB::WriteEntry(uint32_t Index, bool Random) { WriteTrace(TraceTLB, TraceDebug, "%02d %d %llX %llX %llX %llX", Index, Random, m_Reg.PAGE_MASK_REGISTER, m_Reg.ENTRYHI_REGISTER, m_Reg.ENTRYLO0_REGISTER, m_Reg.ENTRYLO1_REGISTER); // Check to see if entry is unmapping itself if (m_tlb[Index].EntryDefined) { uint32_t FastIndx = Index << 1; if (m_Reg.m_PROGRAM_COUNTER >= m_FastTlb[FastIndx].VSTART && m_Reg.m_PROGRAM_COUNTER < m_FastTlb[FastIndx].VEND && m_FastTlb[FastIndx].ValidEntry && m_FastTlb[FastIndx].VALID) { WriteTrace(TraceTLB, TraceDebug, "Ignored PC: %X VAddr Start: %llX VEND: %llX", m_Reg.m_PROGRAM_COUNTER, m_FastTlb[FastIndx].VSTART, m_FastTlb[FastIndx].VEND); return; } if (m_Reg.m_PROGRAM_COUNTER >= m_FastTlb[FastIndx + 1].VSTART && m_Reg.m_PROGRAM_COUNTER < m_FastTlb[FastIndx + 1].VEND && m_FastTlb[FastIndx + 1].ValidEntry && m_FastTlb[FastIndx + 1].VALID) { WriteTrace(TraceTLB, TraceDebug, "Ignored PC: %X VAddr Start: %X VEND: %X", m_Reg.m_PROGRAM_COUNTER, m_FastTlb[FastIndx + 1].VSTART, m_FastTlb[FastIndx + 1].VEND); return; } } // Reset old addresses if (m_tlb[Index].EntryDefined) { for (uint32_t FastIndx = Index << 1; FastIndx <= (Index << 1) + 1; FastIndx++) { if (!m_FastTlb[FastIndx].ValidEntry) { continue; } if (!m_FastTlb[FastIndx].VALID) { continue; } if (m_tlb[Index].PageMask.Value == m_Reg.PAGE_MASK_REGISTER.Value && m_tlb[Index].EntryHi.Value == m_Reg.ENTRYHI_REGISTER.Value) { if (FastIndx == (Index << 1) && m_tlb[Index].EntryLo0.Value == m_Reg.ENTRYLO0_REGISTER.Value) { continue; } if (FastIndx != (Index << 1) && m_tlb[Index].EntryLo1.Value == m_Reg.ENTRYLO1_REGISTER.Value) { continue; } } TLB_Unmaped(m_FastTlb[FastIndx].VSTART, m_FastTlb[FastIndx].Length); } } // Fill in m_tlb entry bool Gloabl = m_Reg.ENTRYLO0_REGISTER.GLOBAL & m_Reg.ENTRYLO1_REGISTER.GLOBAL; m_tlb[Index].PageMask.Value = m_Reg.PAGE_MASK_REGISTER.Value & 0x01554000; m_tlb[Index].PageMask.Value |= m_tlb[Index].PageMask.Value >> 1; m_tlb[Index].EntryHi = m_Reg.ENTRYHI_REGISTER; m_tlb[Index].EntryLo0 = m_Reg.ENTRYLO0_REGISTER; m_tlb[Index].EntryLo0.PFN = m_Reg.ENTRYLO0_REGISTER.PFN & 0xFFFFF; m_tlb[Index].EntryLo0.GLOBAL = Gloabl; m_tlb[Index].EntryLo1 = m_Reg.ENTRYLO1_REGISTER; m_tlb[Index].EntryLo1.PFN = m_Reg.ENTRYLO1_REGISTER.PFN & 0xFFFFF; m_tlb[Index].EntryLo1.GLOBAL = Gloabl; m_tlb[Index].EntryDefined = true; SetupTLB_Entry(Index, Random); if (g_Debugger != nullptr) { g_Debugger->TLBChanged(); } } void CTLB::COP0StatusChanged(void) { m_PrivilegeMode = m_Reg.STATUS_REGISTER.PrivilegeMode; switch (m_PrivilegeMode) { case PrivilegeMode_Kernel: m_AddressSize32bit = m_Reg.STATUS_REGISTER.KernelExtendedAddressing == 0; break; case PrivilegeMode_Supervisor: m_AddressSize32bit = m_Reg.STATUS_REGISTER.SupervisorExtendedAddressing == 0; break; case PrivilegeMode_User: m_AddressSize32bit = m_Reg.STATUS_REGISTER.UserExtendedAddressing == 0; break; } } void CTLB::SetupTLB_Entry(uint32_t Index, bool Random) { // Fix up fast TLB entries if (!m_tlb[Index].EntryDefined) { return; } uint32_t FastIndx = Index << 1; if (m_FastTlb[FastIndx].VALID) { TLB_Unmaped(m_FastTlb[FastIndx].VSTART, m_FastTlb[FastIndx].Length); } m_FastTlb[FastIndx].Length = (uint32_t)((m_tlb[Index].PageMask.Mask << 12) + 0xFFF); m_FastTlb[FastIndx].Region = (uint8_t)m_tlb[Index].EntryHi.R(); m_FastTlb[FastIndx].VSTART = (uint32_t)(m_tlb[Index].EntryHi.VPN2() << 13); m_FastTlb[FastIndx].VEND = m_FastTlb[FastIndx].VSTART + m_FastTlb[FastIndx].Length; m_FastTlb[FastIndx].PHYSSTART = (uint32_t)(m_tlb[Index].EntryLo0.PFN << 12); m_FastTlb[FastIndx].PHYSEND = m_FastTlb[FastIndx].PHYSSTART + m_FastTlb[FastIndx].Length; m_FastTlb[FastIndx].VALID = m_tlb[Index].EntryLo0.V != 0; m_FastTlb[FastIndx].DIRTY = m_tlb[Index].EntryLo0.D != 0; m_FastTlb[FastIndx].GLOBAL = m_tlb[Index].EntryLo0.GLOBAL & m_tlb[Index].EntryLo1.GLOBAL; m_FastTlb[FastIndx].ValidEntry = false; m_FastTlb[FastIndx].Random = Random; m_FastTlb[FastIndx].Probed = false; FastIndx = (Index << 1) + 1; if (m_FastTlb[FastIndx].VALID) { TLB_Unmaped(m_FastTlb[FastIndx].VSTART, m_FastTlb[FastIndx].Length); } m_FastTlb[FastIndx].Length = (uint32_t)((m_tlb[Index].PageMask.Mask << 12) + 0xFFF); m_FastTlb[FastIndx].Region = (uint8_t)m_tlb[Index].EntryHi.R(); m_FastTlb[FastIndx].VSTART = (uint32_t)(m_tlb[Index].EntryHi.VPN2() << 13) + (m_FastTlb[FastIndx].Length + 1); m_FastTlb[FastIndx].VEND = m_FastTlb[FastIndx].VSTART + m_FastTlb[FastIndx].Length; m_FastTlb[FastIndx].PHYSSTART = (uint32_t)m_tlb[Index].EntryLo1.PFN << 12; m_FastTlb[FastIndx].PHYSEND = m_FastTlb[FastIndx].PHYSSTART + m_FastTlb[FastIndx].Length; m_FastTlb[FastIndx].VALID = m_tlb[Index].EntryLo1.V != 0; m_FastTlb[FastIndx].DIRTY = m_tlb[Index].EntryLo1.D != 0; m_FastTlb[FastIndx].GLOBAL = m_tlb[Index].EntryLo0.GLOBAL & m_tlb[Index].EntryLo1.GLOBAL; m_FastTlb[FastIndx].ValidEntry = false; m_FastTlb[FastIndx].Random = Random; m_FastTlb[FastIndx].Probed = false; // Test both entries to see if they are valid for (FastIndx = Index << 1; FastIndx <= (Index << 1) + 1; FastIndx++) { if (!m_FastTlb[FastIndx].VALID) { m_FastTlb[FastIndx].ValidEntry = true; continue; } if ((m_FastTlb[FastIndx].VSTART & 0x80000000) == 0 && m_FastTlb[FastIndx].Region != 0) { continue; } if ((m_FastTlb[FastIndx].VSTART & 0x80000000) != 0 && m_FastTlb[FastIndx].Region != 3) { continue; } if (m_FastTlb[FastIndx].VEND <= m_FastTlb[FastIndx].VSTART) { continue; } if (m_FastTlb[FastIndx].VSTART >= 0x80000000 && m_FastTlb[FastIndx].VEND <= 0xBFFFFFFF) { continue; } if (m_FastTlb[FastIndx].PHYSEND > 0x1FFFFFFF) { continue; } // Map the new m_tlb entry for reading and writing m_FastTlb[FastIndx].ValidEntry = true; m_MMU.TLB_Mapped(m_FastTlb[FastIndx].VSTART, m_FastTlb[FastIndx].Length, m_FastTlb[FastIndx].PHYSSTART, !m_FastTlb[FastIndx].DIRTY); } } void CTLB::TLB_Unmaped(uint32_t VAddr, uint32_t Len) { m_MMU.TLB_Unmaped(VAddr, Len); if (m_Recomp && bSMM_TLB()) { m_Recomp->ClearRecompCode_Virt((uint32_t)VAddr, Len, CRecompiler::Remove_TLB); } } bool CTLB::VAddrToPAddr(uint64_t VAddr, uint32_t & PAddr, bool & MemoryUnused) { MemoryUnused = false; if (b32BitCore() && (uint64_t)((int32_t)VAddr) != VAddr) { MemoryUnused = true; return false; } MemorySegment Segment = VAddrMemorySegment(VAddr); if (Segment == MemorySegment_Mapped) { for (uint32_t i = 0; i < 32; i++) { if (m_tlb[i].EntryLo0.GLOBAL == 0) { continue; } if ((VAddr & 0xE000000000000000) != ((uint64_t)m_tlb[i].EntryHi.R() << 62)) { continue; } uint64_t PageMask = (m_tlb[i].PageMask.Mask << 12); uint64_t AddressMaskHi = ~(PageMask | 0x1fff) & 0xFFFFFFFFFF; uint64_t AddressRegion = ((uint64_t)m_tlb[i].EntryHi.VPN2() << 13); if ((VAddr & AddressMaskHi) != AddressRegion) { continue; } uint64_t AddressSelect = ((m_tlb[i].PageMask.Mask << 12) | 0xfff) + 1; COP0EntryLo EntryLo = (VAddr & AddressSelect) != 0 ? m_tlb[i].EntryLo1 : m_tlb[i].EntryLo0; PAddr = (uint32_t)((EntryLo.PFN << 12) + (VAddr & (PageMask | 0x1fff))); return true; } return false; } if (Segment == MemorySegment_Unused) { MemoryUnused = true; return false; } if (Segment == MemorySegment_Direct32 || Segment == MemorySegment_Cached32) { PAddr = VAddr & 0x7FFFFFFF; return true; } return false; } MemorySegment CTLB::VAddrMemorySegment(uint64_t VAddr) { if (m_AddressSize32bit) { if ((uint64_t)((int32_t)VAddr) != VAddr) { return MemorySegment_Unused; } uint32_t VAddr32 = (uint32_t)VAddr; if (m_PrivilegeMode == PrivilegeMode_Kernel) { if (VAddr32 <= 0x7fffffff) //kuseg { return MemorySegment_Mapped; } if (VAddr32 <= 0x9fffffff) //kseg0 { return MemorySegment_Cached32; } if (VAddr32 <= 0xbfffffff) //kseg1 { return MemorySegment_Direct32; } if (VAddr32 <= 0xdfffffff) //ksseg { return MemorySegment_Mapped; } if (VAddr32 <= 0xffffffff) //kseg3 { return MemorySegment_Mapped; } } else if (m_PrivilegeMode == PrivilegeMode_Supervisor) { if (VAddr32 <= 0x7fffffff) //suseg { return MemorySegment_Mapped; } if (VAddr32 <= 0xbfffffff) { return MemorySegment_Unused; } if (VAddr32 <= 0xdfffffff) //sseg { return MemorySegment_Mapped; } } else if (m_PrivilegeMode == PrivilegeMode_User) { if (VAddr32 <= 0x7fffffff) //useg { return MemorySegment_Mapped; } } return MemorySegment_Unused; } if (m_PrivilegeMode == PrivilegeMode_Kernel) { if (VAddr <= 0x000000ffffffffffull) //xkuseg { return MemorySegment_Mapped; } if (VAddr <= 0x3fffffffffffffffull) { return MemorySegment_Unused; } if (VAddr <= 0x400000ffffffffffull) //xksseg { return MemorySegment_Mapped; } if (VAddr <= 0x7fffffffffffffffull) { return MemorySegment_Unused; } if (VAddr <= 0x80000000ffffffffull) //xkphys* { return MemorySegment_Cached32; } if (VAddr <= 0x87ffffffffffffffull) { return MemorySegment_Unused; } if (VAddr <= 0x88000000ffffffffull) { return MemorySegment_Cached32; //xkphys* } if (VAddr <= 0x8fffffffffffffffull) { return MemorySegment_Unused; } if (VAddr <= 0x90000000ffffffffull) //xkphys* { return MemorySegment_Direct32; } if (VAddr <= 0x97ffffffffffffffull) { return MemorySegment_Unused; } if (VAddr <= 0x98000000ffffffffull) //xkphys* { return MemorySegment_Cached32; } if (VAddr <= 0x9fffffffffffffffull) { return MemorySegment_Unused; } if (VAddr <= 0xa0000000ffffffffull) //xkphys* { return MemorySegment_Cached32; } if (VAddr <= 0xa7ffffffffffffffull) { return MemorySegment_Unused; } if (VAddr <= 0xa8000000ffffffffull) //xkphys* { return MemorySegment_Cached32; } if (VAddr <= 0xafffffffffffffffull) { return MemorySegment_Unused; } if (VAddr <= 0xb0000000ffffffffull) //xkphys* { return MemorySegment_Cached32; } if (VAddr <= 0xb7ffffffffffffffull) { return MemorySegment_Unused; } if (VAddr <= 0xb8000000ffffffffull) //xkphys* { return MemorySegment_Cached32; } if (VAddr <= 0xbfffffffffffffffull) { return MemorySegment_Unused; } if (VAddr <= 0xc00000ff7fffffffull) //xkseg { return MemorySegment_Mapped; } if (VAddr <= 0xffffffff7fffffffull) { return MemorySegment_Unused; } if (VAddr <= 0xffffffff9fffffffull) //ckseg0 { return MemorySegment_Cached32; } if (VAddr <= 0xffffffffbfffffffull) //ckseg1 { return MemorySegment_Direct32; } if (VAddr <= 0xffffffffdfffffffull) //ckseg2 { return MemorySegment_Mapped; } if (VAddr <= 0xffffffffffffffffull) //ckseg3 { return MemorySegment_Mapped; } } else if (m_PrivilegeMode == PrivilegeMode_Kernel) { if (VAddr <= 0x000000ffffffffffull) //xsuseg { return MemorySegment_Mapped; } if (VAddr <= 0x3fffffffffffffffull) { return MemorySegment_Unused; } if (VAddr <= 0x400000ffffffffffull) //xsseg { return MemorySegment_Mapped; } if (VAddr <= 0xffffffffbfffffffull) { return MemorySegment_Unused; } if (VAddr <= 0xffffffffdfffffffull) //csseg { return MemorySegment_Mapped; } if (VAddr <= 0xffffffffffffffffull) { return MemorySegment_Unused; } } else if (m_PrivilegeMode == PrivilegeMode_User) { if (VAddr <= 0x000000ffffffffffull) { return MemorySegment_Mapped; //xuseg } } return MemorySegment_Unused; } bool CTLB::PAddrToVAddr(uint32_t PAddr, uint32_t & VAddr, uint32_t & Index) { for (int i = Index; i < 64; i++) { if (m_FastTlb[i].ValidEntry == false) { continue; } if (PAddr >= m_FastTlb[i].PHYSSTART && PAddr < m_FastTlb[i].PHYSEND) { VAddr = (uint32_t)(m_FastTlb[i].VSTART + (PAddr - m_FastTlb[i].PHYSSTART)); Index = i + 1; return true; } } return false; } void CTLB::RecordDifference(CLog & LogFile, const CTLB & rTLB) { for (int i = 0, n = sizeof(m_tlb) / sizeof(m_tlb[0]); i < n; i++) { if (m_tlb[i].EntryDefined != rTLB.m_tlb[i].EntryDefined) { LogFile.LogF("TLB[%d] Defined: %s %s\r\n", i, m_tlb[i].EntryDefined ? "Yes" : "No", rTLB.m_tlb[i].EntryDefined ? "Yes" : "No"); continue; } if (!m_tlb[i].EntryDefined) { continue; } if (m_tlb[i].PageMask.Value != rTLB.m_tlb[i].PageMask.Value) { LogFile.LogF("TLB[%d] PageMask: %X %X\r\n", i, m_tlb[i].PageMask.Value, rTLB.m_tlb[i].PageMask.Value); } if (m_tlb[i].EntryHi.Value != rTLB.m_tlb[i].EntryHi.Value) { LogFile.LogF("TLB[%d] EntryHi: %X %X\r\n", i, m_tlb[i].EntryHi.Value, rTLB.m_tlb[i].EntryHi.Value); } if (m_tlb[i].EntryLo0.Value != rTLB.m_tlb[i].EntryLo0.Value) { LogFile.LogF("TLB[%d] EntryLo0: %X %X\r\n", i, m_tlb[i].EntryLo0.Value, rTLB.m_tlb[i].EntryLo0.Value); } if (m_tlb[i].EntryLo1.Value != rTLB.m_tlb[i].EntryLo1.Value) { LogFile.LogF("TLB[%d] EntryLo1: %X %X\r\n", i, m_tlb[i].EntryLo1.Value, rTLB.m_tlb[i].EntryLo1.Value); } } } bool CTLB::operator==(const CTLB & rTLB) const { const size_t n = sizeof(m_tlb) / sizeof(m_tlb[0]); for (size_t i = 0; i < n; i++) { if (m_tlb[i].EntryDefined != rTLB.m_tlb[i].EntryDefined) { return false; } if (!m_tlb[i].EntryDefined) { continue; } if (m_tlb[i].PageMask.Value != rTLB.m_tlb[i].PageMask.Value || m_tlb[i].EntryHi.Value != rTLB.m_tlb[i].EntryHi.Value || m_tlb[i].EntryLo0.Value != rTLB.m_tlb[i].EntryLo0.Value || m_tlb[i].EntryLo1.Value != rTLB.m_tlb[i].EntryLo1.Value) { return false; } } return true; } bool CTLB::operator!=(const CTLB & rTLB) const { return !(*this == rTLB); }