Common: Move VirtualMemory related functionality to core

Also rewrites page fault handling to not use EventSource junk.
This commit is contained in:
Stenzek 2022-12-28 16:53:37 +10:00 committed by refractionpcsx2
parent 1b86a6e6f8
commit 4cf041f6cb
25 changed files with 763 additions and 813 deletions

View File

@ -10,7 +10,6 @@ add_library(common)
# x86emitter sources
target_sources(common PRIVATE
AlignedMalloc.cpp
VirtualMemory.cpp
EventSource.inl
SafeArray.inl
Console.cpp
@ -20,6 +19,7 @@ target_sources(common PRIVATE
Exceptions.cpp
FastJmp.cpp
FileSystem.cpp
General.cpp
Image.cpp
HTTPDownloader.cpp
MemorySettingsInterface.cpp
@ -88,7 +88,6 @@ target_sources(common PRIVATE
MD5Digest.h
MRCHelpers.h
Path.h
PageFaultSource.h
PrecompiledHeader.h
ProgressCallback.h
ReadbackSpinManager.h

39
common/General.cpp Normal file
View File

@ -0,0 +1,39 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "General.h"
// --------------------------------------------------------------------------------------
// PageProtectionMode (implementations)
// --------------------------------------------------------------------------------------
std::string PageProtectionMode::ToString() const
{
std::string modeStr;
if (m_read)
modeStr += "Read";
if (m_write)
modeStr += "Write";
if (m_exec)
modeStr += "Exec";
if (modeStr.empty())
return "NoAccess";
if (modeStr.length() <= 5)
modeStr += "Only";
return modeStr;
}

View File

@ -116,6 +116,14 @@ static __fi PageProtectionMode PageAccess_Any()
return PageProtectionMode().All();
}
struct PageFaultInfo
{
uptr pc;
uptr addr;
};
using PageFaultHandler = bool(*)(const PageFaultInfo& info);
// --------------------------------------------------------------------------------------
// HostSys
// --------------------------------------------------------------------------------------
@ -141,6 +149,12 @@ namespace HostSys
extern void DestroySharedMemory(void* ptr);
extern void* MapSharedMemory(void* handle, size_t offset, void* baseaddr, size_t size, const PageProtectionMode& mode);
extern void UnmapSharedMemory(void* baseaddr, size_t size);
/// Installs the specified page fault handler. Only one handler can be active at once.
bool InstallPageFaultHandler(PageFaultHandler handler);
/// Removes the page fault handler. handler is only specified to check against the active callback.
void RemovePageFaultHandler(PageFaultHandler handler);
}
class SharedMemoryMappingArea

View File

@ -21,13 +21,15 @@
#include <fcntl.h>
#include <unistd.h>
#include <mutex>
#include "fmt/core.h"
#include "common/Align.h"
#include "common/PageFaultSource.h"
#include "common/Assertions.h"
#include "common/Console.h"
#include "common/Exceptions.h"
#include "common/General.h"
// Apple uses the MAP_ANON define instead of MAP_ANONYMOUS, but they mean
// the same thing.
@ -44,34 +46,57 @@
#include <ucontext.h>
#endif
extern void SignalExit(int sig);
static std::recursive_mutex s_exception_handler_mutex;
static PageFaultHandler s_exception_handler_callback;
static bool s_in_exception_handler;
static const uptr m_pagemask = getpagesize() - 1;
#if defined(__APPLE__)
#ifdef __APPLE__
static struct sigaction s_old_sigbus_action;
#else
static struct sigaction s_old_sigsegv_action;
#endif
static void CallExistingSignalHandler(int signal, siginfo_t* siginfo, void* ctx)
{
#ifdef __APPLE__
const struct sigaction& sa = s_old_sigbus_action;
#else
const struct sigaction& sa = s_old_sigsegv_action;
#endif
if (sa.sa_flags & SA_SIGINFO)
{
sa.sa_sigaction(signal, siginfo, ctx);
}
else if (sa.sa_handler == SIG_DFL)
{
// Re-raising the signal would just queue it, and since we'd restore the handler back to us,
// we'd end up right back here again. So just abort, because that's probably what it'd do anyway.
abort();
}
else if (sa.sa_handler != SIG_IGN)
{
sa.sa_handler(signal);
}
}
// Linux implementation of SIGSEGV handler. Bind it using sigaction().
static void SysPageFaultSignalFilter(int signal, siginfo_t* siginfo, void* ctx)
{
// [TODO] : Add a thread ID filter to the Linux Signal handler here.
// Rationale: On windows, the __try/__except model allows per-thread specific behavior
// for page fault handling. On linux, there is a single signal handler for the whole
// process, but the handler is executed by the thread that caused the exception.
// Executing the handler concurrently from multiple threads wouldn't go down well.
std::unique_lock lock(s_exception_handler_mutex);
// Prevent recursive exception filtering.
if (s_in_exception_handler)
{
CallExistingSignalHandler(signal, siginfo, ctx);
return;
}
// Stdio Usage note: SIGSEGV handling is a synchronous in-thread signal. It is done
// from the context of the current thread and stackframe. So long as the thread is not
// the main/ui thread, use of the px assertion system should be safe. Use of stdio should
// be safe even on the main thread.
// (in other words, stdio limitations only really apply to process-level asynchronous
// signals)
// Note: Use of stdio functions isn't safe here. Avoid console logs,
// assertions, file logs, or just about anything else useful.
// Note: Use of stdio functions isn't safe here. Avoid console logs, assertions, file logs,
// or just about anything else useful. However, that's really only a concern if the signal
// occurred within those functions. The logging which we do only happens when the exception
// occurred within JIT code.
#if defined(__APPLE__) && defined(__x86_64__)
void* const exception_pc = reinterpret_cast<void*>(static_cast<ucontext_t*>(ctx)->uc_mcontext->__ss.__rip);
@ -81,35 +106,62 @@ static void SysPageFaultSignalFilter(int signal, siginfo_t* siginfo, void* ctx)
void* const exception_pc = nullptr;
#endif
// Note: This signal can be accessed by the EE or MTVU thread
// Source_PageFault is a global variable with its own state information
// so for now we lock this exception code unless someone can fix this better...
std::unique_lock lock(PageFault_Mutex);
const PageFaultInfo pfi{(uptr)exception_pc, (uptr)siginfo->si_addr & ~__pagemask};
Source_PageFault->Dispatch(PageFaultInfo((uptr)exception_pc, (uptr)siginfo->si_addr & ~m_pagemask));
s_in_exception_handler = true;
// resumes execution right where we left off (re-executes instruction that
// caused the SIGSEGV).
if (Source_PageFault->WasHandled())
const bool handled = s_exception_handler_callback(pfi);
s_in_exception_handler = false;
// Resumes execution right where we left off (re-executes instruction that caused the SIGSEGV).
if (handled)
return;
std::fprintf(stderr, "Unhandled page fault @ 0x%08x", siginfo->si_addr);
pxFailRel("Unhandled page fault");
// Call old signal handler, which will likely dump core.
CallExistingSignalHandler(signal, siginfo, ctx);
}
void _platform_InstallSignalHandler()
bool HostSys::InstallPageFaultHandler(PageFaultHandler handler)
{
Console.WriteLn("Installing POSIX SIGSEGV handler...");
std::unique_lock lock(s_exception_handler_mutex);
pxAssertRel(!s_exception_handler_callback, "A page fault handler is already registered.");
if (!s_exception_handler_callback)
{
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = SysPageFaultSignalFilter;
#if defined(__APPLE__)
#ifdef __APPLE__
// MacOS uses SIGBUS for memory permission violations
sigaction(SIGBUS, &sa, &s_old_sigbus_action);
if (sigaction(SIGBUS, &sa, &s_old_sigbus_action) != 0)
return false;
#else
sigaction(SIGSEGV, &sa, &s_old_sigsegv_action);
if (sigaction(SIGSEGV, &sa, &s_old_sigsegv_action) != 0)
return false;
#endif
}
s_exception_handler_callback = handler;
return true;
}
void HostSys::RemovePageFaultHandler(PageFaultHandler handler)
{
std::unique_lock lock(s_exception_handler_mutex);
pxAssertRel(!s_exception_handler_callback || s_exception_handler_callback == handler,
"Not removing the same handler previously registered.");
if (!s_exception_handler_callback)
return;
s_exception_handler_callback = nullptr;
struct sigaction sa;
#ifdef __APPLE__
sigaction(SIGBUS, &s_old_sigbus_action, &sa);
#else
sigaction(SIGSEGV, &s_old_sigsegv_action, &sa);
#endif
}

View File

@ -17,60 +17,78 @@
#include "common/Align.h"
#include "common/RedtapeWindows.h"
#include "common/PageFaultSource.h"
#include "common/Console.h"
#include "common/General.h"
#include "common/Exceptions.h"
#include "common/StringUtil.h"
#include "common/AlignedMalloc.h"
#include "fmt/core.h"
#include "common/Assertions.h"
#include "fmt/core.h"
#include "fmt/format.h"
static long DoSysPageFaultExceptionFilter(EXCEPTION_POINTERS* eps)
{
if (eps->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
return EXCEPTION_CONTINUE_SEARCH;
#include <mutex>
#if defined(_M_AMD64)
void* const exception_pc = reinterpret_cast<void*>(eps->ContextRecord->Rip);
#else
void* const exception_pc = nullptr;
#endif
// Note: This exception can be accessed by the EE or MTVU thread
// Source_PageFault is a global variable with its own state information
// so for now we lock this exception code unless someone can fix this better...
std::unique_lock lock(PageFault_Mutex);
Source_PageFault->Dispatch(PageFaultInfo((uptr)exception_pc, (uptr)eps->ExceptionRecord->ExceptionInformation[1]));
return Source_PageFault->WasHandled() ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH;
}
static std::recursive_mutex s_exception_handler_mutex;
static PageFaultHandler s_exception_handler_callback;
static void* s_exception_handler_handle;
static bool s_in_exception_handler;
long __stdcall SysPageFaultExceptionFilter(EXCEPTION_POINTERS* eps)
{
// Prevent recursive exception filtering by catching the exception from the filter here.
// In the event that the filter causes an access violation (happened during shutdown
// because Source_PageFault was deallocated), this will allow the debugger to catch the
// exception.
// TODO: find a reliable way to debug the filter itself, I've come up with a few ways that
// work but I don't fully understand why some do and some don't.
__try
{
return DoSysPageFaultExceptionFilter(eps);
}
__except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
// Executing the handler concurrently from multiple threads wouldn't go down well.
std::unique_lock lock(s_exception_handler_mutex);
// Prevent recursive exception filtering.
if (s_in_exception_handler)
return EXCEPTION_CONTINUE_SEARCH;
// Only interested in page faults.
if (eps->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
return EXCEPTION_CONTINUE_SEARCH;
void* const exception_pc = reinterpret_cast<void*>(eps->ContextRecord->Rip);
const PageFaultInfo pfi{(uptr)exception_pc, (uptr)eps->ExceptionRecord->ExceptionInformation[1]};
s_in_exception_handler = true;
const bool handled = s_exception_handler_callback(pfi);
s_in_exception_handler = false;
return handled ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH;
}
bool HostSys::InstallPageFaultHandler(PageFaultHandler handler)
{
std::unique_lock lock(s_exception_handler_mutex);
pxAssertRel(!s_exception_handler_callback, "A page fault handler is already registered.");
if (!s_exception_handler_handle)
{
s_exception_handler_handle = AddVectoredExceptionHandler(TRUE, SysPageFaultExceptionFilter);
if (!s_exception_handler_handle)
return false;
}
s_exception_handler_callback = handler;
return true;
}
void HostSys::RemovePageFaultHandler(PageFaultHandler handler)
{
std::unique_lock lock(s_exception_handler_mutex);
pxAssertRel(!s_exception_handler_callback || s_exception_handler_callback == handler,
"Not removing the same handler previously registered.");
s_exception_handler_callback = nullptr;
if (s_exception_handler_handle)
{
RemoveVectoredExceptionHandler(s_exception_handler_handle);
s_exception_handler_handle = {};
}
}
void _platform_InstallSignalHandler()
{
#ifdef _WIN64 // We don't handle SEH properly on Win64 so use a vectored exception handler instead
AddVectoredExceptionHandler(true, SysPageFaultExceptionFilter);
#endif
}
static DWORD ConvertToWinApi(const PageProtectionMode& mode)
{
DWORD winmode = PAGE_NOACCESS;

View File

@ -70,6 +70,7 @@
<ClCompile Include="FastJmp.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="General.cpp" />
<ClCompile Include="GL\Context.cpp" />
<ClCompile Include="GL\ContextWGL.cpp" />
<ClCompile Include="GL\Program.cpp" />
@ -90,7 +91,6 @@
<ClCompile Include="StringUtil.cpp" />
<ClCompile Include="SettingsWrapper.cpp" />
<ClCompile Include="Timer.cpp" />
<ClCompile Include="VirtualMemory.cpp" />
<ClCompile Include="Vulkan\vk_mem_alloc.cpp" />
<ClCompile Include="Vulkan\Builders.cpp" />
<ClCompile Include="Vulkan\Context.cpp" />

View File

@ -67,9 +67,6 @@
<ClCompile Include="emitter\simd.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="VirtualMemory.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="emitter\WinCpuDetect.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -211,6 +208,7 @@
<ClCompile Include="WAVWriter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="General.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="AlignedMalloc.h">

View File

@ -150,6 +150,7 @@ set(pcsx2Sources
Vif_Codes.cpp
Vif_Transfer.cpp
Vif_Unpack.cpp
VirtualMemory.cpp
vtlb.cpp
VU0.cpp
VUmicro.cpp
@ -217,6 +218,7 @@ set(pcsx2Headers
Vif_Dma.h
Vif.h
Vif_Unpack.h
VirtualMemory.h
vtlb.h
VUflags.h
VUmicro.h

View File

@ -14,4 +14,42 @@
*/
#include "PrecompiledHeader.h"
#include "GSFunctionMap.h"
#include "GS/Renderers/Common/GSFunctionMap.h"
#include "System.h"
static GSCodeReserve s_instance;
GSCodeReserve::GSCodeReserve()
: RecompiledCodeReserve("GS Software Renderer")
{
}
GSCodeReserve::~GSCodeReserve() = default;
GSCodeReserve& GSCodeReserve::GetInstance()
{
return s_instance;
}
void GSCodeReserve::Assign(VirtualMemoryManagerPtr allocator)
{
RecompiledCodeReserve::Assign(std::move(allocator), HostMemoryMap::SWrecOffset, HostMemoryMap::SWrecSize);
}
void GSCodeReserve::Reset()
{
RecompiledCodeReserve::Reset();
m_memory_used = 0;
}
u8* GSCodeReserve::Reserve(size_t size)
{
pxAssert((m_memory_used + size) <= m_size);
return m_baseptr + m_memory_used;
}
void GSCodeReserve::Commit(size_t size)
{
pxAssert((m_memory_used + size) <= m_size);
m_memory_used += size;
}

View File

@ -17,7 +17,7 @@
#include "GS/GSExtra.h"
#include "GS/Renderers/SW/GSScanlineEnvironment.h"
#include "System.h"
#include "VirtualMemory.h"
#include "common/emitter/tools.h"
template <class KEY, class VALUE>
@ -142,6 +142,31 @@ public:
}
};
// --------------------------------------------------------------------------------------
// GSCodeReserve
// --------------------------------------------------------------------------------------
// Stores code buffers for the GS software JIT.
//
class GSCodeReserve : public RecompiledCodeReserve
{
public:
GSCodeReserve();
~GSCodeReserve();
static GSCodeReserve& GetInstance();
size_t GetMemoryUsed() const { return m_memory_used; }
void Assign(VirtualMemoryManagerPtr allocator);
void Reset();
u8* Reserve(size_t size);
void Commit(size_t size);
private:
size_t m_memory_used = 0;
};
template <class CG, class KEY, class VALUE>
class GSCodeGeneratorFunctionMap : public GSFunctionMap<KEY, VALUE>
{
@ -175,7 +200,7 @@ public:
}
else
{
u8* code_ptr = GetVmMemory().GSCode().Reserve(MAX_SIZE);
u8* code_ptr = GSCodeReserve::GetInstance().Reserve(MAX_SIZE);
CG cg(key, code_ptr, MAX_SIZE);
ASSERT(cg.getSize() < MAX_SIZE);
@ -185,7 +210,7 @@ public:
sel.Print();
#endif
GetVmMemory().GSCode().Commit(cg.getSize());
GSCodeReserve::GetInstance().Commit(cg.getSize());
ret = (VALUE)cg.getCode();

View File

@ -38,16 +38,16 @@ GSDrawScanline::GSDrawScanline()
: m_sp_map("GSSetupPrim")
, m_ds_map("GSDrawScanline")
{
GetVmMemory().GSCode().AllowModification();
GetVmMemory().GSCode().Reset();
GSCodeReserve::GetInstance().AllowModification();
GSCodeReserve::GetInstance().Reset();
}
GSDrawScanline::~GSDrawScanline()
{
if (const size_t used = GetVmMemory().GSCode().GetMemoryUsed(); used > 0)
if (const size_t used = GSCodeReserve::GetInstance().GetMemoryUsed(); used > 0)
DevCon.WriteLn("SW JIT generated %zu bytes of code", used);
GetVmMemory().GSCode().ForbidModification();
GSCodeReserve::GetInstance().ForbidModification();
}
void GSDrawScanline::BeginDraw(const GSRasterizerData& data, GSScanlineLocalData& local)
@ -85,7 +85,7 @@ void GSDrawScanline::ResetCodeCache()
Console.Warning("GS Software JIT cache overflow, resetting.");
m_sp_map.Clear();
m_ds_map.Clear();
GetVmMemory().GSCode().Reset();
GSCodeReserve::GetInstance().Reset();
}
bool GSDrawScanline::SetupDraw(GSRasterizerData& data)

View File

@ -47,7 +47,6 @@ BIOS
#include "SPU2/spu2.h"
#include "common/AlignedMalloc.h"
#include "common/PageFaultSource.h"
#include "GSDumpReplayer.h"
@ -668,14 +667,6 @@ void memClearPageAddr(u32 vaddr)
///////////////////////////////////////////////////////////////////////////
// PS2 Memory Init / Reset / Shutdown
class mmap_PageFaultHandler : public EventListener_PageFault
{
public:
void OnPageFaultEvent( const PageFaultInfo& info, bool& handled );
};
static mmap_PageFaultHandler* mmap_faultHandler = NULL;
EEVM_MemoryAllocMess* eeMem = NULL;
alignas(__pagesize) u8 eeHw[Ps2MemSize::Hardware];
@ -726,12 +717,6 @@ void eeMemoryReserve::Assign(VirtualMemoryManagerPtr allocator)
{
_parent::Assign(std::move(allocator), HostMemoryMap::EEmemOffset, sizeof(*eeMem));
eeMem = reinterpret_cast<EEVM_MemoryAllocMess*>(GetPtr());
if (!mmap_faultHandler)
{
pxAssert(Source_PageFault);
mmap_faultHandler = new mmap_PageFaultHandler();
}
}
@ -865,166 +850,6 @@ void eeMemoryReserve::Reset()
void eeMemoryReserve::Release()
{
safe_delete(mmap_faultHandler);
eeMem = nullptr;
_parent::Release();
}
// ===========================================================================================
// Memory Protection and Block Checking, vtlb Style!
// ===========================================================================================
// For the first time code is recompiled (executed), the PS2 ram page for that code is
// protected using Virtual Memory (mprotect). If the game modifies its own code then this
// protection causes an *exception* to be raised (signal in Linux), which is handled by
// unprotecting the page and switching the recompiled block to "manual" protection.
//
// Manual protection uses a simple brute-force memcmp of the recompiled code to the code
// currently in RAM for *each time* the block is executed. Fool-proof, but slow, which
// is why we default to using the exception-based protection scheme described above.
//
// Why manual blocks? Because many games contain code and data in the same 4k page, so
// we *cannot* automatically recompile and reprotect pages, lest we end up recompiling and
// reprotecting them constantly (Which would be very slow). As a counter, the R5900 side
// of the block checking code does try to periodically re-protect blocks [going from manual
// back to protected], so that blocks which underwent a single invalidation don't need to
// incur a permanent performance penalty.
//
// Page Granularity:
// Fortunately for us MIPS and x86 use the same page granularity for TLB and memory
// protection, so we can use a 1:1 correspondence when protecting pages. Page granularity
// is 4096 (4k), which is why you'll see a lot of 0xfff's, >><< 12's, and 0x1000's in the
// code below.
//
struct vtlb_PageProtectionInfo
{
// Ram De-mapping -- used to convert fully translated/mapped offsets (which reside with
// in the eeMem->Main block) back into their originating ps2 physical ram address.
// Values are assigned when pages are marked for protection. since pages are automatically
// cleared and reset when TLB-remapped, stale values in this table (due to on-the-fly TLB
// changes) will be re-assigned the next time the page is accessed.
u32 ReverseRamMap;
vtlb_ProtectionMode Mode;
};
alignas(16) static vtlb_PageProtectionInfo m_PageProtectInfo[Ps2MemSize::MainRam >> __pageshift];
// returns:
// ProtMode_NotRequired - unchecked block (resides in ROM, thus is integrity is constant)
// Or the current mode
//
vtlb_ProtectionMode mmap_GetRamPageInfo( u32 paddr )
{
pxAssert( eeMem );
paddr &= ~0xfff;
uptr ptr = (uptr)PSM( paddr );
uptr rampage = ptr - (uptr)eeMem->Main;
if (!ptr || rampage >= Ps2MemSize::MainRam)
return ProtMode_NotRequired; //not in ram, no tracking done ...
rampage >>= __pageshift;
return m_PageProtectInfo[rampage].Mode;
}
// paddr - physically mapped PS2 address
void mmap_MarkCountedRamPage( u32 paddr )
{
pxAssert( eeMem );
paddr &= ~__pagemask;
uptr ptr = (uptr)PSM( paddr );
int rampage = (ptr - (uptr)eeMem->Main) >> __pageshift;
// Important: Update the ReverseRamMap here because TLB changes could alter the paddr
// mapping into eeMem->Main.
m_PageProtectInfo[rampage].ReverseRamMap = paddr;
if( m_PageProtectInfo[rampage].Mode == ProtMode_Write )
return; // skip town if we're already protected.
eeRecPerfLog.Write( (m_PageProtectInfo[rampage].Mode == ProtMode_Manual) ?
"Re-protecting page @ 0x%05x" : "Protected page @ 0x%05x",
paddr>>__pageshift
);
m_PageProtectInfo[rampage].Mode = ProtMode_Write;
HostSys::MemProtect( &eeMem->Main[rampage<<__pageshift], __pagesize, PageAccess_ReadOnly() );
vtlb_UpdateFastmemProtection(rampage << __pageshift, __pagesize, PageAccess_ReadOnly());
}
// offset - offset of address relative to psM.
// All recompiled blocks belonging to the page are cleared, and any new blocks recompiled
// from code residing in this page will use manual protection.
static __fi void mmap_ClearCpuBlock( uint offset )
{
pxAssert( eeMem );
int rampage = offset >> __pageshift;
// Assertion: This function should never be run on a block that's already under
// manual protection. Indicates a logic error in the recompiler or protection code.
pxAssertMsg( m_PageProtectInfo[rampage].Mode != ProtMode_Manual,
"Attempted to clear a block that is already under manual protection." );
HostSys::MemProtect( &eeMem->Main[rampage<<__pageshift], __pagesize, PageAccess_ReadWrite() );
vtlb_UpdateFastmemProtection(rampage << __pageshift, __pagesize, PageAccess_ReadWrite());
m_PageProtectInfo[rampage].Mode = ProtMode_Manual;
Cpu->Clear( m_PageProtectInfo[rampage].ReverseRamMap, __pagesize );
}
void mmap_PageFaultHandler::OnPageFaultEvent( const PageFaultInfo& info, bool& handled )
{
pxAssert( eeMem );
u32 vaddr;
if (CHECK_FASTMEM && vtlb_GetGuestAddress(info.addr, &vaddr))
{
// this was inside the fastmem area. check if it's a code page
// fprintf(stderr, "Fault on fastmem %p vaddr %08X\n", info.addr, vaddr);
uptr ptr = (uptr)PSM(vaddr);
uptr offset = (ptr - (uptr)eeMem->Main);
if (ptr && m_PageProtectInfo[offset >> __pageshift].Mode == ProtMode_Write)
{
// fprintf(stderr, "Not backpatching code write at %08X\n", vaddr);
mmap_ClearCpuBlock(offset);
handled = true;
}
else
{
// fprintf(stderr, "Trying backpatching vaddr %08X\n", vaddr);
if (vtlb_BackpatchLoadStore(info.pc, info.addr))
handled = true;
}
}
else
{
// get bad virtual address
uptr offset = info.addr - (uptr)eeMem->Main;
if (offset >= Ps2MemSize::MainRam)
return;
mmap_ClearCpuBlock(offset);
handled = true;
}
}
// Clears all block tracking statuses, manual protection flags, and write protection.
// This does not clear any recompiler blocks. It is assumed (and necessary) for the caller
// to ensure the EErec is also reset in conjunction with calling this function.
// (this function is called by default from the eerecReset).
void mmap_ResetBlockTracking()
{
//DbgCon.WriteLn( "vtlb/mmap: Block Tracking reset..." );
memzero( m_PageProtectInfo );
if (eeMem) HostSys::MemProtect( eeMem->Main, Ps2MemSize::MainRam, PageAccess_ReadWrite() );
vtlb_UpdateFastmemProtection(0, Ps2MemSize::MainRam, PageAccess_ReadWrite());
}

View File

@ -107,18 +107,6 @@ extern void memBindConditionalHandlers();
extern void memMapVUmicro();
enum vtlb_ProtectionMode
{
ProtMode_None = 0, // page is 'unaccounted' -- neither protected nor unprotected
ProtMode_Write, // page is under write protection (exception handler)
ProtMode_Manual, // page is under manual protection (self-checked at execution)
ProtMode_NotRequired // page doesn't require any protection
};
extern vtlb_ProtectionMode mmap_GetRamPageInfo( u32 paddr );
extern void mmap_MarkCountedRamPage( u32 paddr );
extern void mmap_ResetBlockTracking();
#define memRead8 vtlb_memRead<mem8_t>
#define memRead16 vtlb_memRead<mem16_t>
#define memRead32 vtlb_memRead<mem32_t>

View File

@ -27,13 +27,18 @@
#include "common/Perf.h"
#include "common/StringUtil.h"
#include "CDVD/CDVD.h"
#include "GS/Renderers/Common/GSFunctionMap.h"
#include "common/emitter/x86_intrin.h"
#include "GSDumpReplayer.h"
#include "svnrev.h"
extern R5900cpu GSDumpReplayerCpu;
Pcsx2Config EmuConfig;
SSE_MXCSR g_sseMXCSR = {DEFAULT_sseMXCSR};
SSE_MXCSR g_sseVU0MXCSR = {DEFAULT_sseVUMXCSR};
SSE_MXCSR g_sseVU1MXCSR = {DEFAULT_sseVUMXCSR};
@ -51,121 +56,6 @@ void SetCPUState(SSE_MXCSR sseMXCSR, SSE_MXCSR sseVU0MXCSR, SSE_MXCSR sseVU1MXCS
_mm_setcsr(g_sseMXCSR.bitmask);
}
// --------------------------------------------------------------------------------------
// RecompiledCodeReserve (implementations)
// --------------------------------------------------------------------------------------
// Constructor!
// Parameters:
// name - a nice long name that accurately describes the contents of this reserve.
RecompiledCodeReserve::RecompiledCodeReserve(std::string name)
: VirtualMemoryReserve(std::move(name))
{
}
RecompiledCodeReserve::~RecompiledCodeReserve()
{
Release();
}
void RecompiledCodeReserve::_registerProfiler()
{
if (m_profiler_name.empty() || !IsOk())
return;
Perf::any.map((uptr)m_baseptr, m_size, m_profiler_name.c_str());
}
void RecompiledCodeReserve::Assign(VirtualMemoryManagerPtr allocator, size_t offset, size_t size)
{
// Anything passed to the memory allocator must be page aligned.
size = Common::PageAlign(size);
// Since the memory has already been allocated as part of the main memory map, this should never fail.
u8* base = allocator->Alloc(offset, size);
if (!base)
{
Console.WriteLn("(RecompiledCodeReserve) Failed to allocate %zu bytes for %s at offset %zu", size, m_name.c_str(), offset);
pxFailRel("RecompiledCodeReserve allocation failed.");
}
VirtualMemoryReserve::Assign(std::move(allocator), base, size);
_registerProfiler();
}
void RecompiledCodeReserve::Reset()
{
if (IsDevBuild && m_baseptr)
{
// Clear the recompiled code block to 0xcc (INT3) -- this helps disasm tools show
// the assembly dump more cleanly. We don't clear the block on Release builds since
// it can add a noticeable amount of overhead to large block recompilations.
std::memset(m_baseptr, 0xCC, m_size);
}
}
void RecompiledCodeReserve::AllowModification()
{
// Apple Silicon enforces write protection in hardware.
#if !defined(__APPLE__) || !defined(_M_ARM64)
HostSys::MemProtect(m_baseptr, m_size, PageAccess_Any());
#endif
}
void RecompiledCodeReserve::ForbidModification()
{
// Apple Silicon enforces write protection in hardware.
#if !defined(__APPLE__) || !defined(_M_ARM64)
HostSys::MemProtect(m_baseptr, m_size, PageProtectionMode().Read().Execute());
#endif
}
// Sets the abbreviated name used by the profiler. Name should be under 10 characters long.
// After a name has been set, a profiler source will be automatically registered and cleared
// in accordance with changes in the reserve area.
RecompiledCodeReserve& RecompiledCodeReserve::SetProfilerName(std::string name)
{
m_profiler_name = std::move(name);
_registerProfiler();
return *this;
}
GSCodeReserve::GSCodeReserve()
: RecompiledCodeReserve("GS Software Renderer")
{
}
GSCodeReserve::~GSCodeReserve() = default;
void GSCodeReserve::Assign(VirtualMemoryManagerPtr allocator)
{
RecompiledCodeReserve::Assign(std::move(allocator), HostMemoryMap::SWrecOffset, HostMemoryMap::SWrecSize);
}
void GSCodeReserve::Reset()
{
RecompiledCodeReserve::Reset();
m_memory_used = 0;
}
u8* GSCodeReserve::Reserve(size_t size)
{
pxAssert((m_memory_used + size) <= m_size);
return m_baseptr + m_memory_used;
}
void GSCodeReserve::Commit(size_t size)
{
pxAssert((m_memory_used + size) <= m_size);
m_memory_used += size;
}
#include "svnrev.h"
Pcsx2Config EmuConfig;
// This function should be called once during program execution.
void SysLogMachineCaps()
{
@ -322,7 +212,6 @@ SysMainMemory::~SysMainMemory()
bool SysMainMemory::Allocate()
{
DevCon.WriteLn(Color_StrongBlue, "Allocating host memory for virtual systems...");
pxInstallSignalHandler();
ConsoleIndentScope indent(1);
@ -363,8 +252,6 @@ void SysMainMemory::Release()
m_ee.Release();
m_iop.Release();
m_vu.Release();
safe_delete(Source_PageFault);
}
@ -388,12 +275,12 @@ SysCpuProviderPack::SysCpuProviderPack()
dVifReserve(1);
}
GetVmMemory().GSCode().Assign(GetVmMemory().CodeMemory());
GSCodeReserve::GetInstance().Assign(GetVmMemory().CodeMemory());
}
SysCpuProviderPack::~SysCpuProviderPack()
{
GetVmMemory().GSCode().Release();
GSCodeReserve::GetInstance().Release();
if (newVifDynaRec)
{

View File

@ -19,11 +19,11 @@
#include "common/Exceptions.h"
#include "common/SafeArray.h"
#include "common/Threading.h" // to use threading stuff, include the Threading namespace in your file.
#include "vtlb.h"
#include "common/Threading.h"
#include "Config.h"
#include "VirtualMemory.h"
#include "vtlb.h"
typedef SafeArray<u8> VmStateBuffer;
@ -91,61 +91,6 @@ namespace HostMemoryMap
static const u32 SWrecSize = 0x04000000;
}
// --------------------------------------------------------------------------------------
// RecompiledCodeReserve
// --------------------------------------------------------------------------------------
// A recompiled code reserve is a simple sequential-growth block of memory which is auto-
// cleared to INT 3 (0xcc) as needed.
//
class RecompiledCodeReserve : public VirtualMemoryReserve
{
typedef VirtualMemoryReserve _parent;
protected:
std::string m_profiler_name;
public:
RecompiledCodeReserve(std::string name);
~RecompiledCodeReserve();
void Assign(VirtualMemoryManagerPtr allocator, size_t offset, size_t size);
void Reset();
RecompiledCodeReserve& SetProfilerName(std::string name);
void ForbidModification();
void AllowModification();
operator u8*() { return m_baseptr; }
operator const u8*() const { return m_baseptr; }
protected:
void _registerProfiler();
};
// --------------------------------------------------------------------------------------
// GSCodeReserve
// --------------------------------------------------------------------------------------
// Stores code buffers for the GS software JIT.
class GSCodeReserve : public RecompiledCodeReserve
{
public:
GSCodeReserve();
~GSCodeReserve();
size_t GetMemoryUsed() const { return m_memory_used; }
void Assign(VirtualMemoryManagerPtr allocator);
void Reset();
u8* Reserve(size_t size);
void Commit(size_t size);
private:
size_t m_memory_used = 0;
};
// --------------------------------------------------------------------------------------
// SysMainMemory
// --------------------------------------------------------------------------------------
@ -162,8 +107,6 @@ protected:
iopMemoryReserve m_iop;
vuMemoryReserve m_vu;
GSCodeReserve m_gs_code;
public:
SysMainMemory();
~SysMainMemory();
@ -177,8 +120,6 @@ public:
const iopMemoryReserve& IOPMemory() const { return m_iop; }
const vuMemoryReserve& VUMemory() const { return m_vu; }
GSCodeReserve& GSCode() { return m_gs_code; }
bool Allocate();
void Reset();
void Release();

View File

@ -13,62 +13,18 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "VirtualMemory.h"
#include "common/Align.h"
#include "common/PageFaultSource.h"
#include "common/EventSource.inl"
#include "common/MemsetFast.inl"
#include "common/Console.h"
#include "common/Perf.h"
#include "fmt/core.h"
#include <cinttypes>
template class EventSource<IEventListener_PageFault>;
SrcType_PageFault* Source_PageFault = NULL;
std::mutex PageFault_Mutex;
void pxInstallSignalHandler()
{
if (!Source_PageFault)
{
Source_PageFault = new SrcType_PageFault();
}
_platform_InstallSignalHandler();
// NOP on Win32 systems -- we use __try{} __except{} instead.
}
// --------------------------------------------------------------------------------------
// EventListener_PageFault (implementations)
// --------------------------------------------------------------------------------------
EventListener_PageFault::EventListener_PageFault()
{
pxAssert(Source_PageFault);
Source_PageFault->Add(*this);
}
EventListener_PageFault::~EventListener_PageFault()
{
if (Source_PageFault)
Source_PageFault->Remove(*this);
}
void SrcType_PageFault::Dispatch(const PageFaultInfo& params)
{
m_handled = false;
_parent::Dispatch(params);
}
void SrcType_PageFault::_DispatchRaw(ListenerIterator iter, const ListenerIterator& iend, const PageFaultInfo& evt)
{
do
{
(*iter)->DispatchEvent(evt, m_handled);
} while ((++iter != iend) && !m_handled);
}
// --------------------------------------------------------------------------------------
// VirtualMemoryManager (implementations)
// --------------------------------------------------------------------------------------
@ -335,23 +291,75 @@ void VirtualMemoryReserve::Release()
}
// --------------------------------------------------------------------------------------
// PageProtectionMode (implementations)
// RecompiledCodeReserve (implementations)
// --------------------------------------------------------------------------------------
std::string PageProtectionMode::ToString() const
// Constructor!
// Parameters:
// name - a nice long name that accurately describes the contents of this reserve.
RecompiledCodeReserve::RecompiledCodeReserve(std::string name)
: VirtualMemoryReserve(std::move(name))
{
std::string modeStr;
if (m_read)
modeStr += "Read";
if (m_write)
modeStr += "Write";
if (m_exec)
modeStr += "Exec";
if (modeStr.empty())
return "NoAccess";
if (modeStr.length() <= 5)
modeStr += "Only";
return modeStr;
}
RecompiledCodeReserve::~RecompiledCodeReserve()
{
Release();
}
void RecompiledCodeReserve::_registerProfiler()
{
if (m_profiler_name.empty() || !IsOk())
return;
Perf::any.map((uptr)m_baseptr, m_size, m_profiler_name.c_str());
}
void RecompiledCodeReserve::Assign(VirtualMemoryManagerPtr allocator, size_t offset, size_t size)
{
// Anything passed to the memory allocator must be page aligned.
size = Common::PageAlign(size);
// Since the memory has already been allocated as part of the main memory map, this should never fail.
u8* base = allocator->Alloc(offset, size);
if (!base)
{
Console.WriteLn("(RecompiledCodeReserve) Failed to allocate %zu bytes for %s at offset %zu", size, m_name.c_str(), offset);
pxFailRel("RecompiledCodeReserve allocation failed.");
}
VirtualMemoryReserve::Assign(std::move(allocator), base, size);
_registerProfiler();
}
void RecompiledCodeReserve::Reset()
{
if (IsDevBuild && m_baseptr)
{
// Clear the recompiled code block to 0xcc (INT3) -- this helps disasm tools show
// the assembly dump more cleanly. We don't clear the block on Release builds since
// it can add a noticeable amount of overhead to large block recompilations.
std::memset(m_baseptr, 0xCC, m_size);
}
}
void RecompiledCodeReserve::AllowModification()
{
HostSys::MemProtect(m_baseptr, m_size, PageAccess_Any());
}
void RecompiledCodeReserve::ForbidModification()
{
HostSys::MemProtect(m_baseptr, m_size, PageProtectionMode().Read().Execute());
}
// Sets the abbreviated name used by the profiler. Name should be under 10 characters long.
// After a name has been set, a profiler source will be automatically registered and cleared
// in accordance with changes in the reserve area.
RecompiledCodeReserve& RecompiledCodeReserve::SetProfilerName(std::string name)
{
m_profiler_name = std::move(name);
_registerProfiler();
return *this;
}

View File

@ -13,123 +13,15 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
// [TODO] Rename this file to VirtualMemory.h !!
#pragma once
// =====================================================================================================
// Cross-Platform Memory Protection (Used by VTLB, Recompilers and Texture caches)
// =====================================================================================================
// Win32 platforms use the SEH model: __try {} __except {}
// Linux platforms use the POSIX Signals model: sigaction()
// [TODO] OS-X (Darwin) platforms should use the Mach exception model (not implemented)
#include "EventSource.h"
#include "General.h"
#include "Assertions.h"
#include "common/General.h"
#include "common/Assertions.h"
#include <atomic>
#include <memory>
#include <mutex>
#include <string>
struct PageFaultInfo
{
uptr pc;
uptr addr;
PageFaultInfo(uptr pc_, uptr address)
{
pc = pc_;
addr = address;
}
};
// --------------------------------------------------------------------------------------
// IEventListener_PageFault
// --------------------------------------------------------------------------------------
class IEventListener_PageFault : public IEventDispatcher<PageFaultInfo>
{
public:
typedef PageFaultInfo EvtParams;
public:
virtual ~IEventListener_PageFault() = default;
virtual void DispatchEvent(const PageFaultInfo& evtinfo, bool& handled)
{
OnPageFaultEvent(evtinfo, handled);
}
virtual void DispatchEvent(const PageFaultInfo& evtinfo)
{
pxFailRel("Don't call me, damnit. Use DispatchException instead.");
}
virtual void OnPageFaultEvent(const PageFaultInfo& evtinfo, bool& handled) {}
};
// --------------------------------------------------------------------------------------
// EventListener_PageFault / EventListenerHelper_PageFault
// --------------------------------------------------------------------------------------
class EventListener_PageFault : public IEventListener_PageFault
{
public:
EventListener_PageFault();
virtual ~EventListener_PageFault();
};
template <typename TypeToDispatchTo>
class EventListenerHelper_PageFault : public EventListener_PageFault
{
public:
TypeToDispatchTo* Owner;
public:
EventListenerHelper_PageFault(TypeToDispatchTo& dispatchTo)
{
Owner = &dispatchTo;
}
EventListenerHelper_PageFault(TypeToDispatchTo* dispatchTo)
{
Owner = dispatchTo;
}
virtual ~EventListenerHelper_PageFault() = default;
protected:
virtual void OnPageFaultEvent(const PageFaultInfo& info, bool& handled)
{
Owner->OnPageFaultEvent(info, handled);
}
};
// --------------------------------------------------------------------------------------
// SrcType_PageFault
// --------------------------------------------------------------------------------------
class SrcType_PageFault : public EventSource<IEventListener_PageFault>
{
protected:
typedef EventSource<IEventListener_PageFault> _parent;
protected:
bool m_handled;
public:
SrcType_PageFault()
: m_handled(false)
{
}
virtual ~SrcType_PageFault() = default;
bool WasHandled() const { return m_handled; }
virtual void Dispatch(const PageFaultInfo& params);
protected:
virtual void _DispatchRaw(ListenerIterator iter, const ListenerIterator& iend, const PageFaultInfo& evt);
};
// --------------------------------------------------------------------------------------
// VirtualMemoryManager: Manages the allocation of PCSX2 VM
// Ensures that all memory is close enough together for rip-relative addressing
@ -252,26 +144,34 @@ public:
}
};
#ifdef __POSIX__
// --------------------------------------------------------------------------------------
// RecompiledCodeReserve
// --------------------------------------------------------------------------------------
// A recompiled code reserve is a simple sequential-growth block of memory which is auto-
// cleared to INT 3 (0xcc) as needed.
//
class RecompiledCodeReserve : public VirtualMemoryReserve
{
typedef VirtualMemoryReserve _parent;
#define PCSX2_PAGEFAULT_PROTECT
#define PCSX2_PAGEFAULT_EXCEPT
protected:
std::string m_profiler_name;
#elif defined(_WIN32)
public:
RecompiledCodeReserve(std::string name);
~RecompiledCodeReserve();
struct _EXCEPTION_POINTERS;
extern long __stdcall SysPageFaultExceptionFilter(struct _EXCEPTION_POINTERS* eps);
void Assign(VirtualMemoryManagerPtr allocator, size_t offset, size_t size);
void Reset();
#define PCSX2_PAGEFAULT_PROTECT __try
#define PCSX2_PAGEFAULT_EXCEPT \
__except (SysPageFaultExceptionFilter(GetExceptionInformation())) {}
RecompiledCodeReserve& SetProfilerName(std::string name);
#else
#error PCSX2 - Unsupported operating system platform.
#endif
void ForbidModification();
void AllowModification();
extern void pxInstallSignalHandler();
extern void _platform_InstallSignalHandler();
operator u8*() { return m_baseptr; }
operator const u8*() const { return m_baseptr; }
extern SrcType_PageFault* Source_PageFault;
extern std::mutex PageFault_Mutex;
protected:
void _registerProfiler();
};

View File

@ -352,6 +352,7 @@
<ClCompile Include="USB\usb-pad\usb-seamic.cpp" />
<ClCompile Include="USB\usb-printer\usb-printer.cpp" />
<ClCompile Include="USB\USB.cpp" />
<ClCompile Include="VirtualMemory.cpp" />
<ClCompile Include="VMManager.cpp" />
<ClCompile Include="windows\Optimus.cpp" />
<ClCompile Include="Pcsx2Config.cpp" />
@ -711,7 +712,6 @@
<ClInclude Include="SaveState.h" />
<ClInclude Include="SingleRegisterTypes.h" />
<ClInclude Include="System.h" />
<ClInclude Include="System\SysThreads.h" />
<ClInclude Include="Counters.h" />
<ClInclude Include="Dmac.h" />
<ClInclude Include="Hardware.h" />
@ -719,6 +719,7 @@
<ClInclude Include="ps2\HwInternal.h" />
<ClInclude Include="Cache.h" />
<ClInclude Include="Memory.h" />
<ClInclude Include="VirtualMemory.h" />
<ClInclude Include="VMManager.h" />
<ClInclude Include="vtlb.h" />
<ClInclude Include="MTVU.h" />

View File

@ -1385,6 +1385,9 @@
<ClCompile Include="GSDumpReplayer.cpp">
<Filter>Tools</Filter>
</ClCompile>
<ClCompile Include="VirtualMemory.cpp">
<Filter>System</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Patch.h">
@ -1420,9 +1423,6 @@
<ClInclude Include="System.h">
<Filter>System\Include</Filter>
</ClInclude>
<ClInclude Include="System\SysThreads.h">
<Filter>System\Include</Filter>
</ClInclude>
<ClInclude Include="Counters.h">
<Filter>System\Ps2\EmotionEngine</Filter>
</ClInclude>
@ -2318,6 +2318,9 @@
<ClInclude Include="GS.h">
<Filter>System\Ps2\GS\GIF</Filter>
</ClInclude>
<ClInclude Include="VirtualMemory.h">
<Filter>System</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuildStep Include="rdebug\deci2.h">

View File

@ -36,6 +36,7 @@
#include "Cache.h"
#include "R5900Exceptions.h"
#include "IopMem.h"
#include "Host.h"
#include "common/Align.h"
#include "common/MemsetFast.inl"
@ -57,7 +58,9 @@ using namespace vtlb_private;
namespace vtlb_private
{
alignas(64) MapData vtlbdata;
}
static bool PageFaultHandler(const PageFaultInfo& info);
} // namespace vtlb_private
static vtlbHandler vtlbHandlerCount = 0;
@ -95,22 +98,28 @@ static std::unordered_multimap<u32, u32> s_fastmem_physical_mapping; // maps mai
static std::unordered_map<uptr, LoadstoreBackpatchInfo> s_fastmem_backpatch_info;
static std::unordered_set<u32> s_fastmem_faulting_pcs;
vtlb_private::VTLBPhysical vtlb_private::VTLBPhysical::fromPointer(sptr ptr) {
vtlb_private::VTLBPhysical vtlb_private::VTLBPhysical::fromPointer(sptr ptr)
{
pxAssertMsg(ptr >= 0, "Address too high");
return VTLBPhysical(ptr);
}
vtlb_private::VTLBPhysical vtlb_private::VTLBPhysical::fromHandler(vtlbHandler handler) {
vtlb_private::VTLBPhysical vtlb_private::VTLBPhysical::fromHandler(vtlbHandler handler)
{
return VTLBPhysical(handler | POINTER_SIGN_BIT);
}
vtlb_private::VTLBVirtual::VTLBVirtual(VTLBPhysical phys, u32 paddr, u32 vaddr) {
vtlb_private::VTLBVirtual::VTLBVirtual(VTLBPhysical phys, u32 paddr, u32 vaddr)
{
pxAssertMsg(0 == (paddr & VTLB_PAGE_MASK), "Should be page aligned");
pxAssertMsg(0 == (vaddr & VTLB_PAGE_MASK), "Should be page aligned");
pxAssertMsg((uptr)paddr < POINTER_SIGN_BIT, "Address too high");
if (phys.isHandler()) {
if (phys.isHandler())
{
value = phys.raw() + paddr - vaddr;
} else {
}
else
{
value = phys.raw() - vaddr;
}
}
@ -119,26 +128,30 @@ __inline int CheckCache(u32 addr)
{
u32 mask;
if(((cpuRegs.CP0.n.Config >> 16) & 0x1) == 0)
if (((cpuRegs.CP0.n.Config >> 16) & 0x1) == 0)
{
//DevCon.Warning("Data Cache Disabled! %x", cpuRegs.CP0.n.Config);
return false;//
return false; //
}
for(int i = 1; i < 48; i++)
for (int i = 1; i < 48; i++)
{
if (((tlb[i].EntryLo1 & 0x38) >> 3) == 0x3)
{
if (((tlb[i].EntryLo1 & 0x38) >> 3) == 0x3) {
mask = tlb[i].PageMask;
if ((addr >= tlb[i].PFN1) && (addr <= tlb[i].PFN1 + mask)) {
if ((addr >= tlb[i].PFN1) && (addr <= tlb[i].PFN1 + mask))
{
//DevCon.Warning("Yay! Cache check cache addr=%x, mask=%x, addr+mask=%x, VPN2=%x PFN0=%x", addr, mask, (addr & mask), tlb[i].VPN2, tlb[i].PFN0);
return true;
}
}
if (((tlb[i].EntryLo0 & 0x38) >> 3) == 0x3) {
if (((tlb[i].EntryLo0 & 0x38) >> 3) == 0x3)
{
mask = tlb[i].PageMask;
if ((addr >= tlb[i].PFN0) && (addr <= tlb[i].PFN0 + mask)) {
if ((addr >= tlb[i].PFN0) && (addr <= tlb[i].PFN0 + mask))
{
//DevCon.Warning("Yay! Cache check cache addr=%x, mask=%x, addr+mask=%x, VPN2=%x PFN0=%x", addr, mask, (addr & mask), tlb[i].VPN2, tlb[i].PFN0);
return true;
}
@ -151,19 +164,19 @@ __inline int CheckCache(u32 addr)
// --------------------------------------------------------------------------------------
// See recVTLB.cpp for the dynarec versions.
template< typename DataType >
template <typename DataType>
DataType vtlb_memRead(u32 addr)
{
static const uint DataSize = sizeof(DataType) * 8;
auto vmv = vtlbdata.vmap[addr>>VTLB_PAGE_BITS];
auto vmv = vtlbdata.vmap[addr >> VTLB_PAGE_BITS];
if (!vmv.isHandler(addr))
{
if (!CHECK_EEREC)
{
if(CHECK_CACHE && CheckCache(addr))
if (CHECK_CACHE && CheckCache(addr))
{
switch( DataSize )
switch (DataSize)
{
case 8:
return readCache8(addr);
@ -187,14 +200,14 @@ DataType vtlb_memRead(u32 addr)
}
//has to: translate, find function, call function
u32 paddr=vmv.assumeHandlerGetPAddr(addr);
u32 paddr = vmv.assumeHandlerGetPAddr(addr);
//Console.WriteLn("Translated 0x%08X to 0x%08X", addr,paddr);
//return reinterpret_cast<TemplateHelper<DataSize,false>::HandlerType*>(vtlbdata.RWFT[TemplateHelper<DataSize,false>::sidx][0][hand])(paddr,data);
switch( DataSize )
switch (DataSize)
{
case 8:
return vmv.assumeHandler< 8, false>()(paddr);
return vmv.assumeHandler<8, false>()(paddr);
case 16:
return vmv.assumeHandler<16, false>()(paddr);
case 32:
@ -210,13 +223,13 @@ DataType vtlb_memRead(u32 addr)
RETURNS_R128 vtlb_memRead128(u32 mem)
{
auto vmv = vtlbdata.vmap[mem>>VTLB_PAGE_BITS];
auto vmv = vtlbdata.vmap[mem >> VTLB_PAGE_BITS];
if (!vmv.isHandler(mem))
{
if (!CHECK_EEREC)
{
if(CHECK_CACHE && CheckCache(mem))
if (CHECK_CACHE && CheckCache(mem))
{
return readCache128(mem);
}
@ -233,20 +246,20 @@ RETURNS_R128 vtlb_memRead128(u32 mem)
}
}
template< typename DataType >
template <typename DataType>
void vtlb_memWrite(u32 addr, DataType data)
{
static const uint DataSize = sizeof(DataType) * 8;
auto vmv = vtlbdata.vmap[addr>>VTLB_PAGE_BITS];
auto vmv = vtlbdata.vmap[addr >> VTLB_PAGE_BITS];
if (!vmv.isHandler(addr))
{
if (!CHECK_EEREC)
{
if(CHECK_CACHE && CheckCache(addr))
if (CHECK_CACHE && CheckCache(addr))
{
switch( DataSize )
switch (DataSize)
{
case 8:
writeCache8(addr, data);
@ -264,26 +277,26 @@ void vtlb_memWrite(u32 addr, DataType data)
}
}
*reinterpret_cast<DataType*>(vmv.assumePtr(addr))=data;
*reinterpret_cast<DataType*>(vmv.assumePtr(addr)) = data;
}
else
{
//has to: translate, find function, call function
u32 paddr = vmv.assumeHandlerGetPAddr(addr);
//Console.WriteLn("Translated 0x%08X to 0x%08X", addr,paddr);
return vmv.assumeHandler<sizeof(DataType)*8, true>()(paddr, data);
return vmv.assumeHandler<sizeof(DataType) * 8, true>()(paddr, data);
}
}
void TAKES_R128 vtlb_memWrite128(u32 mem, r128 value)
{
auto vmv = vtlbdata.vmap[mem>>VTLB_PAGE_BITS];
auto vmv = vtlbdata.vmap[mem >> VTLB_PAGE_BITS];
if (!vmv.isHandler(mem))
{
if (!CHECK_EEREC)
{
if(CHECK_CACHE && CheckCache(mem))
if (CHECK_CACHE && CheckCache(mem))
{
alignas(16) const u128 r = r128_to_u128(value);
writeCache128(mem, &r);
@ -366,7 +379,8 @@ static void GoemonTlbMissDebug()
// 0x3d5580 is the address of the TLB cache
GoemonTlb* tlb = (GoemonTlb*)&eeMem->Main[0x3d5580];
for (u32 i = 0; i < 150; i++) {
for (u32 i = 0; i < 150; i++)
{
if (tlb[i].valid == 0x1 && tlb[i].low_add != tlb[i].high_add)
DevCon.WriteLn("GoemonTlbMissDebug: Entry %d is valid. Key %x. From V:0x%8.8x to V:0x%8.8x (P:0x%8.8x)", i, tlb[i].key, tlb[i].low_add, tlb[i].high_add, tlb[i].physical_add);
else if (tlb[i].low_add != tlb[i].high_add)
@ -379,8 +393,10 @@ void GoemonPreloadTlb()
// 0x3d5580 is the address of the TLB cache table
GoemonTlb* tlb = (GoemonTlb*)&eeMem->Main[0x3d5580];
for (u32 i = 0; i < 150; i++) {
if (tlb[i].valid == 0x1 && tlb[i].low_add != tlb[i].high_add) {
for (u32 i = 0; i < 150; i++)
{
if (tlb[i].valid == 0x1 && tlb[i].low_add != tlb[i].high_add)
{
u32 size = tlb[i].high_add - tlb[i].low_add;
u32 vaddr = tlb[i].low_add;
@ -388,11 +404,12 @@ void GoemonPreloadTlb()
// TODO: The old code (commented below) seems to check specifically for handler 0. Is this really correct?
//if ((uptr)vtlbdata.vmap[vaddr>>VTLB_PAGE_BITS] == POINTER_SIGN_BIT) {
auto vmv = vtlbdata.vmap[vaddr>>VTLB_PAGE_BITS];
if (vmv.isHandler(vaddr) && vmv.assumeHandlerGetID() == 0) {
auto vmv = vtlbdata.vmap[vaddr >> VTLB_PAGE_BITS];
if (vmv.isHandler(vaddr) && vmv.assumeHandlerGetID() == 0)
{
DevCon.WriteLn("GoemonPreloadTlb: Entry %d. Key %x. From V:0x%8.8x to P:0x%8.8x (%d pages)", i, tlb[i].key, vaddr, paddr, size >> VTLB_PAGE_BITS);
vtlb_VMap( vaddr , paddr, size);
vtlb_VMap(0x20000000|vaddr , paddr, size);
vtlb_VMap(vaddr, paddr, size);
vtlb_VMap(0x20000000 | vaddr, paddr, size);
}
}
}
@ -402,15 +419,18 @@ void GoemonUnloadTlb(u32 key)
{
// 0x3d5580 is the address of the TLB cache table
GoemonTlb* tlb = (GoemonTlb*)&eeMem->Main[0x3d5580];
for (u32 i = 0; i < 150; i++) {
if (tlb[i].key == key) {
if (tlb[i].valid == 0x1) {
for (u32 i = 0; i < 150; i++)
{
if (tlb[i].key == key)
{
if (tlb[i].valid == 0x1)
{
u32 size = tlb[i].high_add - tlb[i].low_add;
u32 vaddr = tlb[i].low_add;
DevCon.WriteLn("GoemonUnloadTlb: Entry %d. Key %x. From V:0x%8.8x to V:0x%8.8x (%d pages)", i, tlb[i].key, vaddr, vaddr+size, size >> VTLB_PAGE_BITS);
DevCon.WriteLn("GoemonUnloadTlb: Entry %d. Key %x. From V:0x%8.8x to V:0x%8.8x (%d pages)", i, tlb[i].key, vaddr, vaddr + size, size >> VTLB_PAGE_BITS);
vtlb_VMapUnmap( vaddr , size);
vtlb_VMapUnmap(0x20000000|vaddr , size);
vtlb_VMapUnmap(vaddr, size);
vtlb_VMapUnmap(0x20000000 | vaddr, size);
// Unmap the tlb in game cache table
// Note: Game copy FEFEFEFE for others data
@ -418,7 +438,9 @@ void GoemonUnloadTlb(u32 key)
tlb[i].key = 0xFEFEFEFE;
tlb[i].low_add = 0xFEFEFEFE;
tlb[i].high_add = 0xFEFEFEFE;
} else {
}
else
{
DevCon.Error("GoemonUnloadTlb: Entry %d is not valid. Key %x", i, tlb[i].key);
}
}
@ -426,13 +448,14 @@ void GoemonUnloadTlb(u32 key)
}
// Generates a tlbMiss Exception
static __ri void vtlb_Miss(u32 addr,u32 mode)
static __ri void vtlb_Miss(u32 addr, u32 mode)
{
if (EmuConfig.Gamefixes.GoemonTlbHack)
GoemonTlbMissDebug();
// Hack to handle expected tlb miss by some games.
if (Cpu == &intCpu) {
if (Cpu == &intCpu)
{
if (mode)
cpuTlbMissW(addr, cpuRegs.branch);
else
@ -442,33 +465,34 @@ static __ri void vtlb_Miss(u32 addr,u32 mode)
throw Exception::CancelInstruction();
}
if( IsDevBuild )
Cpu->ThrowCpuException( R5900Exception::TLBMiss( addr, !!mode ) );
if (IsDevBuild)
Cpu->ThrowCpuException(R5900Exception::TLBMiss(addr, !!mode));
else
{
static int spamStop = 0;
if ( spamStop++ < 50 )
Console.Error( R5900Exception::TLBMiss( addr, !!mode ).FormatMessage() );
if (spamStop++ < 50)
Console.Error(R5900Exception::TLBMiss(addr, !!mode).FormatMessage());
}
}
// BusError exception: more serious than a TLB miss. If properly emulated the PS2 kernel
// itself would invoke a diagnostic/assertion screen that displays the cpu state at the
// time of the exception.
static __ri void vtlb_BusError(u32 addr,u32 mode)
static __ri void vtlb_BusError(u32 addr, u32 mode)
{
// The exception terminate the program on linux which is very annoying
// Just disable it for the moment
#ifdef __linux__
if (0)
#else
if( IsDevBuild )
if (IsDevBuild)
#endif
Cpu->ThrowCpuException( R5900Exception::BusError( addr, !!mode ) );
Cpu->ThrowCpuException(R5900Exception::BusError(addr, !!mode));
else
Console.Error( R5900Exception::TLBMiss( addr, !!mode ).FormatMessage() );
Console.Error(R5900Exception::TLBMiss(addr, !!mode).FormatMessage());
}
// clang-format off
template <typename OperandType>
static OperandType vtlbUnmappedVReadSm(u32 addr) { vtlb_Miss(addr, 0); return 0; }
static RETURNS_R128 vtlbUnmappedVReadLg(u32 addr) { vtlb_Miss(addr, 0); return r128_zero(); }
@ -484,6 +508,7 @@ static RETURNS_R128 vtlbUnmappedPReadLg(u32 addr) { vtlb_BusError(addr, 0); retu
template <typename OperandType>
static void vtlbUnmappedPWriteSm(u32 addr, OperandType data) { vtlb_BusError(addr, 1); }
static void TAKES_R128 vtlbUnmappedPWriteLg(u32 addr, r128 data) { vtlb_BusError(addr, 1); }
// clang-format on
// --------------------------------------------------------------------------------------
// VTLB mapping errors
@ -537,12 +562,12 @@ static void vtlbDefaultPhyWrite32(u32 addr, mem32_t data)
pxFailDev(fmt::format("(VTLB) Attempted write32 to unmapped physical address @ 0x{:08X}.", addr).c_str());
}
static void vtlbDefaultPhyWrite64(u32 addr,mem64_t data)
static void vtlbDefaultPhyWrite64(u32 addr, mem64_t data)
{
pxFailDev(fmt::format("(VTLB) Attempted write64 to unmapped physical address @ 0x{:08X}.", addr).c_str());
}
static void TAKES_R128 vtlbDefaultPhyWrite128(u32 addr,r128 data)
static void TAKES_R128 vtlbDefaultPhyWrite128(u32 addr, r128 data)
{
pxFailDev(fmt::format("(VTLB) Attempted write128 to unmapped physical address @ 0x{:08X}.", addr).c_str());
}
@ -559,28 +584,28 @@ static void TAKES_R128 vtlbDefaultPhyWrite128(u32 addr,r128 data)
//
// Note: All handlers persist across calls to vtlb_Reset(), but are wiped/invalidated by calls to vtlb_Init()
//
__ri void vtlb_ReassignHandler( vtlbHandler rv,
vtlbMemR8FP* r8,vtlbMemR16FP* r16,vtlbMemR32FP* r32,vtlbMemR64FP* r64,vtlbMemR128FP* r128,
vtlbMemW8FP* w8,vtlbMemW16FP* w16,vtlbMemW32FP* w32,vtlbMemW64FP* w64,vtlbMemW128FP* w128 )
__ri void vtlb_ReassignHandler(vtlbHandler rv,
vtlbMemR8FP* r8, vtlbMemR16FP* r16, vtlbMemR32FP* r32, vtlbMemR64FP* r64, vtlbMemR128FP* r128,
vtlbMemW8FP* w8, vtlbMemW16FP* w16, vtlbMemW32FP* w32, vtlbMemW64FP* w64, vtlbMemW128FP* w128)
{
pxAssume(rv < VTLB_HANDLER_ITEMS);
vtlbdata.RWFT[0][0][rv] = (void*)((r8!=0) ? r8 : vtlbDefaultPhyRead8);
vtlbdata.RWFT[1][0][rv] = (void*)((r16!=0) ? r16 : vtlbDefaultPhyRead16);
vtlbdata.RWFT[2][0][rv] = (void*)((r32!=0) ? r32 : vtlbDefaultPhyRead32);
vtlbdata.RWFT[3][0][rv] = (void*)((r64!=0) ? r64 : vtlbDefaultPhyRead64);
vtlbdata.RWFT[4][0][rv] = (void*)((r128!=0) ? r128 : vtlbDefaultPhyRead128);
vtlbdata.RWFT[0][0][rv] = (void*)((r8 != 0) ? r8 : vtlbDefaultPhyRead8);
vtlbdata.RWFT[1][0][rv] = (void*)((r16 != 0) ? r16 : vtlbDefaultPhyRead16);
vtlbdata.RWFT[2][0][rv] = (void*)((r32 != 0) ? r32 : vtlbDefaultPhyRead32);
vtlbdata.RWFT[3][0][rv] = (void*)((r64 != 0) ? r64 : vtlbDefaultPhyRead64);
vtlbdata.RWFT[4][0][rv] = (void*)((r128 != 0) ? r128 : vtlbDefaultPhyRead128);
vtlbdata.RWFT[0][1][rv] = (void*)((w8!=0) ? w8 : vtlbDefaultPhyWrite8);
vtlbdata.RWFT[1][1][rv] = (void*)((w16!=0) ? w16 : vtlbDefaultPhyWrite16);
vtlbdata.RWFT[2][1][rv] = (void*)((w32!=0) ? w32 : vtlbDefaultPhyWrite32);
vtlbdata.RWFT[3][1][rv] = (void*)((w64!=0) ? w64 : vtlbDefaultPhyWrite64);
vtlbdata.RWFT[4][1][rv] = (void*)((w128!=0) ? w128 : vtlbDefaultPhyWrite128);
vtlbdata.RWFT[0][1][rv] = (void*)((w8 != 0) ? w8 : vtlbDefaultPhyWrite8);
vtlbdata.RWFT[1][1][rv] = (void*)((w16 != 0) ? w16 : vtlbDefaultPhyWrite16);
vtlbdata.RWFT[2][1][rv] = (void*)((w32 != 0) ? w32 : vtlbDefaultPhyWrite32);
vtlbdata.RWFT[3][1][rv] = (void*)((w64 != 0) ? w64 : vtlbDefaultPhyWrite64);
vtlbdata.RWFT[4][1][rv] = (void*)((w128 != 0) ? w128 : vtlbDefaultPhyWrite128);
}
vtlbHandler vtlb_NewHandler()
{
pxAssertDev( vtlbHandlerCount < VTLB_HANDLER_ITEMS, "VTLB handler count overflow!" );
pxAssertDev(vtlbHandlerCount < VTLB_HANDLER_ITEMS, "VTLB handler count overflow!");
return vtlbHandlerCount++;
}
@ -593,11 +618,11 @@ vtlbHandler vtlb_NewHandler()
//
// Returns a handle for the newly created handler See vtlb_MapHandler for use of the return value.
//
__ri vtlbHandler vtlb_RegisterHandler( vtlbMemR8FP* r8,vtlbMemR16FP* r16,vtlbMemR32FP* r32,vtlbMemR64FP* r64,vtlbMemR128FP* r128,
vtlbMemW8FP* w8,vtlbMemW16FP* w16,vtlbMemW32FP* w32,vtlbMemW64FP* w64,vtlbMemW128FP* w128)
__ri vtlbHandler vtlb_RegisterHandler(vtlbMemR8FP* r8, vtlbMemR16FP* r16, vtlbMemR32FP* r32, vtlbMemR64FP* r64, vtlbMemR128FP* r128,
vtlbMemW8FP* w8, vtlbMemW16FP* w16, vtlbMemW32FP* w32, vtlbMemW64FP* w64, vtlbMemW128FP* w128)
{
vtlbHandler rv = vtlb_NewHandler();
vtlb_ReassignHandler( rv, r8, r16, r32, r64, r128, w8, w16, w32, w64, w128 );
vtlb_ReassignHandler(rv, r8, r16, r32, r64, r128, w8, w16, w32, w64, w128);
return rv;
}
@ -611,31 +636,31 @@ __ri vtlbHandler vtlb_RegisterHandler( vtlbMemR8FP* r8,vtlbMemR16FP* r16,vtlbMem
// The memory region start and size parameters must be pagesize aligned.
void vtlb_MapHandler(vtlbHandler handler, u32 start, u32 size)
{
verify(0==(start&VTLB_PAGE_MASK));
verify(0==(size&VTLB_PAGE_MASK) && size>0);
verify(0 == (start & VTLB_PAGE_MASK));
verify(0 == (size & VTLB_PAGE_MASK) && size > 0);
u32 end = start + (size - VTLB_PAGE_SIZE);
pxAssume( (end>>VTLB_PAGE_BITS) < std::size(vtlbdata.pmap) );
pxAssume((end >> VTLB_PAGE_BITS) < std::size(vtlbdata.pmap));
while (start <= end)
{
vtlbdata.pmap[start>>VTLB_PAGE_BITS] = VTLBPhysical::fromHandler(handler);
vtlbdata.pmap[start >> VTLB_PAGE_BITS] = VTLBPhysical::fromHandler(handler);
start += VTLB_PAGE_SIZE;
}
}
void vtlb_MapBlock(void* base, u32 start, u32 size, u32 blocksize)
{
verify(0==(start&VTLB_PAGE_MASK));
verify(0==(size&VTLB_PAGE_MASK) && size>0);
if(!blocksize)
verify(0 == (start & VTLB_PAGE_MASK));
verify(0 == (size & VTLB_PAGE_MASK) && size > 0);
if (!blocksize)
blocksize = size;
verify(0==(blocksize&VTLB_PAGE_MASK) && blocksize>0);
verify(0==(size%blocksize));
verify(0 == (blocksize & VTLB_PAGE_MASK) && blocksize > 0);
verify(0 == (size % blocksize));
sptr baseint = (sptr)base;
u32 end = start + (size - VTLB_PAGE_SIZE);
verify((end>>VTLB_PAGE_BITS) < std::size(vtlbdata.pmap));
verify((end >> VTLB_PAGE_BITS) < std::size(vtlbdata.pmap));
while (start <= end)
{
@ -644,7 +669,7 @@ void vtlb_MapBlock(void* base, u32 start, u32 size, u32 blocksize)
while (loopsz > 0)
{
vtlbdata.pmap[start>>VTLB_PAGE_BITS] = VTLBPhysical::fromPointer(ptr);
vtlbdata.pmap[start >> VTLB_PAGE_BITS] = VTLBPhysical::fromPointer(ptr);
start += VTLB_PAGE_SIZE;
ptr += VTLB_PAGE_SIZE;
@ -653,18 +678,18 @@ void vtlb_MapBlock(void* base, u32 start, u32 size, u32 blocksize)
}
}
void vtlb_Mirror(u32 new_region,u32 start,u32 size)
void vtlb_Mirror(u32 new_region, u32 start, u32 size)
{
verify(0==(new_region&VTLB_PAGE_MASK));
verify(0==(start&VTLB_PAGE_MASK));
verify(0==(size&VTLB_PAGE_MASK) && size>0);
verify(0 == (new_region & VTLB_PAGE_MASK));
verify(0 == (start & VTLB_PAGE_MASK));
verify(0 == (size & VTLB_PAGE_MASK) && size > 0);
u32 end = start + (size-VTLB_PAGE_SIZE);
verify((end>>VTLB_PAGE_BITS) < std::size(vtlbdata.pmap));
u32 end = start + (size - VTLB_PAGE_SIZE);
verify((end >> VTLB_PAGE_BITS) < std::size(vtlbdata.pmap));
while(start <= end)
while (start <= end)
{
vtlbdata.pmap[start>>VTLB_PAGE_BITS] = vtlbdata.pmap[new_region>>VTLB_PAGE_BITS];
vtlbdata.pmap[start >> VTLB_PAGE_BITS] = vtlbdata.pmap[new_region >> VTLB_PAGE_BITS];
start += VTLB_PAGE_SIZE;
new_region += VTLB_PAGE_SIZE;
@ -673,15 +698,15 @@ void vtlb_Mirror(u32 new_region,u32 start,u32 size)
__fi void* vtlb_GetPhyPtr(u32 paddr)
{
if (paddr>=VTLB_PMAP_SZ || vtlbdata.pmap[paddr>>VTLB_PAGE_BITS].isHandler())
if (paddr >= VTLB_PMAP_SZ || vtlbdata.pmap[paddr >> VTLB_PAGE_BITS].isHandler())
return NULL;
else
return reinterpret_cast<void*>(vtlbdata.pmap[paddr>>VTLB_PAGE_BITS].assumePtr()+(paddr&VTLB_PAGE_MASK));
return reinterpret_cast<void*>(vtlbdata.pmap[paddr >> VTLB_PAGE_BITS].assumePtr() + (paddr & VTLB_PAGE_MASK));
}
__fi u32 vtlb_V2P(u32 vaddr)
{
u32 paddr = vtlbdata.ppmap[vaddr>>VTLB_PAGE_BITS];
u32 paddr = vtlbdata.ppmap[vaddr >> VTLB_PAGE_BITS];
paddr |= vaddr & VTLB_PAGE_MASK;
return paddr;
}
@ -821,7 +846,7 @@ static void vtlb_CreateFastmemMapping(u32 vaddr, u32 mainmem_offset, const PageP
// remove reverse mapping
auto range = s_fastmem_physical_mapping.equal_range(mainmem_offset);
for (auto it = range.first; it != range.second; )
for (auto it = range.first; it != range.second;)
{
auto this_it = it++;
if (this_it->second == vaddr)
@ -1023,11 +1048,11 @@ bool vtlb_IsFaultingPC(u32 guest_pc)
//virtual mappings
//TODO: Add invalid paddr checks
void vtlb_VMap(u32 vaddr,u32 paddr,u32 size)
void vtlb_VMap(u32 vaddr, u32 paddr, u32 size)
{
verify(0==(vaddr&VTLB_PAGE_MASK));
verify(0==(paddr&VTLB_PAGE_MASK));
verify(0==(size&VTLB_PAGE_MASK) && size>0);
verify(0 == (vaddr & VTLB_PAGE_MASK));
verify(0 == (paddr & VTLB_PAGE_MASK));
verify(0 == (size & VTLB_PAGE_MASK) && size > 0);
if (CHECK_FASTMEM)
{
@ -1067,10 +1092,10 @@ void vtlb_VMap(u32 vaddr,u32 paddr,u32 size)
}
}
void vtlb_VMapBuffer(u32 vaddr,void* buffer,u32 size)
void vtlb_VMapBuffer(u32 vaddr, void* buffer, u32 size)
{
verify(0==(vaddr&VTLB_PAGE_MASK));
verify(0==(size&VTLB_PAGE_MASK) && size>0);
verify(0 == (vaddr & VTLB_PAGE_MASK));
verify(0 == (size & VTLB_PAGE_MASK) && size > 0);
if (CHECK_FASTMEM)
{
@ -1091,23 +1116,23 @@ void vtlb_VMapBuffer(u32 vaddr,void* buffer,u32 size)
uptr bu8 = (uptr)buffer;
while (size > 0)
{
vtlbdata.vmap[vaddr>>VTLB_PAGE_BITS] = VTLBVirtual::fromPointer(bu8, vaddr);
vtlbdata.vmap[vaddr >> VTLB_PAGE_BITS] = VTLBVirtual::fromPointer(bu8, vaddr);
vaddr += VTLB_PAGE_SIZE;
bu8 += VTLB_PAGE_SIZE;
size -= VTLB_PAGE_SIZE;
}
}
void vtlb_VMapUnmap(u32 vaddr,u32 size)
void vtlb_VMapUnmap(u32 vaddr, u32 size)
{
verify(0==(vaddr&VTLB_PAGE_MASK));
verify(0==(size&VTLB_PAGE_MASK) && size>0);
verify(0 == (vaddr & VTLB_PAGE_MASK));
verify(0 == (size & VTLB_PAGE_MASK) && size > 0);
vtlb_RemoveFastmemMappings(vaddr, size);
while (size > 0)
{
vtlbdata.vmap[vaddr>>VTLB_PAGE_BITS] = VTLBVirtual(VTLBPhysical::fromHandler(UnmappedVirtHandler), vaddr, vaddr);
vtlbdata.vmap[vaddr >> VTLB_PAGE_BITS] = VTLBVirtual(VTLBPhysical::fromHandler(UnmappedVirtHandler), vaddr, vaddr);
vaddr += VTLB_PAGE_SIZE;
size -= VTLB_PAGE_SIZE;
}
@ -1116,7 +1141,7 @@ void vtlb_VMapUnmap(u32 vaddr,u32 size)
// vtlb_Init -- Clears vtlb handlers and memory mappings.
void vtlb_Init()
{
vtlbHandlerCount=0;
vtlbHandlerCount = 0;
memzero(vtlbdata.RWFT);
#define VTLB_BuildUnmappedHandler(baseName) \
@ -1158,7 +1183,8 @@ void vtlb_Init()
void vtlb_Reset()
{
vtlb_RemoveFastmemMappings();
for(int i=0; i<48; i++) UnmapTLB(tlb[i], i);
for (int i = 0; i < 48; i++)
UnmapTLB(tlb[i], i);
}
void vtlb_Shutdown()
@ -1204,7 +1230,7 @@ static constexpr size_t VMAP_SIZE = sizeof(VTLBVirtual) * VTLB_VMAP_ITEMS;
// [TODO] basemem - request allocating memory at the specified virtual location, which can allow
// for easier debugging and/or 3rd party cheat programs. If 0, the operating system
// default is used.
void vtlb_Core_Alloc()
bool vtlb_Core_Alloc()
{
// Can't return regions to the bump allocator
static VTLBVirtual* vmap = nullptr;
@ -1212,7 +1238,10 @@ void vtlb_Core_Alloc()
{
vmap = (VTLBVirtual*)GetVmMemory().BumpAllocator().Alloc(VMAP_SIZE);
if (!vmap)
pxFailRel("Failed to allocate vtlb vmap");
{
Host::ReportErrorAsync("Error", "Failed to allocate vtlb vmap");
return false;
}
}
if (!vtlbdata.vmap)
@ -1226,13 +1255,24 @@ void vtlb_Core_Alloc()
pxAssert(!s_fastmem_area);
s_fastmem_area = SharedMemoryMappingArea::Create(FASTMEM_AREA_SIZE);
if (!s_fastmem_area)
pxFailRel("Failed to allocate fastmem area");
{
Host::ReportErrorAsync("Error", "Failed to allocate fastmem area");
return false;
}
s_fastmem_virtual_mapping.resize(FASTMEM_PAGE_COUNT, NO_FASTMEM_MAPPING);
vtlbdata.fastmem_base = (uptr)s_fastmem_area->BasePointer();
Console.WriteLn(Color_StrongGreen, "Fastmem area: %p - %p",
vtlbdata.fastmem_base, vtlbdata.fastmem_base + (FASTMEM_AREA_SIZE - 1));
}
if (!HostSys::InstallPageFaultHandler(&vtlb_private::PageFaultHandler))
{
Host::ReportErrorAsync("Error", "Failed to install page fault handler.");
return false;
}
return true;
}
static constexpr size_t PPMAP_SIZE = sizeof(*vtlbdata.ppmap) * VTLB_VMAP_ITEMS;
@ -1254,11 +1294,13 @@ void vtlb_Alloc_Ppmap()
// By default a 1:1 virtual to physical mapping
for (u32 i = 0; i < VTLB_VMAP_ITEMS; i++)
vtlbdata.ppmap[i] = i<<VTLB_PAGE_BITS;
vtlbdata.ppmap[i] = i << VTLB_PAGE_BITS;
}
void vtlb_Core_Free()
{
HostSys::RemovePageFaultHandler(&vtlb_private::PageFaultHandler);
if (vtlbdata.vmap)
{
HostSys::MemProtect(vtlbdata.vmap, VMAP_SIZE, PageProtectionMode());
@ -1307,3 +1349,163 @@ void VtlbMemoryReserve::Reset()
{
memzero_sse_a(GetPtr(), GetSize());
}
// ===========================================================================================
// Memory Protection and Block Checking, vtlb Style!
// ===========================================================================================
// For the first time code is recompiled (executed), the PS2 ram page for that code is
// protected using Virtual Memory (mprotect). If the game modifies its own code then this
// protection causes an *exception* to be raised (signal in Linux), which is handled by
// unprotecting the page and switching the recompiled block to "manual" protection.
//
// Manual protection uses a simple brute-force memcmp of the recompiled code to the code
// currently in RAM for *each time* the block is executed. Fool-proof, but slow, which
// is why we default to using the exception-based protection scheme described above.
//
// Why manual blocks? Because many games contain code and data in the same 4k page, so
// we *cannot* automatically recompile and reprotect pages, lest we end up recompiling and
// reprotecting them constantly (Which would be very slow). As a counter, the R5900 side
// of the block checking code does try to periodically re-protect blocks [going from manual
// back to protected], so that blocks which underwent a single invalidation don't need to
// incur a permanent performance penalty.
//
// Page Granularity:
// Fortunately for us MIPS and x86 use the same page granularity for TLB and memory
// protection, so we can use a 1:1 correspondence when protecting pages. Page granularity
// is 4096 (4k), which is why you'll see a lot of 0xfff's, >><< 12's, and 0x1000's in the
// code below.
//
struct vtlb_PageProtectionInfo
{
// Ram De-mapping -- used to convert fully translated/mapped offsets (which reside with
// in the eeMem->Main block) back into their originating ps2 physical ram address.
// Values are assigned when pages are marked for protection. since pages are automatically
// cleared and reset when TLB-remapped, stale values in this table (due to on-the-fly TLB
// changes) will be re-assigned the next time the page is accessed.
u32 ReverseRamMap;
vtlb_ProtectionMode Mode;
};
alignas(16) static vtlb_PageProtectionInfo m_PageProtectInfo[Ps2MemSize::MainRam >> __pageshift];
// returns:
// ProtMode_NotRequired - unchecked block (resides in ROM, thus is integrity is constant)
// Or the current mode
//
vtlb_ProtectionMode mmap_GetRamPageInfo(u32 paddr)
{
pxAssert(eeMem);
paddr &= ~0xfff;
uptr ptr = (uptr)PSM(paddr);
uptr rampage = ptr - (uptr)eeMem->Main;
if (!ptr || rampage >= Ps2MemSize::MainRam)
return ProtMode_NotRequired; //not in ram, no tracking done ...
rampage >>= __pageshift;
return m_PageProtectInfo[rampage].Mode;
}
// paddr - physically mapped PS2 address
void mmap_MarkCountedRamPage(u32 paddr)
{
pxAssert(eeMem);
paddr &= ~__pagemask;
uptr ptr = (uptr)PSM(paddr);
int rampage = (ptr - (uptr)eeMem->Main) >> __pageshift;
// Important: Update the ReverseRamMap here because TLB changes could alter the paddr
// mapping into eeMem->Main.
m_PageProtectInfo[rampage].ReverseRamMap = paddr;
if (m_PageProtectInfo[rampage].Mode == ProtMode_Write)
return; // skip town if we're already protected.
eeRecPerfLog.Write((m_PageProtectInfo[rampage].Mode == ProtMode_Manual) ?
"Re-protecting page @ 0x%05x" :
"Protected page @ 0x%05x",
paddr >> __pageshift);
m_PageProtectInfo[rampage].Mode = ProtMode_Write;
HostSys::MemProtect(&eeMem->Main[rampage << __pageshift], __pagesize, PageAccess_ReadOnly());
vtlb_UpdateFastmemProtection(rampage << __pageshift, __pagesize, PageAccess_ReadOnly());
}
// offset - offset of address relative to psM.
// All recompiled blocks belonging to the page are cleared, and any new blocks recompiled
// from code residing in this page will use manual protection.
static __fi void mmap_ClearCpuBlock(uint offset)
{
pxAssert(eeMem);
int rampage = offset >> __pageshift;
// Assertion: This function should never be run on a block that's already under
// manual protection. Indicates a logic error in the recompiler or protection code.
pxAssertMsg(m_PageProtectInfo[rampage].Mode != ProtMode_Manual,
"Attempted to clear a block that is already under manual protection.");
HostSys::MemProtect(&eeMem->Main[rampage << __pageshift], __pagesize, PageAccess_ReadWrite());
vtlb_UpdateFastmemProtection(rampage << __pageshift, __pagesize, PageAccess_ReadWrite());
m_PageProtectInfo[rampage].Mode = ProtMode_Manual;
Cpu->Clear(m_PageProtectInfo[rampage].ReverseRamMap, __pagesize);
}
bool vtlb_private::PageFaultHandler(const PageFaultInfo& info)
{
pxAssert(eeMem);
u32 vaddr;
if (CHECK_FASTMEM && vtlb_GetGuestAddress(info.addr, &vaddr))
{
// this was inside the fastmem area. check if it's a code page
// fprintf(stderr, "Fault on fastmem %p vaddr %08X\n", info.addr, vaddr);
uptr ptr = (uptr)PSM(vaddr);
uptr offset = (ptr - (uptr)eeMem->Main);
if (ptr && m_PageProtectInfo[offset >> __pageshift].Mode == ProtMode_Write)
{
// fprintf(stderr, "Not backpatching code write at %08X\n", vaddr);
mmap_ClearCpuBlock(offset);
return true;
}
else
{
// fprintf(stderr, "Trying backpatching vaddr %08X\n", vaddr);
return vtlb_BackpatchLoadStore(info.pc, info.addr);
}
}
else
{
// get bad virtual address
uptr offset = info.addr - (uptr)eeMem->Main;
if (offset >= Ps2MemSize::MainRam)
return false;
mmap_ClearCpuBlock(offset);
return true;
}
}
// Clears all block tracking statuses, manual protection flags, and write protection.
// This does not clear any recompiler blocks. It is assumed (and necessary) for the caller
// to ensure the EErec is also reset in conjunction with calling this function.
// (this function is called by default from the eerecReset).
void mmap_ResetBlockTracking()
{
//DbgCon.WriteLn( "vtlb/mmap: Block Tracking reset..." );
memzero(m_PageProtectInfo);
if (eeMem)
HostSys::MemProtect(eeMem->Main, Ps2MemSize::MainRam, PageAccess_ReadWrite());
vtlb_UpdateFastmemProtection(0, Ps2MemSize::MainRam, PageAccess_ReadWrite());
}

View File

@ -17,8 +17,7 @@
#include "MemoryTypes.h"
#include "SingleRegisterTypes.h"
#include "common/PageFaultSource.h"
#include "VirtualMemory.h"
static const uptr VTLB_AllocUpperBounds = _1gb * 2;
@ -51,7 +50,7 @@ template<> struct vtlbMemFP<128, true> { typedef vtlbMemW128FP fn; static const
typedef u32 vtlbHandler;
extern void vtlb_Core_Alloc();
extern bool vtlb_Core_Alloc();
extern void vtlb_Core_Free();
extern void vtlb_Alloc_Ppmap();
extern void vtlb_Init();
@ -289,6 +288,18 @@ namespace vtlb_private
}
}
enum vtlb_ProtectionMode
{
ProtMode_None = 0, // page is 'unaccounted' -- neither protected nor unprotected
ProtMode_Write, // page is under write protection (exception handler)
ProtMode_Manual, // page is under manual protection (self-checked at execution)
ProtMode_NotRequired // page doesn't require any protection
};
extern vtlb_ProtectionMode mmap_GetRamPageInfo(u32 paddr);
extern void mmap_MarkCountedRamPage(u32 paddr);
extern void mmap_ResetBlockTracking();
// --------------------------------------------------------------------------------------
// Goemon game fix
// --------------------------------------------------------------------------------------

View File

@ -23,11 +23,11 @@
#include "iR3000A.h"
#include "R3000A.h"
#include "BaseblockEx.h"
#include "System.h"
#include "R5900OpcodeTables.h"
#include "IopBios.h"
#include "IopHw.h"
#include "Common.h"
#include "VirtualMemory.h"
#include "VMManager.h"
#include <time.h>

View File

@ -24,8 +24,7 @@
#include "iR5900.h"
#include "iR5900Analysis.h"
#include "BaseblockEx.h"
#include "System.h"
#include "VirtualMemory.h"
#include "vtlb.h"
#include "VMManager.h"

View File

@ -30,7 +30,7 @@ using namespace x86Emitter;
#include "Gif_Unit.h"
#include "iR5900.h"
#include "R5900OpcodeTables.h"
#include "System.h"
#include "VirtualMemory.h"
#include "common/emitter/x86emitter.h"
#include "microVU_Misc.h"
#include "microVU_IR.h"

View File

@ -17,9 +17,9 @@
#include "Vif.h"
#include "VU.h"
#include "VirtualMemory.h"
#include "common/emitter/x86emitter.h"
#include "System.h"
using namespace x86Emitter;