-
Notifications
You must be signed in to change notification settings - Fork 135
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3367 from bylaws/prepwow
Windows: Commonise WOW64 logic that can be shared with ARM64EC
- Loading branch information
Showing
10 changed files
with
306 additions
and
1,574 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,22 @@ | ||
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( | ||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lib${name}.a | ||
COMMAND ${CMAKE_DLLTOOL} -d ${CMAKE_CURRENT_SOURCE_DIR}/Defs/${name}.def -k -l lib${name}.a | ||
COMMENT "Building 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_ex}.a | ||
COMMENT "Building lib${name_ex}.a" | ||
) | ||
|
||
add_library(${name} SHARED IMPORTED) | ||
set_property(TARGET ${name} PROPERTY IMPORTED_IMPLIB ${CMAKE_CURRENT_BINARY_DIR}/lib${name}.a) | ||
add_dependencies(${name} ${name}lib) | ||
add_library(${name_ex} SHARED IMPORTED) | ||
set_property(TARGET ${name_ex} PROPERTY IMPORTED_IMPLIB ${CMAKE_CURRENT_BINARY_DIR}/lib${name_ex}.a) | ||
add_dependencies(${name_ex} ${name_ex}lib) | ||
endfunction() | ||
|
||
build_implib(ntdll) | ||
build_implib(wow64) | ||
|
||
add_subdirectory(Common) | ||
|
||
if (_M_ARM_64) | ||
add_subdirectory(WOW64) | ||
endif() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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/" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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{}; | ||
}; | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
}; | ||
} |
Oops, something went wrong.