From 4cf041f6cbc25e4d4bd2c792664b104ea27eefe3 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Wed, 28 Dec 2022 16:53:37 +1000 Subject: [PATCH] Common: Move VirtualMemory related functionality to core Also rewrites page fault handling to not use EventSource junk. --- common/CMakeLists.txt | 3 +- common/General.cpp | 39 ++ common/General.h | 14 + common/Linux/LnxHostSys.cpp | 128 +++-- common/Windows/WinHostSys.cpp | 96 ++-- common/common.vcxproj | 2 +- common/common.vcxproj.filters | 4 +- pcsx2/CMakeLists.txt | 2 + pcsx2/GS/Renderers/Common/GSFunctionMap.cpp | 40 +- pcsx2/GS/Renderers/Common/GSFunctionMap.h | 31 +- pcsx2/GS/Renderers/SW/GSDrawScanline.cpp | 10 +- pcsx2/Memory.cpp | 175 ------ pcsx2/Memory.h | 12 - pcsx2/System.cpp | 127 +---- pcsx2/System.h | 65 +-- {common => pcsx2}/VirtualMemory.cpp | 140 ++--- .../VirtualMemory.h | 152 +----- pcsx2/pcsx2core.vcxproj | 3 +- pcsx2/pcsx2core.vcxproj.filters | 9 +- pcsx2/vtlb.cpp | 498 ++++++++++++------ pcsx2/vtlb.h | 17 +- pcsx2/x86/iR3000A.cpp | 2 +- pcsx2/x86/ix86-32/iR5900-32.cpp | 3 +- pcsx2/x86/microVU.h | 2 +- pcsx2/x86/newVif.h | 2 +- 25 files changed, 763 insertions(+), 813 deletions(-) create mode 100644 common/General.cpp rename {common => pcsx2}/VirtualMemory.cpp (81%) rename common/PageFaultSource.h => pcsx2/VirtualMemory.h (60%) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index a74cfd263..8f8303efa 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -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 diff --git a/common/General.cpp b/common/General.cpp new file mode 100644 index 000000000..cb32ddc91 --- /dev/null +++ b/common/General.cpp @@ -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 . + */ + +#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; +} diff --git a/common/General.h b/common/General.h index c0aa8b58c..24a82a932 100644 --- a/common/General.h +++ b/common/General.h @@ -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 diff --git a/common/Linux/LnxHostSys.cpp b/common/Linux/LnxHostSys.cpp index e760a37cc..e17123d6a 100644 --- a/common/Linux/LnxHostSys.cpp +++ b/common/Linux/LnxHostSys.cpp @@ -21,13 +21,15 @@ #include #include +#include + #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 #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(static_cast(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..."); - struct sigaction sa; + 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__) - // MacOS uses SIGBUS for memory permission violations - sigaction(SIGBUS, &sa, &s_old_sigbus_action); + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = SysPageFaultSignalFilter; +#ifdef __APPLE__ + // MacOS uses SIGBUS for memory permission violations + 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 } diff --git a/common/Windows/WinHostSys.cpp b/common/Windows/WinHostSys.cpp index 6fc236ca9..558de9433 100644 --- a/common/Windows/WinHostSys.cpp +++ b/common/Windows/WinHostSys.cpp @@ -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 -#if defined(_M_AMD64) - void* const exception_pc = reinterpret_cast(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(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; diff --git a/common/common.vcxproj b/common/common.vcxproj index 5605ff9e2..68bdfd19f 100644 --- a/common/common.vcxproj +++ b/common/common.vcxproj @@ -70,6 +70,7 @@ true + @@ -90,7 +91,6 @@ - diff --git a/common/common.vcxproj.filters b/common/common.vcxproj.filters index d34353b4f..c419d377f 100644 --- a/common/common.vcxproj.filters +++ b/common/common.vcxproj.filters @@ -67,9 +67,6 @@ Source Files - - Source Files - Source Files @@ -211,6 +208,7 @@ Source Files + diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index e22465a37..5e35b4890 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -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 diff --git a/pcsx2/GS/Renderers/Common/GSFunctionMap.cpp b/pcsx2/GS/Renderers/Common/GSFunctionMap.cpp index a131e8871..2b29062b8 100644 --- a/pcsx2/GS/Renderers/Common/GSFunctionMap.cpp +++ b/pcsx2/GS/Renderers/Common/GSFunctionMap.cpp @@ -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; +} diff --git a/pcsx2/GS/Renderers/Common/GSFunctionMap.h b/pcsx2/GS/Renderers/Common/GSFunctionMap.h index 4496ad02c..573d7188a 100644 --- a/pcsx2/GS/Renderers/Common/GSFunctionMap.h +++ b/pcsx2/GS/Renderers/Common/GSFunctionMap.h @@ -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 @@ -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 GSCodeGeneratorFunctionMap : public GSFunctionMap { @@ -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(); diff --git a/pcsx2/GS/Renderers/SW/GSDrawScanline.cpp b/pcsx2/GS/Renderers/SW/GSDrawScanline.cpp index 1b1de5e33..c6f928d44 100644 --- a/pcsx2/GS/Renderers/SW/GSDrawScanline.cpp +++ b/pcsx2/GS/Renderers/SW/GSDrawScanline.cpp @@ -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) diff --git a/pcsx2/Memory.cpp b/pcsx2/Memory.cpp index c494ca0bd..eb33501d7 100644 --- a/pcsx2/Memory.cpp +++ b/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(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()); -} diff --git a/pcsx2/Memory.h b/pcsx2/Memory.h index a4c8dd3da..ad8f14b66 100644 --- a/pcsx2/Memory.h +++ b/pcsx2/Memory.h @@ -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 #define memRead16 vtlb_memRead #define memRead32 vtlb_memRead diff --git a/pcsx2/System.cpp b/pcsx2/System.cpp index 850dbbbca..534c08d1e 100644 --- a/pcsx2/System.cpp +++ b/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) { diff --git a/pcsx2/System.h b/pcsx2/System.h index 83d73239b..cfb0e4ca8 100644 --- a/pcsx2/System.h +++ b/pcsx2/System.h @@ -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 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(); diff --git a/common/VirtualMemory.cpp b/pcsx2/VirtualMemory.cpp similarity index 81% rename from common/VirtualMemory.cpp rename to pcsx2/VirtualMemory.cpp index 418a1be5c..e479883e2 100644 --- a/common/VirtualMemory.cpp +++ b/pcsx2/VirtualMemory.cpp @@ -13,62 +13,18 @@ * If not, see . */ +#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 -template class EventSource; - -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; } diff --git a/common/PageFaultSource.h b/pcsx2/VirtualMemory.h similarity index 60% rename from common/PageFaultSource.h rename to pcsx2/VirtualMemory.h index 861d6a153..820d7857d 100644 --- a/common/PageFaultSource.h +++ b/pcsx2/VirtualMemory.h @@ -13,123 +13,15 @@ * If not, see . */ -// [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 #include #include #include -struct PageFaultInfo -{ - uptr pc; - uptr addr; - - PageFaultInfo(uptr pc_, uptr address) - { - pc = pc_; - addr = address; - } -}; - -// -------------------------------------------------------------------------------------- -// IEventListener_PageFault -// -------------------------------------------------------------------------------------- -class IEventListener_PageFault : public IEventDispatcher -{ -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 -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 -{ -protected: - typedef EventSource _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(); +}; \ No newline at end of file diff --git a/pcsx2/pcsx2core.vcxproj b/pcsx2/pcsx2core.vcxproj index 0fa426b43..ea87ed9eb 100644 --- a/pcsx2/pcsx2core.vcxproj +++ b/pcsx2/pcsx2core.vcxproj @@ -352,6 +352,7 @@ + @@ -711,7 +712,6 @@ - @@ -719,6 +719,7 @@ + diff --git a/pcsx2/pcsx2core.vcxproj.filters b/pcsx2/pcsx2core.vcxproj.filters index 7f72e1334..24d2729e0 100644 --- a/pcsx2/pcsx2core.vcxproj.filters +++ b/pcsx2/pcsx2core.vcxproj.filters @@ -1385,6 +1385,9 @@ Tools + + System + @@ -1420,9 +1423,6 @@ System\Include - - System\Include - System\Ps2\EmotionEngine @@ -2318,6 +2318,9 @@ System\Ps2\GS\GIF + + System + diff --git a/pcsx2/vtlb.cpp b/pcsx2/vtlb.cpp index f0e57b3e5..f0432fe87 100644 --- a/pcsx2/vtlb.cpp +++ b/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; @@ -91,26 +94,32 @@ static constexpr u32 NO_FASTMEM_MAPPING = 0xFFFFFFFFu; static std::unique_ptr s_fastmem_area; static std::vector s_fastmem_virtual_mapping; // maps vaddr -> mainmem offset -static std::unordered_multimap s_fastmem_physical_mapping; // maps mainmem offset -> vaddr +static std::unordered_multimap s_fastmem_physical_mapping; // maps mainmem offset -> vaddr static std::unordered_map s_fastmem_backpatch_info; static std::unordered_set 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) { - mask = tlb[i].PageMask; + 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) { - mask = tlb[i].PageMask; + 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 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); @@ -178,7 +191,7 @@ DataType vtlb_memRead(u32 addr) return readCache64(addr); break; - jNO_DEFAULT; + jNO_DEFAULT; } } } @@ -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::HandlerType*>(vtlbdata.RWFT[TemplateHelper::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: @@ -202,21 +215,21 @@ DataType vtlb_memRead(u32 addr) case 64: return vmv.assumeHandler<64, false>()(paddr); - jNO_DEFAULT; + jNO_DEFAULT; } - return 0; // technically unreachable, but suppresses warnings. + return 0; // technically unreachable, but suppresses warnings. } 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,57 +246,57 @@ RETURNS_R128 vtlb_memRead128(u32 mem) } } -template< typename DataType > +template 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); - return; - case 16: - writeCache16(addr, data); - return; - case 32: - writeCache32(addr, data); - return; - case 64: - writeCache64(addr, data); - return; + case 8: + writeCache8(addr, data); + return; + case 16: + writeCache16(addr, data); + return; + case 32: + writeCache32(addr, data); + return; + case 64: + writeCache64(addr, data); + return; } } } - *reinterpret_cast(vmv.assumePtr(addr))=data; + *reinterpret_cast(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()(paddr, data); + return vmv.assumeHandler()(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,20 +393,23 @@ 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 size = tlb[i].high_add - tlb[i].low_add; u32 vaddr = tlb[i].low_add; u32 paddr = tlb[i].physical_add; // 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,23 +419,28 @@ 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) { - u32 size = tlb[i].high_add - tlb[i].low_add; + 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 - tlb[i].valid = 0; - tlb[i].key = 0xFEFEFEFE; - tlb[i].low_add = 0xFEFEFEFE; + tlb[i].valid = 0; + 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 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 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,45 +669,45 @@ 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; - loopsz -= VTLB_PAGE_SIZE; + start += VTLB_PAGE_SIZE; + ptr += VTLB_PAGE_SIZE; + loopsz -= VTLB_PAGE_SIZE; } } } -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; + start += VTLB_PAGE_SIZE; + new_region += VTLB_PAGE_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(vtlbdata.pmap[paddr>>VTLB_PAGE_BITS].assumePtr()+(paddr&VTLB_PAGE_MASK)); + return reinterpret_cast(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]; - paddr |= vaddr & VTLB_PAGE_MASK; + 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) @@ -836,7 +861,7 @@ static void vtlb_CreateFastmemMapping(u32 vaddr, u32 mainmem_offset, const PageP const u32 host_offset = vtlb_HostAlignOffset(mainmem_offset); if (!s_fastmem_area->Map(GetVmMemory().MainMemory()->GetFileHandle(), host_offset, - s_fastmem_area->PagePointer(host_page), __pagesize, mode)) + s_fastmem_area->PagePointer(host_page), __pagesize, mode)) { Console.Error("Failed to map vaddr %08X to mainmem offset %08X", vtlb_HostAlignOffset(vaddr), host_offset); s_fastmem_virtual_mapping[page] = NO_FASTMEM_MAPPING; @@ -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,14 +1141,14 @@ 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) \ - baseName##ReadSm, baseName##ReadSm, baseName##ReadSm, \ - baseName##ReadSm, baseName##ReadLg, \ - baseName##WriteSm, baseName##WriteSm, baseName##WriteSm, \ - baseName##WriteSm, baseName##WriteLg + baseName##ReadSm, baseName##ReadSm, baseName##ReadSm, \ + baseName##ReadSm, baseName##ReadLg, \ + baseName##WriteSm, baseName##WriteSm, baseName##WriteSm, \ + baseName##WriteSm, baseName##WriteLg //Register default handlers //Unmapped Virt handlers _MUST_ be registered first. @@ -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<><< 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()); +} diff --git a/pcsx2/vtlb.h b/pcsx2/vtlb.h index 475d546c9..ba36ca31d 100644 --- a/pcsx2/vtlb.h +++ b/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 // -------------------------------------------------------------------------------------- diff --git a/pcsx2/x86/iR3000A.cpp b/pcsx2/x86/iR3000A.cpp index b49b27d68..c076889f6 100644 --- a/pcsx2/x86/iR3000A.cpp +++ b/pcsx2/x86/iR3000A.cpp @@ -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 diff --git a/pcsx2/x86/ix86-32/iR5900-32.cpp b/pcsx2/x86/ix86-32/iR5900-32.cpp index d28d46eed..b1b1b684b 100644 --- a/pcsx2/x86/ix86-32/iR5900-32.cpp +++ b/pcsx2/x86/ix86-32/iR5900-32.cpp @@ -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" diff --git a/pcsx2/x86/microVU.h b/pcsx2/x86/microVU.h index 7d3b04cf9..9924b0086 100644 --- a/pcsx2/x86/microVU.h +++ b/pcsx2/x86/microVU.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" diff --git a/pcsx2/x86/newVif.h b/pcsx2/x86/newVif.h index e915e5de2..10aeb8db4 100644 --- a/pcsx2/x86/newVif.h +++ b/pcsx2/x86/newVif.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;