diff --git a/.github/workflows/scripts/linux/flatpak/modules/20-sdl2.json b/.github/workflows/scripts/linux/flatpak/modules/20-sdl2.json index 6fc45065d8561..20baa81195250 100644 --- a/.github/workflows/scripts/linux/flatpak/modules/20-sdl2.json +++ b/.github/workflows/scripts/linux/flatpak/modules/20-sdl2.json @@ -14,8 +14,8 @@ "sources": [ { "type": "archive", - "url": "https://libsdl.org/release/SDL2-2.30.2.tar.gz", - "sha256": "891d66ac8cae51361d3229e3336ebec1c407a8a2a063b61df14f5fdf3ab5ac31" + "url": "https://libsdl.org/release/SDL2-2.30.3.tar.gz", + "sha256": "820440072f8f5b50188c1dae104f2ad25984de268785be40c41a099a510f0aec" } ], "cleanup": [ diff --git a/cmake/SearchForStuff.cmake b/cmake/SearchForStuff.cmake index b8339b136792c..ceac56aed5cf7 100644 --- a/cmake/SearchForStuff.cmake +++ b/cmake/SearchForStuff.cmake @@ -17,7 +17,7 @@ find_package(ZLIB REQUIRED) # v1.3, but Mac uses the SDK version. find_package(Zstd 1.5.5 REQUIRED) find_package(LZ4 REQUIRED) find_package(WebP REQUIRED) # v1.3.2, spews an error on Linux because no pkg-config. -find_package(SDL2 2.30.2 REQUIRED) +find_package(SDL2 2.30.3 REQUIRED) find_package(Freetype 2.11.1 REQUIRED) if(USE_VULKAN) diff --git a/common/CrashHandler.cpp b/common/CrashHandler.cpp index 665dc2364085f..74b99304012e8 100644 --- a/common/CrashHandler.cpp +++ b/common/CrashHandler.cpp @@ -1,8 +1,9 @@ -// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team +// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team // SPDX-License-Identifier: LGPL-3.0+ #include "Pcsx2Defs.h" #include "CrashHandler.h" +#include "DynamicLibrary.h" #include "FileSystem.h" #include "StringUtil.h" #include @@ -75,8 +76,7 @@ static bool WriteMinidump(HMODULE hDbgHelp, HANDLE hFile, HANDLE hProcess, DWORD } static std::wstring s_write_directory; -static HMODULE s_dbghelp_module = nullptr; -static PVOID s_veh_handle = nullptr; +static DynamicLibrary s_dbghelp_module; static bool s_in_crash_handler = false; static void GenerateCrashFilename(wchar_t* buf, size_t len, const wchar_t* prefix, const wchar_t* extension) @@ -114,7 +114,7 @@ static void WriteMinidumpAndCallstack(PEXCEPTION_POINTERS exi) MiniDumpWithThreadInfo | MiniDumpWithIndirectlyReferencedMemory); const HANDLE hMinidumpFile = CreateFileW(filename, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, nullptr); if (hMinidumpFile == INVALID_HANDLE_VALUE || - !WriteMinidump(s_dbghelp_module, hMinidumpFile, GetCurrentProcess(), GetCurrentProcessId(), + !WriteMinidump(static_cast(s_dbghelp_module.GetHandle()), hMinidumpFile, GetCurrentProcess(), GetCurrentProcessId(), GetCurrentThreadId(), exi, minidump_type)) { static const char error_message[] = "Failed to write minidump file.\n"; @@ -136,32 +136,13 @@ static void WriteMinidumpAndCallstack(PEXCEPTION_POINTERS exi) static LONG NTAPI ExceptionHandler(PEXCEPTION_POINTERS exi) { - if (s_in_crash_handler) - return EXCEPTION_CONTINUE_SEARCH; + // if the debugger is attached, or we're recursively crashing, let it take care of it. + if (!s_in_crash_handler) + WriteMinidumpAndCallstack(exi); - switch (exi->ExceptionRecord->ExceptionCode) - { - case EXCEPTION_ACCESS_VIOLATION: - case EXCEPTION_BREAKPOINT: - case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: - case EXCEPTION_INT_DIVIDE_BY_ZERO: - case EXCEPTION_INT_OVERFLOW: - case EXCEPTION_PRIV_INSTRUCTION: - case EXCEPTION_ILLEGAL_INSTRUCTION: - case EXCEPTION_NONCONTINUABLE_EXCEPTION: - case EXCEPTION_STACK_OVERFLOW: - case EXCEPTION_GUARD_PAGE: - break; - - default: - return EXCEPTION_CONTINUE_SEARCH; - } - - // if the debugger is attached, let it take care of it. - if (IsDebuggerPresent()) - return EXCEPTION_CONTINUE_SEARCH; - - WriteMinidumpAndCallstack(exi); + // returning EXCEPTION_CONTINUE_SEARCH makes sense, except for the fact that it seems to leave zombie processes + // around. instead, force ourselves to terminate. + TerminateProcess(GetCurrentProcess(), 0xFEFEFEFEu); return EXCEPTION_CONTINUE_SEARCH; } @@ -169,17 +150,16 @@ bool CrashHandler::Install() { // load dbghelp at install/startup, that way we're not LoadLibrary()'ing after a crash // .. because that probably wouldn't go down well. - s_dbghelp_module = StackWalker::LoadDbgHelpLibrary(); + HMODULE mod = StackWalker::LoadDbgHelpLibrary(); + if (mod) + s_dbghelp_module.Adopt(mod); - s_veh_handle = AddVectoredExceptionHandler(0, ExceptionHandler); - return (s_veh_handle != nullptr); + SetUnhandledExceptionFilter(ExceptionHandler); + return true; } -void CrashHandler::SetWriteDirectory(const std::string_view& dump_directory) +void CrashHandler::SetWriteDirectory(std::string_view dump_directory) { - if (!s_veh_handle) - return; - s_write_directory = FileSystem::GetWin32Path(dump_directory); } @@ -188,21 +168,6 @@ void CrashHandler::WriteDumpForCaller() WriteMinidumpAndCallstack(nullptr); } -void CrashHandler::Uninstall() -{ - if (s_veh_handle) - { - RemoveVectoredExceptionHandler(s_veh_handle); - s_veh_handle = nullptr; - } - - if (s_dbghelp_module) - { - FreeLibrary(s_dbghelp_module); - s_dbghelp_module = nullptr; - } -} - #elif defined(HAS_LIBBACKTRACE) #include "FileSystem.h" @@ -382,7 +347,7 @@ bool CrashHandler::Install() return true; } -void CrashHandler::SetWriteDirectory(const std::string_view& dump_directory) +void CrashHandler::SetWriteDirectory(std::string_view dump_directory) { } @@ -390,12 +355,6 @@ void CrashHandler::WriteDumpForCaller() { } -void CrashHandler::Uninstall() -{ - // We can't really unchain the signal handlers... so, YOLO. -} - - #else bool CrashHandler::Install() @@ -403,7 +362,7 @@ bool CrashHandler::Install() return false; } -void CrashHandler::SetWriteDirectory(const std::string_view& dump_directory) +void CrashHandler::SetWriteDirectory(std::string_view dump_directory) { } @@ -411,8 +370,4 @@ void CrashHandler::WriteDumpForCaller() { } -void CrashHandler::Uninstall() -{ -} - #endif \ No newline at end of file diff --git a/common/CrashHandler.h b/common/CrashHandler.h index f626da087c58c..333d612324e13 100644 --- a/common/CrashHandler.h +++ b/common/CrashHandler.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team +// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team // SPDX-License-Identifier: LGPL-3.0+ #include @@ -6,7 +6,6 @@ namespace CrashHandler { bool Install(); - void SetWriteDirectory(const std::string_view& dump_directory); + void SetWriteDirectory(std::string_view dump_directory); void WriteDumpForCaller(); - void Uninstall(); } // namespace CrashHandler diff --git a/common/DynamicLibrary.cpp b/common/DynamicLibrary.cpp index 3a017b1d21af5..9f8c2229ff2ab 100644 --- a/common/DynamicLibrary.cpp +++ b/common/DynamicLibrary.cpp @@ -100,6 +100,15 @@ bool DynamicLibrary::Open(const char* filename, Error* error) #endif } +void DynamicLibrary::Adopt(void* handle) +{ + pxAssertRel(handle, "Handle is valid"); + + Close(); + + m_handle = handle; +} + void DynamicLibrary::Close() { if (!IsOpen()) diff --git a/common/DynamicLibrary.h b/common/DynamicLibrary.h index 65082912ab86d..8767366f5b25d 100644 --- a/common/DynamicLibrary.h +++ b/common/DynamicLibrary.h @@ -45,6 +45,9 @@ class DynamicLibrary final /// Returns true if the library was loaded and can be used. bool Open(const char* filename, Error* error); + /// Adopts, or takes ownership of an existing opened library. + void Adopt(void* handle); + /// Unloads the library, any function pointers from this library are no longer valid. void Close(); @@ -61,6 +64,9 @@ class DynamicLibrary final return *ptr != nullptr; } + /// Returns the opaque OS-specific handle. + void* GetHandle() const { return m_handle; } + /// Move assignment, transfer ownership. DynamicLibrary& operator=(DynamicLibrary&& move); diff --git a/common/Error.cpp b/common/Error.cpp index 1d7b54a15337d..9877e4bc73a58 100644 --- a/common/Error.cpp +++ b/common/Error.cpp @@ -141,12 +141,13 @@ void Error::SetHResult(std::string_view prefix, long err) static_cast(std::size(buf)), nullptr); if (r > 0) { - m_description = - fmt::format("{}HRESULT {:08X}: {}", prefix, err, StringUtil::WideStringToUTF8String(std::wstring_view(buf, r))); + m_description = fmt::format("{}HRESULT {:08X}: {}", prefix, static_cast(err), + StringUtil::WideStringToUTF8String(std::wstring_view(buf, r))); } else { - m_description = fmt::format("{}HRESULT {:08X}: ", prefix, err); + m_description = fmt::format("{}HRESULT {:08X}: ", prefix, + static_cast(err)); } } diff --git a/common/HostSys.h b/common/HostSys.h index 988429c49ce3e..9fc0aaf908fad 100644 --- a/common/HostSys.h +++ b/common/HostSys.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team +// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team // SPDX-License-Identifier: LGPL-3.0+ #pragma once @@ -10,6 +10,8 @@ #include #include +class Error; + // -------------------------------------------------------------------------------------- // PageProtectionMode // -------------------------------------------------------------------------------------- @@ -83,14 +85,6 @@ static __fi PageProtectionMode PageAccess_Any() return PageProtectionMode().All(); } -struct PageFaultInfo -{ - uptr pc; - uptr addr; -}; - -using PageFaultHandler = bool(*)(const PageFaultInfo& info); - // -------------------------------------------------------------------------------------- // HostSys // -------------------------------------------------------------------------------------- @@ -111,12 +105,6 @@ namespace HostSys 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); - /// JIT write protect for Apple Silicon. Needs to be called prior to writing to any RWX pages. #if !defined(__APPLE__) || !defined(_M_ARM64) // clang-format -off @@ -137,6 +125,12 @@ namespace HostSys #endif } +namespace PageFaultHandler +{ + bool HandlePageFault(uptr pc, uptr addr, bool is_write); + bool Install(Error* error); +} // namespace PageFaultHandler + class SharedMemoryMappingArea { public: diff --git a/common/Linux/LnxHostSys.cpp b/common/Linux/LnxHostSys.cpp index a01ce9b998c0b..397cfa24e07e0 100644 --- a/common/Linux/LnxHostSys.cpp +++ b/common/Linux/LnxHostSys.cpp @@ -17,9 +17,10 @@ #include "fmt/core.h" -#include "common/BitUtils.h" #include "common/Assertions.h" +#include "common/BitUtils.h" #include "common/Console.h" +#include "common/Error.h" #include "common/HostSys.h" // Apple uses the MAP_ANON define instead of MAP_ANONYMOUS, but they mean @@ -38,8 +39,8 @@ #endif static std::recursive_mutex s_exception_handler_mutex; -static PageFaultHandler s_exception_handler_callback; -static bool s_in_exception_handler; +static bool s_in_exception_handler = false; +static bool s_exception_handler_installed = false; #ifdef __APPLE__ #include @@ -54,6 +55,43 @@ static struct sigaction s_old_sigbus_action; static struct sigaction s_old_sigsegv_action; #endif +#ifdef __aarch64__ +[[maybe_unused]] static bool IsStoreInstruction(uptr ptr) +{ + u32 bits; + std::memcpy(&bits, reinterpret_cast(pc), sizeof(bits)); + + // Based on vixl's disassembler Instruction::IsStore(). + // if (Mask(LoadStoreAnyFMask) != LoadStoreAnyFixed) + if ((bits & 0x0a000000) != 0x08000000) + return false; + + // if (Mask(LoadStorePairAnyFMask) == LoadStorePairAnyFixed) + if ((bits & 0x3a000000) == 0x28000000) + { + // return Mask(LoadStorePairLBit) == 0 + return (bits & (1 << 22)) == 0; + } + + switch (bits & 0xC4C00000) + { + case 0x00000000: // STRB_w + case 0x40000000: // STRH_w + case 0x80000000: // STR_w + case 0xC0000000: // STR_x + case 0x04000000: // STR_b + case 0x44000000: // STR_h + case 0x84000000: // STR_s + case 0xC4000000: // STR_d + case 0x04800000: // STR_q + return true; + + default: + return false; + } +} +#endif + static void CallExistingSignalHandler(int signal, siginfo_t* siginfo, void* ctx) { #if defined(__aarch64__) @@ -81,7 +119,7 @@ static void CallExistingSignalHandler(int signal, siginfo_t* siginfo, void* ctx) } // Linux implementation of SIGSEGV handler. Bind it using sigaction(). -static void SysPageFaultSignalFilter(int signal, siginfo_t* siginfo, void* ctx) +static void SysPageFaultSignalFilter(int signal, siginfo_t* info, void* ctx) { // Executing the handler concurrently from multiple threads wouldn't go down well. std::unique_lock lock(s_exception_handler_mutex); @@ -90,37 +128,44 @@ static void SysPageFaultSignalFilter(int signal, siginfo_t* siginfo, void* ctx) if (s_in_exception_handler) { lock.unlock(); - CallExistingSignalHandler(signal, siginfo, ctx); + CallExistingSignalHandler(signal, info, ctx); return; } - // 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); -#elif defined(__FreeBSD__) && defined(__x86_64__) - void* const exception_pc = reinterpret_cast(static_cast(ctx)->uc_mcontext.mc_rip); -#elif defined(__x86_64__) - void* const exception_pc = reinterpret_cast(static_cast(ctx)->uc_mcontext.gregs[REG_RIP]); -#elif defined(__aarch64__) - #ifndef __APPLE__ - void* const exception_pc = reinterpret_cast(static_cast(ctx)->uc_mcontext.pc); - #else - void* const exception_pc = reinterpret_cast(static_cast(ctx)->uc_mcontext->__ss.__pc); +#if defined(__linux__) + const uptr exception_address = reinterpret_cast(info->si_addr); + #if defined(__x86_64__) + const uptr exception_pc = static_cast(static_cast(ctx)->uc_mcontext.gregs[REG_RIP]); + const bool is_write = (static_cast(ctx)->uc_mcontext.gregs[REG_ERR] & 2) != 0; + #elif defined(__aarch64__) + const uptr exception_pc = static_cast(static_cast(ctx)->uc_mcontext.pc); + const bool is_write = IsStoreInstruction(exception_pc); + #endif +#elif defined(__APPLE__) + #if defined(__x86_64__) + const uptr exception_pc = static_cast(static_cast(ctx)->uc_mcontext->__ss.__rip); + const uptr exception_address = static_cast(static_cast(ctx)->uc_mcontext->__es.__faultvaddr); + const bool is_write = (static_cast(ctx)->uc_mcontext->__es.__err & 2) != 0; + #elif defined(__aarch64__) + const uptr exception_address = static_cast(static_cast(ctx)->uc_mcontext->__es.__far); + const uptr exception_pc = static_cast(static_cast(ctx)->uc_mcontext->__ss.__pc); + const bool is_write = IsStoreInstruction(exception_pc); + #endif +#elif defined(__FreeBSD__) + #if defined(__x86_64__) + const uptr exception_address = static_cast(static_cast(ctx)->uc_mcontext.mc_addr); + const uptr exception_pc = static_cast(static_cast(ctx)->uc_mcontext.mc_rip); + const bool is_write = (static_cast(ctx)->uc_mcontext.mc_err & 2) != 0; + #elif defined(__aarch64__) + const uptr exception_address = static_cast(static_cast(ctx)->uc_mcontext->__es.__far); + const uptr exception_pc = static_cast(static_cast(ctx)->uc_mcontext->__ss.__pc); + const bool is_write = IsStoreInstruction(exception_pc); #endif -#else - void* const exception_pc = nullptr; #endif - const PageFaultInfo pfi{ - reinterpret_cast(exception_pc), reinterpret_cast(siginfo->si_addr) & ~static_cast(__pagemask)}; - s_in_exception_handler = true; - const bool handled = s_exception_handler_callback(pfi); + const bool handled = PageFaultHandler::HandlePageFault(exception_pc, exception_address, is_write); s_in_exception_handler = false; @@ -130,62 +175,47 @@ static void SysPageFaultSignalFilter(int signal, siginfo_t* siginfo, void* ctx) // Call old signal handler, which will likely dump core. lock.unlock(); - CallExistingSignalHandler(signal, siginfo, ctx); + CallExistingSignalHandler(signal, info, ctx); } -bool HostSys::InstallPageFaultHandler(PageFaultHandler handler) +bool PageFaultHandler::Install(Error* error) { 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; + pxAssertRel(!s_exception_handler_installed, "Page fault handler has already been installed."); + + struct sigaction sa; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_SIGINFO; - sa.sa_sigaction = SysPageFaultSignalFilter; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = SysPageFaultSignalFilter; #ifdef __linux__ - // Don't block the signal from executing recursively, we want to fire the original handler. - sa.sa_flags |= SA_NODEFER; + // Don't block the signal from executing recursively, we want to fire the original handler. + sa.sa_flags |= SA_NODEFER; #endif #if defined(__APPLE__) || defined(__aarch64__) - // MacOS uses SIGBUS for memory permission violations, as well as SIGSEGV on ARM64. - if (sigaction(SIGBUS, &sa, &s_old_sigbus_action) != 0) - return false; + // MacOS uses SIGBUS for memory permission violations, as well as SIGSEGV on ARM64. + if (sigaction(SIGBUS, &sa, &s_old_sigbus_action) != 0) + { + Error::SetErrno(error, "sigaction() for SIGSEGV failed: ", errno); + return false; + } #endif #if !defined(__APPLE__) || defined(__aarch64__) - if (sigaction(SIGSEGV, &sa, &s_old_sigsegv_action) != 0) - return false; + if (sigaction(SIGSEGV, &sa, &s_old_sigsegv_action) != 0) + { + Error::SetErrno(error, "sigaction() for SIGBUS failed: ", errno); + return false; + } #endif #if defined(__APPLE__) && defined(__aarch64__) - // Stops LLDB getting in a EXC_BAD_ACCESS loop when passing page faults to PCSX2. - task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, MACH_PORT_NULL, EXCEPTION_DEFAULT, 0); + // Stops LLDB getting in a EXC_BAD_ACCESS loop when passing page faults to PCSX2. + task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, MACH_PORT_NULL, EXCEPTION_DEFAULT, 0); #endif - } - s_exception_handler_callback = handler; + s_exception_handler_installed = true; 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; -#if defined(__APPLE__) || defined(__aarch64__) - sigaction(SIGBUS, &s_old_sigbus_action, &sa); -#endif -#if !defined(__APPLE__) || defined(__aarch64__) - sigaction(SIGSEGV, &s_old_sigsegv_action, &sa); -#endif -} - static __ri uint LinuxProt(const PageProtectionMode& mode) { u32 lnxmode = 0; diff --git a/common/Windows/WinHostSys.cpp b/common/Windows/WinHostSys.cpp index f66d3e2d0c772..23eea6cbcb167 100644 --- a/common/Windows/WinHostSys.cpp +++ b/common/Windows/WinHostSys.cpp @@ -1,15 +1,16 @@ -// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team +// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team // SPDX-License-Identifier: LGPL-3.0+ #if defined(_WIN32) -#include "common/BitUtils.h" -#include "common/RedtapeWindows.h" -#include "common/Console.h" #include "common/HostSys.h" -#include "common/StringUtil.h" #include "common/AlignedMalloc.h" #include "common/Assertions.h" +#include "common/BitUtils.h" +#include "common/Console.h" +#include "common/Error.h" +#include "common/RedtapeWindows.h" +#include "common/StringUtil.h" #include "fmt/core.h" #include "fmt/format.h" @@ -17,9 +18,8 @@ #include 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; +static bool s_in_exception_handler = false; +static bool s_exception_handler_installed = false; long __stdcall SysPageFaultExceptionFilter(EXCEPTION_POINTERS* eps) { @@ -35,53 +35,39 @@ long __stdcall SysPageFaultExceptionFilter(EXCEPTION_POINTERS* eps) return EXCEPTION_CONTINUE_SEARCH; #if defined(_M_AMD64) - void* const exception_pc = reinterpret_cast(eps->ContextRecord->Rip); + const uptr exception_pc = static_cast(eps->ContextRecord->Rip); #elif defined(_M_ARM64) - void* const exception_pc = reinterpret_cast(eps->ContextRecord->Pc); -#else - void* const exception_pc = nullptr; + const uptr exception_pc = static_cast(eps->ContextRecord->Pc); #endif - const PageFaultInfo pfi{(uptr)exception_pc, (uptr)eps->ExceptionRecord->ExceptionInformation[1]}; + const uptr exception_addr = static_cast(eps->ExceptionRecord->ExceptionInformation[1]); + const bool is_write = (eps->ExceptionRecord->ExceptionInformation[0] == 1); s_in_exception_handler = true; - const bool handled = s_exception_handler_callback(pfi); + const bool handled = PageFaultHandler::HandlePageFault(exception_pc, exception_addr, is_write); s_in_exception_handler = false; return handled ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH; } -bool HostSys::InstallPageFaultHandler(PageFaultHandler handler) +bool PageFaultHandler::Install(Error* error) { 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) + pxAssertRel(!s_exception_handler_installed, "Page fault handler has already been installed."); + + PVOID handle = AddVectoredExceptionHandler(1, SysPageFaultExceptionFilter); + if (!handle) { - s_exception_handler_handle = AddVectoredExceptionHandler(TRUE, SysPageFaultExceptionFilter); - if (!s_exception_handler_handle) - return false; + Error::SetWin32(error, "AddVectoredExceptionHandler() failed: ", GetLastError()); + return false; } - s_exception_handler_callback = handler; + s_exception_handler_installed = true; 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 = {}; - } -} - static DWORD ConvertToWinApi(const PageProtectionMode& mode) { DWORD winmode = PAGE_NOACCESS; diff --git a/pcsx2-qt/Settings/GameListSettingsWidget.cpp b/pcsx2-qt/Settings/GameListSettingsWidget.cpp index 04e690395ce48..8b5dd37a8ae60 100644 --- a/pcsx2-qt/Settings/GameListSettingsWidget.cpp +++ b/pcsx2-qt/Settings/GameListSettingsWidget.cpp @@ -40,16 +40,14 @@ GameListSettingsWidget::GameListSettingsWidget(SettingsWindow* dialog, QWidget* m_ui.searchDirectoryList->setCurrentIndex({}); m_ui.searchDirectoryList->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu); - connect(m_ui.searchDirectoryList, &QTableWidget::customContextMenuRequested, this, - &GameListSettingsWidget::onDirectoryListContextMenuRequested); - connect(m_ui.addSearchDirectoryButton, &QPushButton::clicked, this, - &GameListSettingsWidget::onAddSearchDirectoryButtonClicked); - connect(m_ui.removeSearchDirectoryButton, &QPushButton::clicked, this, - &GameListSettingsWidget::onRemoveSearchDirectoryButtonClicked); + connect(m_ui.searchDirectoryList, &QTableWidget::customContextMenuRequested, this, &GameListSettingsWidget::onDirectoryListContextMenuRequested); + connect(m_ui.searchDirectoryList, &QTableWidget::itemSelectionChanged, this, &GameListSettingsWidget::onDirectoryListSelectionChanged); + connect(m_ui.addSearchDirectoryButton, &QPushButton::clicked, this, &GameListSettingsWidget::onAddSearchDirectoryButtonClicked); + connect(m_ui.removeSearchDirectoryButton, &QPushButton::clicked, this, &GameListSettingsWidget::onRemoveSearchDirectoryButtonClicked); connect(m_ui.addExcludedFile, &QPushButton::clicked, this, &GameListSettingsWidget::onAddExcludedFileButtonClicked); connect(m_ui.addExcludedPath, &QPushButton::clicked, this, &GameListSettingsWidget::onAddExcludedPathButtonClicked); - connect(m_ui.removeExcludedPath, &QPushButton::clicked, this, - &GameListSettingsWidget::onRemoveExcludedPathButtonClicked); + connect(m_ui.removeExcludedPath, &QPushButton::clicked, this, &GameListSettingsWidget::onRemoveExcludedPathButtonClicked); + connect(m_ui.excludedPaths, &QListWidget::itemSelectionChanged, this, &GameListSettingsWidget::onExcludedPathsSelectionChanged); connect(m_ui.rescanAllGames, &QPushButton::clicked, this, &GameListSettingsWidget::onRescanAllGamesClicked); connect(m_ui.scanForNewGames, &QPushButton::clicked, this, &GameListSettingsWidget::onScanForNewGamesClicked); @@ -77,6 +75,8 @@ void GameListSettingsWidget::refreshExclusionList() const std::vector paths(Host::GetBaseStringListSetting("GameList", "ExcludedPaths")); for (const std::string& path : paths) m_ui.excludedPaths->addItem(QString::fromStdString(path)); + + m_ui.removeExcludedPath->setEnabled(false); } bool GameListSettingsWidget::event(QEvent* event) @@ -142,6 +142,8 @@ void GameListSettingsWidget::refreshDirectoryList() addPathToTable(entry, true); m_ui.searchDirectoryList->sortByColumn(0, Qt::AscendingOrder); + + m_ui.removeSearchDirectoryButton->setEnabled(false); } void GameListSettingsWidget::addSearchDirectory(const QString& path, bool recursive) @@ -185,6 +187,11 @@ void GameListSettingsWidget::onDirectoryListContextMenuRequested(const QPoint& p menu.exec(m_ui.searchDirectoryList->mapToGlobal(point)); } +void GameListSettingsWidget::onDirectoryListSelectionChanged() +{ + m_ui.removeSearchDirectoryButton->setEnabled(m_ui.searchDirectoryList->selectionModel()->hasSelection()); +} + void GameListSettingsWidget::addSearchDirectory(QWidget* parent_widget) { QString dir = @@ -258,6 +265,11 @@ void GameListSettingsWidget::onRemoveExcludedPathButtonClicked() g_main_window->refreshGameList(false); } +void GameListSettingsWidget::onExcludedPathsSelectionChanged() +{ + m_ui.removeExcludedPath->setEnabled(!m_ui.excludedPaths->selectedItems().isEmpty()); +} + void GameListSettingsWidget::onRescanAllGamesClicked() { g_main_window->refreshGameList(true); diff --git a/pcsx2-qt/Settings/GameListSettingsWidget.h b/pcsx2-qt/Settings/GameListSettingsWidget.h index b25f0dadf723b..5cc2cdaea7bdd 100644 --- a/pcsx2-qt/Settings/GameListSettingsWidget.h +++ b/pcsx2-qt/Settings/GameListSettingsWidget.h @@ -28,11 +28,13 @@ public Q_SLOTS: private Q_SLOTS: void onDirectoryListContextMenuRequested(const QPoint& point); + void onDirectoryListSelectionChanged(); void onAddSearchDirectoryButtonClicked(); void onRemoveSearchDirectoryButtonClicked(); void onAddExcludedFileButtonClicked(); void onAddExcludedPathButtonClicked(); void onRemoveExcludedPathButtonClicked(); + void onExcludedPathsSelectionChanged(); void onScanForNewGamesClicked(); void onRescanAllGamesClicked(); diff --git a/pcsx2-qt/SetupWizardDialog.cpp b/pcsx2-qt/SetupWizardDialog.cpp index 8831a00d3b9ba..079f2e76dc84d 100644 --- a/pcsx2-qt/SetupWizardDialog.cpp +++ b/pcsx2-qt/SetupWizardDialog.cpp @@ -237,12 +237,10 @@ void SetupWizardDialog::setupGameListPage() m_ui.searchDirectoryList->setCurrentIndex({}); m_ui.searchDirectoryList->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu); - connect(m_ui.searchDirectoryList, &QTableWidget::customContextMenuRequested, this, - &SetupWizardDialog::onDirectoryListContextMenuRequested); - connect(m_ui.addSearchDirectoryButton, &QPushButton::clicked, this, - &SetupWizardDialog::onAddSearchDirectoryButtonClicked); - connect(m_ui.removeSearchDirectoryButton, &QPushButton::clicked, this, - &SetupWizardDialog::onRemoveSearchDirectoryButtonClicked); + connect(m_ui.searchDirectoryList, &QTableWidget::customContextMenuRequested, this, &SetupWizardDialog::onDirectoryListContextMenuRequested); + connect(m_ui.searchDirectoryList, &QTableWidget::itemSelectionChanged, this, &SetupWizardDialog::onDirectoryListSelectionChanged); + connect(m_ui.addSearchDirectoryButton, &QPushButton::clicked, this, &SetupWizardDialog::onAddSearchDirectoryButtonClicked); + connect(m_ui.removeSearchDirectoryButton, &QPushButton::clicked, this, &SetupWizardDialog::onRemoveSearchDirectoryButtonClicked); refreshDirectoryList(); } @@ -263,6 +261,11 @@ void SetupWizardDialog::onDirectoryListContextMenuRequested(const QPoint& point) menu.exec(m_ui.searchDirectoryList->mapToGlobal(point)); } +void SetupWizardDialog::onDirectoryListSelectionChanged() +{ + m_ui.removeSearchDirectoryButton->setEnabled(!m_ui.searchDirectoryList->selectedItems().isEmpty()); +} + void SetupWizardDialog::onAddSearchDirectoryButtonClicked() { QString dir = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("Select Search Directory"))); @@ -349,6 +352,7 @@ void SetupWizardDialog::refreshDirectoryList() addPathToTable(entry, true); m_ui.searchDirectoryList->sortByColumn(0, Qt::AscendingOrder); + m_ui.removeSearchDirectoryButton->setEnabled(false); } void SetupWizardDialog::resizeDirectoryListColumns() diff --git a/pcsx2-qt/SetupWizardDialog.h b/pcsx2-qt/SetupWizardDialog.h index 2be48675e8c32..13b0ba7345249 100644 --- a/pcsx2-qt/SetupWizardDialog.h +++ b/pcsx2-qt/SetupWizardDialog.h @@ -33,6 +33,7 @@ private Q_SLOTS: void biosListItemChanged(const QTreeWidgetItem* current, const QTreeWidgetItem* previous); void onDirectoryListContextMenuRequested(const QPoint& point); + void onDirectoryListSelectionChanged(); void onAddSearchDirectoryButtonClicked(); void onRemoveSearchDirectoryButtonClicked(); void refreshDirectoryList(); diff --git a/pcsx2/vtlb.cpp b/pcsx2/vtlb.cpp index e5c5b4aad0537..f255f49b45c8f 100644 --- a/pcsx2/vtlb.cpp +++ b/pcsx2/vtlb.cpp @@ -25,6 +25,7 @@ #include "VMManager.h" #include "common/BitUtils.h" +#include "common/Error.h" #include "fmt/core.h" @@ -43,8 +44,6 @@ using namespace vtlb_private; namespace vtlb_private { alignas(64) MapData vtlbdata; - - static bool PageFaultHandler(const PageFaultInfo& info); } // namespace vtlb_private static vtlbHandler vtlbHandlerCount = 0; @@ -1304,9 +1303,10 @@ bool vtlb_Core_Alloc() DevCon.WriteLn(Color_StrongGreen, "Fastmem area: %p - %p", vtlbdata.fastmem_base, vtlbdata.fastmem_base + (FASTMEM_AREA_SIZE - 1)); - if (!HostSys::InstallPageFaultHandler(&vtlb_private::PageFaultHandler)) + Error error; + if (!PageFaultHandler::Install(&error)) { - Host::ReportErrorAsync("Error", "Failed to install page fault handler."); + Host::ReportErrorAsync("Failed to install page fault handler.", error.GetDescription()); return false; } @@ -1332,8 +1332,6 @@ void vtlb_Alloc_Ppmap() void vtlb_Core_Free() { - HostSys::RemovePageFaultHandler(&vtlb_private::PageFaultHandler); - vtlbdata.vmap = nullptr; vtlbdata.ppmap = nullptr; @@ -1456,12 +1454,12 @@ static __fi void mmap_ClearCpuBlock(uint offset) Cpu->Clear(m_PageProtectInfo[rampage].ReverseRamMap, __pagesize); } -bool vtlb_private::PageFaultHandler(const PageFaultInfo& info) +bool PageFaultHandler::HandlePageFault(uptr pc, uptr addr, bool is_write) { pxAssert(eeMem); u32 vaddr; - if (CHECK_FASTMEM && vtlb_GetGuestAddress(info.addr, &vaddr)) + if (CHECK_FASTMEM && vtlb_GetGuestAddress(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); @@ -1477,13 +1475,13 @@ bool vtlb_private::PageFaultHandler(const PageFaultInfo& info) else { // fprintf(stderr, "Trying backpatching vaddr %08X\n", vaddr); - return vtlb_BackpatchLoadStore(info.pc, info.addr); + return vtlb_BackpatchLoadStore(pc, addr); } } else { // get bad virtual address - uptr offset = info.addr - (uptr)eeMem->Main; + uptr offset = addr - (uptr)eeMem->Main; if (offset >= Ps2MemSize::MainRam) return false;