mirror of https://github.com/PCSX2/pcsx2.git
Common: Move VirtualMemory related functionality to core
Also rewrites page fault handling to not use EventSource junk.
This commit is contained in:
parent
1b86a6e6f8
commit
4cf041f6cb
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
175
pcsx2/Memory.cpp
175
pcsx2/Memory.cpp
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
127
pcsx2/System.cpp
127
pcsx2/System.cpp
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
};
|
|
@ -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" />
|
||||
|
|
|
@ -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">
|
||||
|
|
430
pcsx2/vtlb.cpp
430
pcsx2/vtlb.cpp
|
@ -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());
|
||||
}
|
||||
|
|
17
pcsx2/vtlb.h
17
pcsx2/vtlb.h
|
@ -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
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
|
||||
#include "Vif.h"
|
||||
#include "VU.h"
|
||||
#include "VirtualMemory.h"
|
||||
|
||||
#include "common/emitter/x86emitter.h"
|
||||
#include "System.h"
|
||||
|
||||
using namespace x86Emitter;
|
||||
|
||||
|
|
Loading…
Reference in New Issue