mirror of
https://github.com/FEX-Emu/FEX.git
synced 2025-01-19 12:52:25 +00:00
Merge pull request #3367 from bylaws/prepwow
Windows: Commonise WOW64 logic that can be shared with ARM64EC
This commit is contained in:
commit
8ff4b525fc
@ -1,19 +1,22 @@
|
|||||||
function(build_implib name)
|
function(build_implib name)
|
||||||
add_custom_target(${name}lib ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/lib${name}.a)
|
set(name_ex ${name}_ex)
|
||||||
|
add_custom_target(${name_ex}lib ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/lib${name_ex}.a)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lib${name}.a
|
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lib${name_ex}.a
|
||||||
COMMAND ${CMAKE_DLLTOOL} -d ${CMAKE_CURRENT_SOURCE_DIR}/Defs/${name}.def -k -l lib${name}.a
|
COMMAND ${CMAKE_DLLTOOL} -d ${CMAKE_CURRENT_SOURCE_DIR}/Defs/${name}.def -k -l lib${name_ex}.a
|
||||||
COMMENT "Building lib${name}.a"
|
COMMENT "Building lib${name_ex}.a"
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(${name} SHARED IMPORTED)
|
add_library(${name_ex} SHARED IMPORTED)
|
||||||
set_property(TARGET ${name} PROPERTY IMPORTED_IMPLIB ${CMAKE_CURRENT_BINARY_DIR}/lib${name}.a)
|
set_property(TARGET ${name_ex} PROPERTY IMPORTED_IMPLIB ${CMAKE_CURRENT_BINARY_DIR}/lib${name_ex}.a)
|
||||||
add_dependencies(${name} ${name}lib)
|
add_dependencies(${name_ex} ${name_ex}lib)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
build_implib(ntdll)
|
build_implib(ntdll)
|
||||||
build_implib(wow64)
|
build_implib(wow64)
|
||||||
|
|
||||||
|
add_subdirectory(Common)
|
||||||
|
|
||||||
if (_M_ARM_64)
|
if (_M_ARM_64)
|
||||||
add_subdirectory(WOW64)
|
add_subdirectory(WOW64)
|
||||||
endif()
|
endif()
|
||||||
|
6
Source/Windows/Common/CMakeLists.txt
Normal file
6
Source/Windows/Common/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
add_library(CommonWindows STATIC CPUFeatures.cpp InvalidationTracker.cpp)
|
||||||
|
target_link_libraries(CommonWindows FEXCore_Base)
|
||||||
|
|
||||||
|
target_include_directories(CommonWindows PRIVATE
|
||||||
|
"${CMAKE_SOURCE_DIR}/Source/Windows/include/"
|
||||||
|
)
|
102
Source/Windows/Common/CPUFeatures.cpp
Normal file
102
Source/Windows/Common/CPUFeatures.cpp
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <FEXCore/Core/Context.h>
|
||||||
|
#include "CPUFeatures.h"
|
||||||
|
|
||||||
|
namespace FEX::Windows {
|
||||||
|
CPUFeatures::CPUFeatures(FEXCore::Context::Context &CTX) {
|
||||||
|
CpuInfo.ProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
|
||||||
|
|
||||||
|
// Baseline FEX feature-set
|
||||||
|
CpuInfo.ProcessorFeatureBits = CPU_FEATURE_VME | CPU_FEATURE_TSC | CPU_FEATURE_CMOV | CPU_FEATURE_PGE |
|
||||||
|
CPU_FEATURE_PSE | CPU_FEATURE_MTRR | CPU_FEATURE_CX8 | CPU_FEATURE_MMX |
|
||||||
|
CPU_FEATURE_X86 | CPU_FEATURE_PAT | CPU_FEATURE_FXSR | CPU_FEATURE_SEP |
|
||||||
|
CPU_FEATURE_SSE | CPU_FEATURE_3DNOW | CPU_FEATURE_SSE2 | CPU_FEATURE_SSE3 |
|
||||||
|
CPU_FEATURE_CX128 | CPU_FEATURE_NX | CPU_FEATURE_SSSE3 | CPU_FEATURE_SSE41 |
|
||||||
|
CPU_FEATURE_PAE | CPU_FEATURE_DAZ;
|
||||||
|
|
||||||
|
// Features that require specific host CPU support
|
||||||
|
const auto CPUIDResult01 = CTX.RunCPUIDFunction(0x01, 0);
|
||||||
|
if (CPUIDResult01.ecx & (1 << 20)) {
|
||||||
|
CpuInfo.ProcessorFeatureBits |= CPU_FEATURE_SSE42;
|
||||||
|
}
|
||||||
|
if (CPUIDResult01.ecx & (1 << 27)) {
|
||||||
|
CpuInfo.ProcessorFeatureBits |= CPU_FEATURE_XSAVE;
|
||||||
|
}
|
||||||
|
if (CPUIDResult01.ecx & (1 << 28)) {
|
||||||
|
CpuInfo.ProcessorFeatureBits |= CPU_FEATURE_AVX;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto CPUIDResult07 = CTX.RunCPUIDFunction(0x07, 0);
|
||||||
|
if (CPUIDResult07.ebx & (1 << 5)) {
|
||||||
|
CpuInfo.ProcessorFeatureBits |= CPU_FEATURE_AVX2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto FamilyIdentifier = CPUIDResult01.eax;
|
||||||
|
CpuInfo.ProcessorLevel = ((FamilyIdentifier >> 8) & 0xf) + ((FamilyIdentifier >> 20) & 0xff); // Family
|
||||||
|
CpuInfo.ProcessorRevision = (FamilyIdentifier & 0xf0000) >> 4; // Extended Model
|
||||||
|
CpuInfo.ProcessorRevision |= (FamilyIdentifier & 0xf0) << 4; // Model
|
||||||
|
CpuInfo.ProcessorRevision |= FamilyIdentifier & 0xf; // Stepping
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CPUFeatures::IsFeaturePresent(uint32_t Feature) {
|
||||||
|
switch (Feature) {
|
||||||
|
case PF_FLOATING_POINT_PRECISION_ERRATA:
|
||||||
|
return FALSE;
|
||||||
|
case PF_FLOATING_POINT_EMULATED:
|
||||||
|
return FALSE;
|
||||||
|
case PF_COMPARE_EXCHANGE_DOUBLE:
|
||||||
|
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_CX8);
|
||||||
|
case PF_MMX_INSTRUCTIONS_AVAILABLE:
|
||||||
|
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_MMX);
|
||||||
|
case PF_XMMI_INSTRUCTIONS_AVAILABLE:
|
||||||
|
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_SSE);
|
||||||
|
case PF_3DNOW_INSTRUCTIONS_AVAILABLE:
|
||||||
|
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_3DNOW);
|
||||||
|
case PF_RDTSC_INSTRUCTION_AVAILABLE:
|
||||||
|
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_TSC);
|
||||||
|
case PF_PAE_ENABLED:
|
||||||
|
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_PAE);
|
||||||
|
case PF_XMMI64_INSTRUCTIONS_AVAILABLE:
|
||||||
|
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_SSE2);
|
||||||
|
case PF_SSE3_INSTRUCTIONS_AVAILABLE:
|
||||||
|
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_SSE3);
|
||||||
|
case PF_SSSE3_INSTRUCTIONS_AVAILABLE:
|
||||||
|
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_SSSE3);
|
||||||
|
case PF_XSAVE_ENABLED:
|
||||||
|
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_XSAVE);
|
||||||
|
case PF_COMPARE_EXCHANGE128:
|
||||||
|
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_CX128);
|
||||||
|
case PF_SSE_DAZ_MODE_AVAILABLE:
|
||||||
|
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_DAZ);
|
||||||
|
case PF_NX_ENABLED:
|
||||||
|
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_NX);
|
||||||
|
case PF_SECOND_LEVEL_ADDRESS_TRANSLATION:
|
||||||
|
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_2NDLEV);
|
||||||
|
case PF_VIRT_FIRMWARE_ENABLED:
|
||||||
|
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_VIRT);
|
||||||
|
case PF_RDWRFSGSBASE_AVAILABLE:
|
||||||
|
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_RDFS);
|
||||||
|
case PF_FASTFAIL_AVAILABLE:
|
||||||
|
return TRUE;
|
||||||
|
case PF_SSE4_1_INSTRUCTIONS_AVAILABLE:
|
||||||
|
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_SSE41);
|
||||||
|
case PF_SSE4_2_INSTRUCTIONS_AVAILABLE:
|
||||||
|
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_SSE42);
|
||||||
|
case PF_AVX_INSTRUCTIONS_AVAILABLE:
|
||||||
|
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_AVX);
|
||||||
|
case PF_AVX2_INSTRUCTIONS_AVAILABLE:
|
||||||
|
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_AVX2);
|
||||||
|
default:
|
||||||
|
LogMan::Msg::DFmt("Unknown CPU feature: {:X}", Feature);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPUFeatures::UpdateInformation(SYSTEM_CPU_INFORMATION *Info) {
|
||||||
|
Info->ProcessorArchitecture = CpuInfo.ProcessorArchitecture;
|
||||||
|
Info->ProcessorLevel = CpuInfo.ProcessorLevel;
|
||||||
|
Info->ProcessorRevision = CpuInfo.ProcessorRevision;
|
||||||
|
Info->ProcessorFeatureBits = CpuInfo.ProcessorFeatureBits;
|
||||||
|
}
|
||||||
|
}
|
32
Source/Windows/Common/CPUFeatures.h
Normal file
32
Source/Windows/Common/CPUFeatures.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windef.h>
|
||||||
|
#include <winternl.h>
|
||||||
|
|
||||||
|
namespace FEXCore::Context {
|
||||||
|
class Context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Maps CPUID results to Windows CPU info structures
|
||||||
|
*/
|
||||||
|
namespace FEX::Windows {
|
||||||
|
class CPUFeatures {
|
||||||
|
public:
|
||||||
|
CPUFeatures(FEXCore::Context::Context &CTX);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief If the given PF_* feature is supported
|
||||||
|
*/
|
||||||
|
bool IsFeaturePresent(uint32_t Feature);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fills in `Info` according to the detected CPU features
|
||||||
|
*/
|
||||||
|
void UpdateInformation(SYSTEM_CPU_INFORMATION *Info);
|
||||||
|
|
||||||
|
private:
|
||||||
|
SYSTEM_CPU_INFORMATION CpuInfo{};
|
||||||
|
};
|
||||||
|
}
|
98
Source/Windows/Common/InvalidationTracker.cpp
Normal file
98
Source/Windows/Common/InvalidationTracker.cpp
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <FEXCore/Utils/LogManager.h>
|
||||||
|
#include <FEXCore/Core/Context.h>
|
||||||
|
#include <FEXCore/Debug/InternalThreadState.h>
|
||||||
|
#include "InvalidationTracker.h"
|
||||||
|
#include <windef.h>
|
||||||
|
#include <winternl.h>
|
||||||
|
|
||||||
|
namespace FEX::Windows {
|
||||||
|
void InvalidationTracker::HandleMemoryProtectionNotification(FEXCore::Core::InternalThreadState *Thread, uint64_t Address, uint64_t Size, ULONG Prot) {
|
||||||
|
const auto AlignedBase = Address & FHU::FEX_PAGE_MASK;
|
||||||
|
const auto AlignedSize = (Address - AlignedBase + Size + FHU::FEX_PAGE_SIZE - 1) & FHU::FEX_PAGE_MASK;
|
||||||
|
|
||||||
|
if (Prot & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE)) {
|
||||||
|
Thread->CTX->InvalidateGuestCodeRange(Thread, AlignedBase, AlignedSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Prot & PAGE_EXECUTE_READWRITE) {
|
||||||
|
LogMan::Msg::DFmt("Add SMC interval: {:X} - {:X}", AlignedBase, AlignedBase + AlignedSize);
|
||||||
|
std::scoped_lock Lock(RWXIntervalsLock);
|
||||||
|
RWXIntervals.Insert({AlignedBase, AlignedBase + AlignedSize});
|
||||||
|
} else {
|
||||||
|
std::scoped_lock Lock(RWXIntervalsLock);
|
||||||
|
RWXIntervals.Remove({AlignedBase, AlignedBase + AlignedSize});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InvalidationTracker::InvalidateContainingSection(FEXCore::Core::InternalThreadState *Thread, uint64_t Address, bool Free) {
|
||||||
|
MEMORY_BASIC_INFORMATION Info;
|
||||||
|
if (NtQueryVirtualMemory(NtCurrentProcess(), reinterpret_cast<void *>(Address), MemoryBasicInformation, &Info, sizeof(Info), nullptr))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto SectionBase = reinterpret_cast<uint64_t>(Info.AllocationBase);
|
||||||
|
const auto SectionSize = reinterpret_cast<uint64_t>(Info.BaseAddress) + Info.RegionSize
|
||||||
|
- reinterpret_cast<uint64_t>(Info.AllocationBase);
|
||||||
|
Thread->CTX->InvalidateGuestCodeRange(Thread, SectionBase, SectionSize);
|
||||||
|
|
||||||
|
if (Free) {
|
||||||
|
std::scoped_lock Lock(RWXIntervalsLock);
|
||||||
|
RWXIntervals.Remove({SectionBase, SectionBase + SectionSize});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InvalidationTracker::InvalidateAlignedInterval(FEXCore::Core::InternalThreadState *Thread, uint64_t Address, uint64_t Size, bool Free) {
|
||||||
|
const auto AlignedBase = Address & FHU::FEX_PAGE_MASK;
|
||||||
|
const auto AlignedSize = (Address - AlignedBase + Size + FHU::FEX_PAGE_SIZE - 1) & FHU::FEX_PAGE_MASK;
|
||||||
|
Thread->CTX->InvalidateGuestCodeRange(Thread, AlignedBase, AlignedSize);
|
||||||
|
|
||||||
|
if (Free) {
|
||||||
|
std::scoped_lock Lock(RWXIntervalsLock);
|
||||||
|
RWXIntervals.Remove({AlignedBase, AlignedBase + AlignedSize});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InvalidationTracker::ReprotectRWXIntervals(uint64_t Address, uint64_t Size) {
|
||||||
|
const auto End = Address + Size;
|
||||||
|
std::scoped_lock Lock(RWXIntervalsLock);
|
||||||
|
|
||||||
|
do {
|
||||||
|
const auto Query = RWXIntervals.Query(Address);
|
||||||
|
if (Query.Enclosed) {
|
||||||
|
void *TmpAddress = reinterpret_cast<void *>(Address);
|
||||||
|
SIZE_T TmpSize = static_cast<SIZE_T>(std::min(End, Address + Query.Size) - Address);
|
||||||
|
ULONG TmpProt;
|
||||||
|
NtProtectVirtualMemory(NtCurrentProcess(), &TmpAddress, &TmpSize, PAGE_EXECUTE_READ, &TmpProt);
|
||||||
|
} else if (!Query.Size) {
|
||||||
|
// No more regions past `Address` in the interval list
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Address += Query.Size;
|
||||||
|
} while (Address < End);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InvalidationTracker::HandleRWXAccessViolation(FEXCore::Core::InternalThreadState *Thread, uint64_t FaultAddress) {
|
||||||
|
const bool NeedsInvalidate = [&](uint64_t Address) {
|
||||||
|
std::unique_lock Lock(RWXIntervalsLock);
|
||||||
|
const bool Enclosed = RWXIntervals.Query(Address).Enclosed;
|
||||||
|
// Invalidate just the single faulting page
|
||||||
|
if (!Enclosed)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ULONG TmpProt;
|
||||||
|
void *TmpAddress = reinterpret_cast<void *>(Address);
|
||||||
|
SIZE_T TmpSize = 1;
|
||||||
|
NtProtectVirtualMemory(NtCurrentProcess(), &TmpAddress, &TmpSize, PAGE_EXECUTE_READWRITE, &TmpProt);
|
||||||
|
return true;
|
||||||
|
}(FaultAddress);
|
||||||
|
|
||||||
|
if (NeedsInvalidate) {
|
||||||
|
// RWXIntervalsLock cannot be held during invalidation
|
||||||
|
Thread->CTX->InvalidateGuestCodeRange(Thread, FaultAddress & FHU::FEX_PAGE_MASK, FHU::FEX_PAGE_SIZE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
28
Source/Windows/Common/InvalidationTracker.h
Normal file
28
Source/Windows/Common/InvalidationTracker.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// FIXME TODO put in cpp
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "IntervalList.h"
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace FEXCore::Core {
|
||||||
|
struct InternalThreadState;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace FEX::Windows {
|
||||||
|
/**
|
||||||
|
* @brief Handles SMC and regular code invalidation
|
||||||
|
*/
|
||||||
|
class InvalidationTracker {
|
||||||
|
public:
|
||||||
|
void HandleMemoryProtectionNotification(FEXCore::Core::InternalThreadState *Thread, uint64_t Address, uint64_t Size, ULONG Prot);
|
||||||
|
void InvalidateContainingSection(FEXCore::Core::InternalThreadState *Thread, uint64_t Address, bool Free);
|
||||||
|
void InvalidateAlignedInterval(FEXCore::Core::InternalThreadState *Thread, uint64_t Address, uint64_t Size, bool Free);
|
||||||
|
void ReprotectRWXIntervals(uint64_t Address, uint64_t Size);
|
||||||
|
bool HandleRWXAccessViolation(FEXCore::Core::InternalThreadState *Thread, uint64_t FaultAddress);
|
||||||
|
|
||||||
|
private:
|
||||||
|
IntervalList<uint64_t> RWXIntervals;
|
||||||
|
std::mutex RWXIntervalsLock;
|
||||||
|
};
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -5,6 +5,7 @@ add_library(wow64fex SHARED
|
|||||||
|
|
||||||
target_include_directories(wow64fex PRIVATE
|
target_include_directories(wow64fex PRIVATE
|
||||||
"${CMAKE_SOURCE_DIR}/Source/Windows/include/"
|
"${CMAKE_SOURCE_DIR}/Source/Windows/include/"
|
||||||
|
"${CMAKE_SOURCE_DIR}/Source/Windows/"
|
||||||
"${CMAKE_SOURCE_DIR}/Source/"
|
"${CMAKE_SOURCE_DIR}/Source/"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -13,7 +14,9 @@ target_link_libraries(wow64fex
|
|||||||
FEXCore
|
FEXCore
|
||||||
Common
|
Common
|
||||||
CommonTools
|
CommonTools
|
||||||
wow64
|
CommonWindows
|
||||||
|
wow64_ex
|
||||||
|
ntdll_ex
|
||||||
ntdll
|
ntdll
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,9 +26,10 @@ $end_info$
|
|||||||
#include <FEXHeaderUtils/TypeDefines.h>
|
#include <FEXHeaderUtils/TypeDefines.h>
|
||||||
|
|
||||||
#include "Common/Config.h"
|
#include "Common/Config.h"
|
||||||
|
#include "Common/InvalidationTracker.h"
|
||||||
|
#include "Common/CPUFeatures.h"
|
||||||
#include "DummyHandlers.h"
|
#include "DummyHandlers.h"
|
||||||
#include "BTInterface.h"
|
#include "BTInterface.h"
|
||||||
#include "IntervalList.h"
|
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
@ -92,7 +93,8 @@ namespace {
|
|||||||
fextl::unique_ptr<FEX::DummyHandlers::DummySignalDelegator> SignalDelegator;
|
fextl::unique_ptr<FEX::DummyHandlers::DummySignalDelegator> SignalDelegator;
|
||||||
fextl::unique_ptr<WowSyscallHandler> SyscallHandler;
|
fextl::unique_ptr<WowSyscallHandler> SyscallHandler;
|
||||||
|
|
||||||
SYSTEM_CPU_INFORMATION CpuInfo{};
|
FEX::Windows::InvalidationTracker InvalidationTracker;
|
||||||
|
std::optional<FEX::Windows::CPUFeatures> CPUFeatures;
|
||||||
|
|
||||||
std::mutex ThreadSuspendLock;
|
std::mutex ThreadSuspendLock;
|
||||||
std::unordered_set<DWORD> InitializedWOWThreads; // Set of TIDs, `ThreadSuspendLock` must be locked when accessing
|
std::unordered_set<DWORD> InitializedWOWThreads; // Set of TIDs, `ThreadSuspendLock` must be locked when accessing
|
||||||
@ -326,99 +328,6 @@ namespace Context {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Invalidation {
|
|
||||||
static IntervalList<uint64_t> RWXIntervals;
|
|
||||||
static std::mutex RWXIntervalsLock;
|
|
||||||
|
|
||||||
void HandleMemoryProtectionNotification(uint64_t Address, uint64_t Size, ULONG Prot) {
|
|
||||||
const auto AlignedBase = Address & FHU::FEX_PAGE_MASK;
|
|
||||||
const auto AlignedSize = (Address - AlignedBase + Size + FHU::FEX_PAGE_SIZE - 1) & FHU::FEX_PAGE_MASK;
|
|
||||||
|
|
||||||
if (Prot & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE)) {
|
|
||||||
CTX->InvalidateGuestCodeRange(GetTLS().ThreadState(), AlignedBase, AlignedSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Prot & PAGE_EXECUTE_READWRITE) {
|
|
||||||
LogMan::Msg::DFmt("Add SMC interval: {:X} - {:X}", AlignedBase, AlignedBase + AlignedSize);
|
|
||||||
std::scoped_lock Lock(RWXIntervalsLock);
|
|
||||||
RWXIntervals.Insert({AlignedBase, AlignedBase + AlignedSize});
|
|
||||||
} else {
|
|
||||||
std::scoped_lock Lock(RWXIntervalsLock);
|
|
||||||
RWXIntervals.Remove({AlignedBase, AlignedBase + AlignedSize});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void InvalidateContainingSection(uint64_t Address, bool Free) {
|
|
||||||
MEMORY_BASIC_INFORMATION Info;
|
|
||||||
if (NtQueryVirtualMemory(NtCurrentProcess(), reinterpret_cast<void *>(Address), MemoryBasicInformation, &Info, sizeof(Info), nullptr))
|
|
||||||
return;
|
|
||||||
|
|
||||||
const auto SectionBase = reinterpret_cast<uint64_t>(Info.AllocationBase);
|
|
||||||
const auto SectionSize = reinterpret_cast<uint64_t>(Info.BaseAddress) + Info.RegionSize
|
|
||||||
- reinterpret_cast<uint64_t>(Info.AllocationBase);
|
|
||||||
CTX->InvalidateGuestCodeRange(GetTLS().ThreadState(), SectionBase, SectionSize);
|
|
||||||
|
|
||||||
if (Free) {
|
|
||||||
std::scoped_lock Lock(RWXIntervalsLock);
|
|
||||||
RWXIntervals.Remove({SectionBase, SectionBase + SectionSize});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void InvalidateAlignedInterval(uint64_t Address, uint64_t Size, bool Free) {
|
|
||||||
const auto AlignedBase = Address & FHU::FEX_PAGE_MASK;
|
|
||||||
const auto AlignedSize = (Address - AlignedBase + Size + FHU::FEX_PAGE_SIZE - 1) & FHU::FEX_PAGE_MASK;
|
|
||||||
CTX->InvalidateGuestCodeRange(GetTLS().ThreadState(), AlignedBase, AlignedSize);
|
|
||||||
|
|
||||||
if (Free) {
|
|
||||||
std::scoped_lock Lock(RWXIntervalsLock);
|
|
||||||
RWXIntervals.Remove({AlignedBase, AlignedBase + AlignedSize});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReprotectRWXIntervals(uint64_t Address, uint64_t Size) {
|
|
||||||
const auto End = Address + Size;
|
|
||||||
std::scoped_lock Lock(RWXIntervalsLock);
|
|
||||||
|
|
||||||
do {
|
|
||||||
const auto Query = RWXIntervals.Query(Address);
|
|
||||||
if (Query.Enclosed) {
|
|
||||||
void *TmpAddress = reinterpret_cast<void *>(Address);
|
|
||||||
SIZE_T TmpSize = static_cast<SIZE_T>(std::min(End, Address + Query.Size) - Address);
|
|
||||||
ULONG TmpProt;
|
|
||||||
NtProtectVirtualMemory(NtCurrentProcess(), &TmpAddress, &TmpSize, PAGE_EXECUTE_READ, &TmpProt);
|
|
||||||
} else if (!Query.Size) {
|
|
||||||
// No more regions past `Address` in the interval list
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Address += Query.Size;
|
|
||||||
} while (Address < End);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HandleRWXAccessViolation(uint64_t FaultAddress) {
|
|
||||||
const bool NeedsInvalidate = [](uint64_t Address) {
|
|
||||||
std::unique_lock Lock(RWXIntervalsLock);
|
|
||||||
const bool Enclosed = RWXIntervals.Query(Address).Enclosed;
|
|
||||||
// Invalidate just the single faulting page
|
|
||||||
if (!Enclosed)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
ULONG TmpProt;
|
|
||||||
void *TmpAddress = reinterpret_cast<void *>(Address);
|
|
||||||
SIZE_T TmpSize = 1;
|
|
||||||
NtProtectVirtualMemory(NtCurrentProcess(), &TmpAddress, &TmpSize, PAGE_EXECUTE_READWRITE, &TmpProt);
|
|
||||||
return true;
|
|
||||||
}(FaultAddress);
|
|
||||||
|
|
||||||
if (NeedsInvalidate) {
|
|
||||||
// RWXIntervalsLock cannot be held during invalidation
|
|
||||||
CTX->InvalidateGuestCodeRange(GetTLS().ThreadState(), FaultAddress & FHU::FEX_PAGE_MASK, FHU::FEX_PAGE_SIZE);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Logging {
|
namespace Logging {
|
||||||
void MsgHandler(LogMan::DebugLevels Level, char const *Message) {
|
void MsgHandler(LogMan::DebugLevels Level, char const *Message) {
|
||||||
const auto Output = fextl::fmt::format("[{}][{:X}] {}\n", LogMan::DebugLevelStr(Level), GetCurrentThreadId(), Message);
|
const auto Output = fextl::fmt::format("[{}][{:X}] {}\n", LogMan::DebugLevelStr(Level), GetCurrentThreadId(), Message);
|
||||||
@ -490,7 +399,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MarkGuestExecutableRange(FEXCore::Core::InternalThreadState *Thread, uint64_t Start, uint64_t Length) override {
|
void MarkGuestExecutableRange(FEXCore::Core::InternalThreadState *Thread, uint64_t Start, uint64_t Length) override {
|
||||||
Invalidation::ReprotectRWXIntervals(Start, Length);
|
InvalidationTracker.ReprotectRWXIntervals(Start, Length);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -519,39 +428,7 @@ void BTCpuProcessInit() {
|
|||||||
CTX->SetSignalDelegator(SignalDelegator.get());
|
CTX->SetSignalDelegator(SignalDelegator.get());
|
||||||
CTX->SetSyscallHandler(SyscallHandler.get());
|
CTX->SetSyscallHandler(SyscallHandler.get());
|
||||||
CTX->InitCore();
|
CTX->InitCore();
|
||||||
|
CPUFeatures.emplace(*CTX);
|
||||||
CpuInfo.ProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
|
|
||||||
|
|
||||||
// Baseline FEX feature-set
|
|
||||||
CpuInfo.ProcessorFeatureBits = CPU_FEATURE_VME | CPU_FEATURE_TSC | CPU_FEATURE_CMOV | CPU_FEATURE_PGE |
|
|
||||||
CPU_FEATURE_PSE | CPU_FEATURE_MTRR | CPU_FEATURE_CX8 | CPU_FEATURE_MMX |
|
|
||||||
CPU_FEATURE_X86 | CPU_FEATURE_PAT | CPU_FEATURE_FXSR | CPU_FEATURE_SEP |
|
|
||||||
CPU_FEATURE_SSE | CPU_FEATURE_3DNOW | CPU_FEATURE_SSE2 | CPU_FEATURE_SSE3 |
|
|
||||||
CPU_FEATURE_CX128 | CPU_FEATURE_NX | CPU_FEATURE_SSSE3 | CPU_FEATURE_SSE41 |
|
|
||||||
CPU_FEATURE_PAE | CPU_FEATURE_DAZ;
|
|
||||||
|
|
||||||
// Features that require specific host CPU support
|
|
||||||
const auto CPUIDResult01 = CTX->RunCPUIDFunction(0x01, 0);
|
|
||||||
if (CPUIDResult01.ecx & (1 << 20)) {
|
|
||||||
CpuInfo.ProcessorFeatureBits |= CPU_FEATURE_SSE42;
|
|
||||||
}
|
|
||||||
if (CPUIDResult01.ecx & (1 << 27)) {
|
|
||||||
CpuInfo.ProcessorFeatureBits |= CPU_FEATURE_XSAVE;
|
|
||||||
}
|
|
||||||
if (CPUIDResult01.ecx & (1 << 28)) {
|
|
||||||
CpuInfo.ProcessorFeatureBits |= CPU_FEATURE_AVX;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto CPUIDResult07 = CTX->RunCPUIDFunction(0x07, 0);
|
|
||||||
if (CPUIDResult07.ebx & (1 << 5)) {
|
|
||||||
CpuInfo.ProcessorFeatureBits |= CPU_FEATURE_AVX2;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto FamilyIdentifier = CPUIDResult01.eax;
|
|
||||||
CpuInfo.ProcessorLevel = ((FamilyIdentifier >> 8) & 0xf) + ((FamilyIdentifier >> 20) & 0xff); // Family
|
|
||||||
CpuInfo.ProcessorRevision = (FamilyIdentifier & 0xf0000) >> 4; // Extended Model
|
|
||||||
CpuInfo.ProcessorRevision |= (FamilyIdentifier & 0xf0) << 4; // Model
|
|
||||||
CpuInfo.ProcessorRevision |= FamilyIdentifier & 0xf; // Stepping
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NTSTATUS BTCpuThreadInit() {
|
NTSTATUS BTCpuThreadInit() {
|
||||||
@ -743,7 +620,7 @@ NTSTATUS BTCpuResetToConsistentState(EXCEPTION_POINTERS *Ptrs) {
|
|||||||
if (Exception->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
|
if (Exception->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
|
||||||
const auto FaultAddress = static_cast<uint64_t>(Exception->ExceptionInformation[1]);
|
const auto FaultAddress = static_cast<uint64_t>(Exception->ExceptionInformation[1]);
|
||||||
|
|
||||||
if (Invalidation::HandleRWXAccessViolation(FaultAddress)) {
|
if (InvalidationTracker.HandleRWXAccessViolation(GetTLS().ThreadState(), FaultAddress)) {
|
||||||
LogMan::Msg::DFmt("Handled self-modifying code: pc: {:X} fault: {:X}", Context->Pc, FaultAddress);
|
LogMan::Msg::DFmt("Handled self-modifying code: pc: {:X} fault: {:X}", Context->Pc, FaultAddress);
|
||||||
NtContinue(Context, FALSE);
|
NtContinue(Context, FALSE);
|
||||||
}
|
}
|
||||||
@ -773,89 +650,36 @@ NTSTATUS BTCpuResetToConsistentState(EXCEPTION_POINTERS *Ptrs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BTCpuFlushInstructionCache2(const void *Address, SIZE_T Size) {
|
void BTCpuFlushInstructionCache2(const void *Address, SIZE_T Size) {
|
||||||
Invalidation::InvalidateAlignedInterval(reinterpret_cast<uint64_t>(Address), static_cast<uint64_t>(Size), false);
|
InvalidationTracker.InvalidateAlignedInterval(GetTLS().ThreadState(), reinterpret_cast<uint64_t>(Address), static_cast<uint64_t>(Size), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BTCpuNotifyMemoryAlloc(void *Address, SIZE_T Size, ULONG Type, ULONG Prot) {
|
void BTCpuNotifyMemoryAlloc(void *Address, SIZE_T Size, ULONG Type, ULONG Prot) {
|
||||||
Invalidation::HandleMemoryProtectionNotification(reinterpret_cast<uint64_t>(Address), static_cast<uint64_t>(Size),
|
InvalidationTracker.HandleMemoryProtectionNotification(GetTLS().ThreadState(), reinterpret_cast<uint64_t>(Address), static_cast<uint64_t>(Size),
|
||||||
Prot);
|
Prot);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BTCpuNotifyMemoryProtect(void *Address, SIZE_T Size, ULONG NewProt) {
|
void BTCpuNotifyMemoryProtect(void *Address, SIZE_T Size, ULONG NewProt) {
|
||||||
Invalidation::HandleMemoryProtectionNotification(reinterpret_cast<uint64_t>(Address), static_cast<uint64_t>(Size),
|
InvalidationTracker.HandleMemoryProtectionNotification(GetTLS().ThreadState(), reinterpret_cast<uint64_t>(Address), static_cast<uint64_t>(Size),
|
||||||
NewProt);
|
NewProt);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BTCpuNotifyMemoryFree(void *Address, SIZE_T Size, ULONG FreeType) {
|
void BTCpuNotifyMemoryFree(void *Address, SIZE_T Size, ULONG FreeType) {
|
||||||
if (!Size) {
|
if (!Size) {
|
||||||
Invalidation::InvalidateContainingSection(reinterpret_cast<uint64_t>(Address), true);
|
InvalidationTracker.InvalidateContainingSection(GetTLS().ThreadState(), reinterpret_cast<uint64_t>(Address), true);
|
||||||
} else if (FreeType & MEM_DECOMMIT) {
|
} else if (FreeType & MEM_DECOMMIT) {
|
||||||
Invalidation::InvalidateAlignedInterval(reinterpret_cast<uint64_t>(Address), static_cast<uint64_t>(Size), true);
|
InvalidationTracker.InvalidateAlignedInterval(GetTLS().ThreadState(), reinterpret_cast<uint64_t>(Address), static_cast<uint64_t>(Size), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BTCpuNotifyUnmapViewOfSection(void *Address, ULONG Flags) {
|
void BTCpuNotifyUnmapViewOfSection(void *Address, ULONG Flags) {
|
||||||
Invalidation::InvalidateContainingSection(reinterpret_cast<uint64_t>(Address), true);
|
InvalidationTracker.InvalidateContainingSection(GetTLS().ThreadState(), reinterpret_cast<uint64_t>(Address), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOLEAN WINAPI BTCpuIsProcessorFeaturePresent(UINT Feature) {
|
BOOLEAN WINAPI BTCpuIsProcessorFeaturePresent(UINT Feature) {
|
||||||
switch (Feature) {
|
return CPUFeatures->IsFeaturePresent(Feature) ? TRUE : FALSE;
|
||||||
case PF_FLOATING_POINT_PRECISION_ERRATA:
|
|
||||||
return FALSE;
|
|
||||||
case PF_FLOATING_POINT_EMULATED:
|
|
||||||
return FALSE;
|
|
||||||
case PF_COMPARE_EXCHANGE_DOUBLE:
|
|
||||||
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_CX8);
|
|
||||||
case PF_MMX_INSTRUCTIONS_AVAILABLE:
|
|
||||||
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_MMX);
|
|
||||||
case PF_XMMI_INSTRUCTIONS_AVAILABLE:
|
|
||||||
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_SSE);
|
|
||||||
case PF_3DNOW_INSTRUCTIONS_AVAILABLE:
|
|
||||||
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_3DNOW);
|
|
||||||
case PF_RDTSC_INSTRUCTION_AVAILABLE:
|
|
||||||
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_TSC);
|
|
||||||
case PF_PAE_ENABLED:
|
|
||||||
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_PAE);
|
|
||||||
case PF_XMMI64_INSTRUCTIONS_AVAILABLE:
|
|
||||||
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_SSE2);
|
|
||||||
case PF_SSE3_INSTRUCTIONS_AVAILABLE:
|
|
||||||
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_SSE3);
|
|
||||||
case PF_SSSE3_INSTRUCTIONS_AVAILABLE:
|
|
||||||
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_SSSE3);
|
|
||||||
case PF_XSAVE_ENABLED:
|
|
||||||
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_XSAVE);
|
|
||||||
case PF_COMPARE_EXCHANGE128:
|
|
||||||
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_CX128);
|
|
||||||
case PF_SSE_DAZ_MODE_AVAILABLE:
|
|
||||||
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_DAZ);
|
|
||||||
case PF_NX_ENABLED:
|
|
||||||
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_NX);
|
|
||||||
case PF_SECOND_LEVEL_ADDRESS_TRANSLATION:
|
|
||||||
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_2NDLEV);
|
|
||||||
case PF_VIRT_FIRMWARE_ENABLED:
|
|
||||||
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_VIRT);
|
|
||||||
case PF_RDWRFSGSBASE_AVAILABLE:
|
|
||||||
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_RDFS);
|
|
||||||
case PF_FASTFAIL_AVAILABLE:
|
|
||||||
return TRUE;
|
|
||||||
case PF_SSE4_1_INSTRUCTIONS_AVAILABLE:
|
|
||||||
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_SSE41);
|
|
||||||
case PF_SSE4_2_INSTRUCTIONS_AVAILABLE:
|
|
||||||
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_SSE42);
|
|
||||||
case PF_AVX_INSTRUCTIONS_AVAILABLE:
|
|
||||||
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_AVX);
|
|
||||||
case PF_AVX2_INSTRUCTIONS_AVAILABLE:
|
|
||||||
return !!(CpuInfo.ProcessorFeatureBits & CPU_FEATURE_AVX2);
|
|
||||||
default:
|
|
||||||
LogMan::Msg::DFmt("Unknown CPU feature: {:X}", Feature);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOLEAN BTCpuUpdateProcessorInformation(SYSTEM_CPU_INFORMATION *Info) {
|
BOOLEAN BTCpuUpdateProcessorInformation(SYSTEM_CPU_INFORMATION *Info) {
|
||||||
Info->ProcessorArchitecture = CpuInfo.ProcessorArchitecture;
|
CPUFeatures->UpdateInformation(Info);
|
||||||
Info->ProcessorLevel = CpuInfo.ProcessorLevel;
|
|
||||||
Info->ProcessorRevision = CpuInfo.ProcessorRevision;
|
|
||||||
Info->ProcessorFeatureBits = CpuInfo.ProcessorFeatureBits;
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user