mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-05-25 21:03:18 +00:00
cpu_patches: Patch just-in-time using signal handlers. (#852)
* cpu_patches: Patch just-in-time using illegal instruction handler. * core: Add common signal dispatch system and use for on-demand TCB patches. * page_manager: Re-add userfaultfd implementation.
This commit is contained in:
parent
b09b28c7f3
commit
75a4df53a5
7 changed files with 404 additions and 143 deletions
|
@ -2,21 +2,21 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <thread>
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
#include "common/alignment.h"
|
||||
#include "common/arch.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/error.h"
|
||||
#include "core/signals.h"
|
||||
#include "video_core/page_manager.h"
|
||||
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
||||
|
||||
#ifndef _WIN64
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#ifdef ENABLE_USERFAULTFD
|
||||
#include <fcntl.h>
|
||||
#include <linux/userfaultfd.h>
|
||||
#include <poll.h>
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
#else
|
||||
#include <windows.h>
|
||||
|
@ -27,45 +27,7 @@ namespace VideoCore {
|
|||
constexpr size_t PAGESIZE = 4_KB;
|
||||
constexpr size_t PAGEBITS = 12;
|
||||
|
||||
#ifdef _WIN64
|
||||
struct PageManager::Impl {
|
||||
Impl(Vulkan::Rasterizer* rasterizer_) {
|
||||
rasterizer = rasterizer_;
|
||||
|
||||
veh_handle = AddVectoredExceptionHandler(0, GuestFaultSignalHandler);
|
||||
ASSERT_MSG(veh_handle, "Failed to register an exception handler");
|
||||
}
|
||||
|
||||
void OnMap(VAddr address, size_t size) {}
|
||||
|
||||
void OnUnmap(VAddr address, size_t size) {}
|
||||
|
||||
void Protect(VAddr address, size_t size, bool allow_write) {
|
||||
DWORD prot = allow_write ? PAGE_READWRITE : PAGE_READONLY;
|
||||
DWORD old_prot{};
|
||||
BOOL result = VirtualProtect(std::bit_cast<LPVOID>(address), size, prot, &old_prot);
|
||||
ASSERT_MSG(result != 0, "Region protection failed");
|
||||
}
|
||||
|
||||
static LONG WINAPI GuestFaultSignalHandler(EXCEPTION_POINTERS* pExp) noexcept {
|
||||
const u32 ec = pExp->ExceptionRecord->ExceptionCode;
|
||||
if (ec == EXCEPTION_ACCESS_VIOLATION) {
|
||||
const auto info = pExp->ExceptionRecord->ExceptionInformation;
|
||||
if (info[0] == 1) { // Write violation
|
||||
const VAddr addr_aligned = Common::AlignDown(info[1], PAGESIZE);
|
||||
rasterizer->InvalidateMemory(addr_aligned, PAGESIZE);
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
} /* else {
|
||||
UNREACHABLE();
|
||||
}*/
|
||||
}
|
||||
return EXCEPTION_CONTINUE_SEARCH; // pass further
|
||||
}
|
||||
|
||||
inline static Vulkan::Rasterizer* rasterizer;
|
||||
void* veh_handle{};
|
||||
};
|
||||
#elif ENABLE_USERFAULTFD
|
||||
#if ENABLE_USERFAULTFD
|
||||
struct PageManager::Impl {
|
||||
Impl(Vulkan::Rasterizer* rasterizer_) : rasterizer{rasterizer_} {
|
||||
uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
|
||||
|
@ -160,72 +122,48 @@ struct PageManager::Impl {
|
|||
int uffd;
|
||||
};
|
||||
#else
|
||||
|
||||
#if defined(__APPLE__)
|
||||
|
||||
#if defined(ARCH_X86_64)
|
||||
#define IS_WRITE_ERROR(ctx) ((ctx)->uc_mcontext->__es.__err & 0x2)
|
||||
#elif defined(ARCH_ARM64)
|
||||
#define IS_WRITE_ERROR(ctx) ((ctx)->uc_mcontext->__es.__esr & 0x40)
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#if defined(ARCH_X86_64)
|
||||
#define IS_WRITE_ERROR(ctx) ((ctx)->uc_mcontext.gregs[REG_ERR] & 0x2)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef IS_WRITE_ERROR
|
||||
#error "Missing IS_WRITE_ERROR() implementation for target OS and CPU architecture.
|
||||
#endif
|
||||
|
||||
struct PageManager::Impl {
|
||||
Impl(Vulkan::Rasterizer* rasterizer_) {
|
||||
rasterizer = rasterizer_;
|
||||
|
||||
#ifdef __APPLE__
|
||||
// Read-only memory write results in SIGBUS on Apple.
|
||||
static constexpr int SignalType = SIGBUS;
|
||||
#else
|
||||
static constexpr int SignalType = SIGSEGV;
|
||||
#endif
|
||||
sigset_t signal_mask;
|
||||
sigemptyset(&signal_mask);
|
||||
sigaddset(&signal_mask, SignalType);
|
||||
|
||||
using HandlerType = decltype(sigaction::sa_sigaction);
|
||||
|
||||
struct sigaction guest_access_fault {};
|
||||
guest_access_fault.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
guest_access_fault.sa_sigaction = &GuestFaultSignalHandler;
|
||||
guest_access_fault.sa_mask = signal_mask;
|
||||
sigaction(SignalType, &guest_access_fault, nullptr);
|
||||
// Should be called first.
|
||||
constexpr auto priority = std::numeric_limits<u32>::min();
|
||||
Core::Signals::Instance()->RegisterAccessViolationHandler(GuestFaultSignalHandler,
|
||||
priority);
|
||||
}
|
||||
|
||||
void OnMap(VAddr address, size_t size) {}
|
||||
void OnMap(VAddr address, size_t size) {
|
||||
owned_ranges += boost::icl::interval<VAddr>::right_open(address, address + size);
|
||||
}
|
||||
|
||||
void OnUnmap(VAddr address, size_t size) {}
|
||||
void OnUnmap(VAddr address, size_t size) {
|
||||
owned_ranges -= boost::icl::interval<VAddr>::right_open(address, address + size);
|
||||
}
|
||||
|
||||
void Protect(VAddr address, size_t size, bool allow_write) {
|
||||
#ifdef _WIN32
|
||||
DWORD prot = allow_write ? PAGE_READWRITE : PAGE_READONLY;
|
||||
DWORD old_prot{};
|
||||
BOOL result = VirtualProtect(std::bit_cast<LPVOID>(address), size, prot, &old_prot);
|
||||
ASSERT_MSG(result != 0, "Region protection failed");
|
||||
#else
|
||||
mprotect(reinterpret_cast<void*>(address), size,
|
||||
PROT_READ | (allow_write ? PROT_WRITE : 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
static void GuestFaultSignalHandler(int sig, siginfo_t* info, void* raw_context) {
|
||||
ucontext_t* ctx = reinterpret_cast<ucontext_t*>(raw_context);
|
||||
const VAddr address = reinterpret_cast<VAddr>(info->si_addr);
|
||||
if (IS_WRITE_ERROR(ctx)) {
|
||||
const VAddr addr_aligned = Common::AlignDown(address, PAGESIZE);
|
||||
static bool GuestFaultSignalHandler(void* code_address, void* fault_address, bool is_write) {
|
||||
const auto addr = reinterpret_cast<VAddr>(fault_address);
|
||||
if (is_write && owned_ranges.find(addr) != owned_ranges.end()) {
|
||||
const VAddr addr_aligned = Common::AlignDown(addr, PAGESIZE);
|
||||
rasterizer->InvalidateMemory(addr_aligned, PAGESIZE);
|
||||
} else {
|
||||
// Read not supported!
|
||||
UNREACHABLE();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline static Vulkan::Rasterizer* rasterizer;
|
||||
inline static boost::icl::interval_set<VAddr> owned_ranges;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue