diff --git a/Source/Windows/Common/Allocator.cpp b/Source/Windows/Common/Allocator.cpp index 0643e062f..d535bdef7 100644 --- a/Source/Windows/Common/Allocator.cpp +++ b/Source/Windows/Common/Allocator.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT #include #include +#include "Windows/Common/FEXUnixLib.h" #include #include @@ -13,228 +14,13 @@ #include namespace FEX::Windows::Allocator { -#define PR_SET_VMA 0x53564d41 -#define PR_SET_VMA_ANON_NAME 0 - -#define MADV_HUGEPAGE 14 -#define MADV_NOHUGEPAGE 15 - -namespace Trampoline { - struct madvise_data { - const void* addr; - size_t size; - int advise; - }; - - struct prctl_data { - int op; - uint64_t attr; - const void* addr; - size_t size; - const char* name; - uint64_t ret; - }; - - __attribute__((naked)) uint64_t wine_prctl(prctl_data* d) { - asm volatile( - R"( - .globl wine_prctl_begin - wine_prctl_begin: - mov x19, x0; - mov x8, 167; // prctl - ldr x0, [x19]; // op - ldp x1, x2, [x19, %[attr_offset]]; // {attr, addr} - ldp x3, x4, [x19, %[size_offset]]; // {size, name} - svc #0; - str x0, [x19, %[ret_offset]]; - // Tell wine it was all groovy. - mov x0, 0; - ret; - - .globl wine_prctl_end - wine_prctl_end: - )" - : - : [attr_offset] "i"(offsetof(prctl_data, attr)), [size_offset] "i"(offsetof(prctl_data, size)), [ret_offset] "i"(offsetof(prctl_data, ret)) - : "memory"); - }; - - __attribute__((naked)) uint64_t wine_madvise(madvise_data* d) { - asm volatile(R"( - .globl wine_madvise_begin - wine_madvise_begin: - mov x8, 233; // madvise - ldr x2, [x0, %[advise_offset]]; // advise - ldp x0, x1, [x0]; // {addr, size} - svc #0; - // Tell wine it was all groovy. - mov x0, 0; - ret; - - .globl wine_madvise_end - wine_madvise_end: - )" ::[advise_offset] "i"(offsetof(madvise_data, advise)) - : "memory"); - } - - extern "C" uint64_t wine_madvise_begin; - extern "C" uint64_t wine_madvise_end; - - void* const wine_madvise_begin_loc = &wine_madvise_begin; - void* const wine_madvise_end_loc = &wine_madvise_end; - - extern "C" uint64_t wine_prctl_begin; - extern "C" uint64_t wine_prctl_end; - - void* const wine_prctl_begin_loc = &wine_prctl_begin; - void* const wine_prctl_end_loc = &wine_prctl_end; - - extern NTSTATUS(WINAPI* __wine_unix_call_dispatcher)(uint64_t, unsigned int, void*); - decltype(__wine_unix_call_dispatcher) WineUnixCall; - - enum unix_function_indexes { - INDEX_PRCTL = 0, - INDEX_MADVISE = 1, - INDEX_MAX, - }; - static std::array unix_functions {}; - - static uint64_t wine_prctl_trampoline(int op, uint64_t attr, const void* addr, size_t size, const char* name) { - prctl_data d { - .op = op, - .attr = attr, - .addr = addr, - .size = size, - .name = name, - }; - WineUnixCall(reinterpret_cast(unix_functions.data()), INDEX_PRCTL, &d); - return d.ret; - } - - static uint64_t wine_madvise_trampoline(const void* addr, size_t size, int advice) { - madvise_data d { - .addr = addr, - .size = size, - .advise = advice, - }; - WineUnixCall(reinterpret_cast(unix_functions.data()), INDEX_MADVISE, &d); - return 0; - } - - void VirtualName(const char* Name, const void* Ptr, size_t Size) { - static bool Supports {true}; - if (Supports) { - auto Result = wine_prctl_trampoline(PR_SET_VMA, PR_SET_VMA_ANON_NAME, Ptr, Size, Name); - if (Result != 0) { - // Disable any additional attempts. - Supports = false; - } - } - } - - void VirtualTHPControl(const void* Ptr, size_t Size, FEXCore::Allocator::THPControl Control) { - wine_madvise_trampoline(Ptr, Size, Control == FEXCore::Allocator::THPControl::Disable ? MADV_NOHUGEPAGE : MADV_HUGEPAGE); - } -} // namespace Trampoline - -// This code path will eventually crash once Wine and the kernel implements `userspace syscall dispatch`. -// FEX will need to switch over to using WINE's unixlib syscall approach then. -// See `SetupHooks` for why unixlib doesn't work today. -namespace Illegal { - __attribute__((naked)) uint64_t prctl(int op, uint64_t attr, const void* addr, size_t size, const char* Name) { - asm volatile(R"( - mov x8, 167; // prctl - svc #0; - ret; - )" :: - : "memory"); - } - - __attribute__((naked)) uint64_t madvise(const void* addr, size_t size, int advice) { - asm volatile(R"( - mov x8, 233; // madvise - svc #0; - ret; - )" :: - : "memory"); - } - - void VirtualName(const char* Name, const void* Ptr, size_t Size) { - static bool Supports {true}; - if (Supports) { - auto Result = prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, Ptr, Size, Name); - if (Result != 0) { - // Disable any additional attempts. - Supports = false; - } - } - } - - void VirtualTHPControl(const void* Ptr, size_t Size, FEXCore::Allocator::THPControl Control) { - madvise(Ptr, Size, Control == FEXCore::Allocator::THPControl::Disable ? MADV_NOHUGEPAGE : MADV_HUGEPAGE); - } -} // namespace Illegal - void SetupHooks(HMODULE ntdll) { - // If this symbol doesn't exist, then we aren't running under WINE. - const auto Sym = GetProcAddress(ntdll, "__wine_unix_call_dispatcher"); - - if (!Sym) { - return; - } - FEXCore::Allocator::HookPtrs Ptrs {}; - // Wine will soon require us to use unixlib for calling helper routines that use Linux syscalls. - // It needs this for `userspace syscall dispatch` to capture rogue applications doing raw Windows syscalls. - // If FEX doesn't use unixlib, then when WINE and a kernel implements this, then our "illegal" path will start crashing. - // - // This code currently conflicts with FEX's `Call Checker` since the latter captures uses of `unixlib`. - // We hence have to stick to the `Illegal::` functions until a workaround is implemented. - if constexpr (false) { - // NTSTATUS __wine_unix_call_dispatcher( unixlib_handle_t, unsigned int, void * ); - // - unixlib_handle_t is just an array of functions - // - uint32_t is just an index in to that - // - void* is the user provided pointer, gets loaded in to x0 in for the unix_function called. - // - Return value - SUCCESS or other error. - Trampoline::WineUnixCall = *reinterpret_cast(Sym); - - // This code must be copied over to allocated memory from top down allocations apparently. - auto Code = reinterpret_cast( - ::VirtualAlloc(nullptr, FEXCore::Utils::FEX_PAGE_SIZE, MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE)); - if (!Code) { - return; - } - - size_t CurrentOffset {}; - const size_t prctl_size = - reinterpret_cast(Trampoline::wine_prctl_end_loc) - reinterpret_cast(Trampoline::wine_prctl_begin_loc); - const size_t madvise_size = - reinterpret_cast(Trampoline::wine_madvise_end_loc) - reinterpret_cast(Trampoline::wine_madvise_begin_loc); - - // Copy prctl. - memcpy(Code + CurrentOffset, Trampoline::wine_prctl_begin_loc, prctl_size); - Trampoline::unix_functions[Trampoline::INDEX_PRCTL] = Code + CurrentOffset; - CurrentOffset += prctl_size; - - // Copy madvise. - memcpy(Code + CurrentOffset, Trampoline::wine_madvise_begin_loc, madvise_size); - Trampoline::unix_functions[Trampoline::INDEX_MADVISE] = Code + CurrentOffset; - - // Protect the page now. - FEXCore::Allocator::VirtualProtect(Code, FEXCore::Utils::FEX_PAGE_SIZE, - FEXCore::Allocator::ProtectOptions::Read | FEXCore::Allocator::ProtectOptions::Exec); - - Ptrs = { - .VirtualName = Trampoline::VirtualName, - .VirtualTHPControl = Trampoline::VirtualTHPControl, - }; - } else { - Ptrs = { - .VirtualName = Illegal::VirtualName, - .VirtualTHPControl = Illegal::VirtualTHPControl, - }; - } + Ptrs = { + .VirtualName = UnixLib::VirtualName, + .VirtualTHPControl = UnixLib::VirtualTHPControl, + }; SYSTEM_INFO system_info {}; GetSystemInfo(&system_info); diff --git a/Source/Windows/Common/FEXUnixLib.cpp b/Source/Windows/Common/FEXUnixLib.cpp index 301a9f29c..dfdd5bc83 100644 --- a/Source/Windows/Common/FEXUnixLib.cpp +++ b/Source/Windows/Common/FEXUnixLib.cpp @@ -1,16 +1,26 @@ // SPDX-License-Identifier: MIT +#include #include #include #include +#include #define __WINESRC__ #include #include #include "FEXUnixLib.h" +#define PR_SET_VMA 0x53564d41 +#define PR_SET_VMA_ANON_NAME 0 + +#define MADV_HUGEPAGE 14 +#define MADV_NOHUGEPAGE 15 + extern "C" IMAGE_DOS_HEADER __ImageBase; namespace FEX::Windows::UnixLib { +static bool UsingNTQueryPath {}; +static bool SupportsVirtualName {true}; unixlib_handle_t UnixLibHandle {}; decltype(__wine_unix_call_dispatcher) UnixCallDispatcher {}; @@ -30,6 +40,50 @@ static NTSTATUS __attribute__((naked)) TrampolineCall(unixlib_handle_t, unsigned : "memory"); } #endif +// This code path will eventually crash once Wine and the kernel implements `userspace syscall dispatch`. +// FEX will need to switch over to using WINE's unixlib syscall approach then. +namespace Illegal { + __attribute__((naked)) uint64_t prctl(int op, uint64_t attr, const void* addr, size_t size, const char* Name) { + asm volatile(R"( + mov x8, 167; // prctl + svc #0; + ret; + )" :: + : "memory"); + } + + __attribute__((naked)) uint64_t madvise(const void* addr, size_t size, int advice) { + asm volatile(R"( + mov x8, 233; // madvise + svc #0; + ret; + )" :: + : "memory"); + } + + __attribute__((naked)) uint64_t linux_getpid() { + asm volatile(R"( + mov x8, 172; + svc #0; + ret; + )" :: + : "r0", "r8"); + } + + void VirtualName(const char* Name, const void* Ptr, size_t Size) { + if (SupportsVirtualName) { + auto Result = prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, Ptr, Size, Name); + if (Result != 0) { + // Disable any additional attempts. + SupportsVirtualName = false; + } + } + } + + void VirtualTHPControl(const void* Ptr, size_t Size, FEXCore::Allocator::THPControl Control) { + madvise(Ptr, Size, Control == FEXCore::Allocator::THPControl::Disable ? MADV_NOHUGEPAGE : MADV_HUGEPAGE); + } +} // namespace Illegal bool Init(HMODULE NtDll) { const auto Sym = GetProcAddress(NtDll, "__wine_unix_call_dispatcher"); @@ -99,4 +153,137 @@ bool TryEnableHardwareTSO() { return Status == STATUS_SUCCESS; } +bool SetKernelUnalignedAtomicControl(uint64_t Flags) { + if (UnixLibAvailable()) { + // UnixLib path. + FEXUnixLib_SetKernelUnalignedAtomicControl Args { + .Flags = Flags, + }; + + return Call(FEXUnixLibFunctions::SetKernelUnalignedAtomicControl, &Args) == STATUS_SUCCESS; + } + + // Legacy Proton path. + return NtSetInformationProcess(NtCurrentProcess(), ProcessFexUnalignAtomic, &Flags, sizeof(Flags)) == STATUS_SUCCESS; +} + +void VirtualTHPControl(const void* Ptr, size_t Size, FEXCore::Allocator::THPControl Control) { + if (UnixLibAvailable()) { + // UnixLib path. + FEXUnixLib_Madvise Args { + .Addr = Ptr, + .Size = Size, + .Advise = Control == FEXCore::Allocator::THPControl::Disable ? MADV_NOHUGEPAGE : MADV_HUGEPAGE, + }; + + Call(FEXUnixLibFunctions::Madvise, &Args); + return; + } + + // Legacy Proton path. + Illegal::VirtualTHPControl(Ptr, Size, Control); +} + +void VirtualName(const char* Name, const void* Ptr, size_t Size) { + if (!SupportsVirtualName) { + return; + } + + if (UnixLibAvailable()) { + // UnixLib path. + FEXUnixLib_SetVMAName Args { + .Addr = Ptr, + .Size = Size, + .Name = Name, + }; + + if (Call(FEXUnixLibFunctions::SetVMAName, &Args) != STATUS_SUCCESS) { + SupportsVirtualName = false; + } + return; + } + + // Legacy Proton path. + Illegal::VirtualName(Name, Ptr, Size); +} + +SHMSlotResult AllocateSHMSlots(void* SHMBase, uint32_t MapSize, uint32_t MaxSize) { + if (UnixLibAvailable()) { + // UnixLib path. + FEXUnixLib_GetSHMStatsVMA Args { + .SHMBase = SHMBase, + .MapSize = MapSize, + .MaxSize = MaxSize, + }; + + if (Call(FEXUnixLibFunctions::GetSHMStatsVMA, &Args) == STATUS_SUCCESS) { + return { + .SHMBase = Args.SHMBase, + .MappedSize = Args.MapSize, + }; + } + + return {}; + } + + // Legacy Proton path. + // Magic WINE+FEX path. + if (SHMBase == nullptr || UsingNTQueryPath) { + MEMORY_FEX_STATS_SHM_INFORMATION Info { + .shm_base = SHMBase, + .map_size = MapSize, + .max_size = MaxSize, + }; + size_t Length {}; + auto Result = NtQueryVirtualMemory(NtCurrentProcess(), nullptr, MemoryFexStatsShm, &Info, sizeof(Info), &Length); + if (!Result) { + UsingNTQueryPath = true; + return { + .SHMBase = Info.shm_base, + .MappedSize = MapSize, + }; + } + } + + // Opaque handle path, doesn't support resizing. + auto handle = CreateFile(fextl::fmt::format("/dev/shm/fex-{}-stats", Illegal::linux_getpid()).c_str(), GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + + // Create the section mapping for the file handle for the full size. + HANDLE SectionMapping; + LARGE_INTEGER SectionSize {{MaxSize}}; + auto Result = NtCreateSection(&SectionMapping, SECTION_EXTEND_SIZE | SECTION_MAP_READ | SECTION_MAP_WRITE, nullptr, &SectionSize, + PAGE_READWRITE, SEC_COMMIT, handle); + if (Result != STATUS_SUCCESS) { + CloseHandle(handle); + return {}; + } + + // Section mapping is used from now on. + CloseHandle(handle); + + // Now actually map the view of the section. + void* Base = nullptr; + size_t FullSize = MaxSize; + Result = NtMapViewOfSection(SectionMapping, NtCurrentProcess(), &Base, 0, 0, nullptr, &FullSize, ViewUnmap, MEM_RESERVE | MEM_TOP_DOWN, + PAGE_READWRITE); + if (Result != STATUS_SUCCESS) { + CloseHandle(SectionMapping); + return {}; + } + + return {.SHMBase = Base, .MappedSize = MaxSize}; +} + +void DeleteSHMStatsFile() { + if (UnixLibAvailable()) { + // UnixLib path. + Call(FEXUnixLibFunctions::DeleteSHMStatsFile, nullptr); + return; + } + + // Legacy Proton path. + DeleteFile(fextl::fmt::format("/dev/shm/fex-{}-stats", Illegal::linux_getpid()).c_str()); +} + } // namespace FEX::Windows::UnixLib diff --git a/Source/Windows/Common/FEXUnixLib.h b/Source/Windows/Common/FEXUnixLib.h index bd98c27f5..36da52c54 100644 --- a/Source/Windows/Common/FEXUnixLib.h +++ b/Source/Windows/Common/FEXUnixLib.h @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT #pragma once #include +#include #include #include "../UnixLib/FEXUnixLib.h" #include "wine/unixlib.h" @@ -25,4 +26,23 @@ bool Init(HMODULE NtDll); * @return true if hardware TSO is supported and enabled. */ bool TryEnableHardwareTSO(); + +/** + * @brief Tries to enable kernel unaligned atomic handling if supported. + * + * @param Flags Which unaligned types to enable + * + * @return true if the flags were enabled + */ +bool SetKernelUnalignedAtomicControl(uint64_t Flags); + +void VirtualTHPControl(const void* Ptr, size_t Size, FEXCore::Allocator::THPControl Control); +void VirtualName(const char* Name, const void* Ptr, size_t Size); + +struct SHMSlotResult { + void* SHMBase; + uint32_t MappedSize; +}; +SHMSlotResult AllocateSHMSlots(void* SHMBase, uint32_t MapSize, uint32_t MaxSize); +void DeleteSHMStatsFile(); } // namespace FEX::Windows::UnixLib diff --git a/Source/Windows/Common/SHMStats.cpp b/Source/Windows/Common/SHMStats.cpp index 9d844b3a3..81daa0d72 100644 --- a/Source/Windows/Common/SHMStats.cpp +++ b/Source/Windows/Common/SHMStats.cpp @@ -1,4 +1,5 @@ // SPDX-License-Identifier: MIT +#include "Windows/Common/FEXUnixLib.h" #include "Windows/Common/SHMStats.h" #include @@ -12,88 +13,34 @@ #include namespace FEX::Windows { -__attribute__((naked)) uint64_t linux_getpid() { - asm volatile(R"( - mov x8, 172; - svc #0; - ret; - )" :: - : "r0", "r8"); -} - uint32_t StatAlloc::FrontendAllocateSlots(uint32_t NewSize) { - if (CurrentSize == MAX_STATS_SIZE || !UsingNTQueryPath) { + if (CurrentSize == MAX_STATS_SIZE) { LogMan::Msg::DFmt("Ran out of slots. Can't allocate more"); return CurrentSize; } - MEMORY_FEX_STATS_SHM_INFORMATION Info { - .shm_base = nullptr, - .map_size = std::min(CurrentSize * 2, MAX_STATS_SIZE), - .max_size = MAX_STATS_SIZE, - }; - size_t Length {}; - auto Result = NtQueryVirtualMemory(NtCurrentProcess(), nullptr, MemoryFexStatsShm, &Info, sizeof(Info), &Length); - if (!Result) { - CurrentSize = Info.map_size; + auto Result = UnixLib::AllocateSHMSlots(Base, std::min(CurrentSize * 2, MAX_STATS_SIZE), MAX_STATS_SIZE); + + if (Result.SHMBase) { + CurrentSize = Result.MappedSize; } return CurrentSize; } StatAlloc::StatAlloc(FEXCore::SHMStats::AppType AppType) { - // Try wine+fex magic path. + auto Result = UnixLib::AllocateSHMSlots(Base, FEXCore::Utils::FEX_PAGE_SIZE, MAX_STATS_SIZE); - { - MEMORY_FEX_STATS_SHM_INFORMATION Info { - .shm_base = nullptr, - .map_size = FEXCore::Utils::FEX_PAGE_SIZE, - .max_size = MAX_STATS_SIZE, - }; - size_t Length {}; - auto Result = NtQueryVirtualMemory(NtCurrentProcess(), nullptr, MemoryFexStatsShm, &Info, sizeof(Info), &Length); - if (!Result) { - UsingNTQueryPath = true; - CurrentSize = Info.map_size; - Base = Info.shm_base; - SaveHeader(AppType); - return; - } - } - CurrentSize = MAX_STATS_SIZE; - - auto handle = CreateFile(fextl::fmt::format("/dev/shm/fex-{}-stats", linux_getpid()).c_str(), GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - - // Create the section mapping for the file handle for the full size. - HANDLE SectionMapping; - LARGE_INTEGER SectionSize {{MAX_STATS_SIZE}}; - auto Result = NtCreateSection(&SectionMapping, SECTION_EXTEND_SIZE | SECTION_MAP_READ | SECTION_MAP_WRITE, nullptr, &SectionSize, - PAGE_READWRITE, SEC_COMMIT, handle); - if (Result != 0) { - CloseHandle(handle); + if (Result.SHMBase) { + CurrentSize = Result.MappedSize; + Base = Result.SHMBase; + SaveHeader(AppType); return; } - - // Section mapping is used from now on. - CloseHandle(handle); - - // Now actually map the view of the section. - Base = 0; - size_t FullSize = MAX_STATS_SIZE; - Result = NtMapViewOfSection(SectionMapping, NtCurrentProcess(), &Base, 0, 0, nullptr, &FullSize, ViewUnmap, MEM_RESERVE | MEM_TOP_DOWN, - PAGE_READWRITE); - if (Result != 0) { - CloseHandle(SectionMapping); - return; - } - - // Once WINE supports NtExtendSection and SECTION_EXTEND_SIZE correctly then we can map/commit a single page, map the full MAX_STATS_SIZE - // view as reserved, and extend the view using NtExtendSection. - SaveHeader(AppType); } + StatAlloc::~StatAlloc() { - DeleteFile(fextl::fmt::format("/dev/shm/fex-{}-stats", linux_getpid()).c_str()); + UnixLib::DeleteSHMStatsFile(); } } // namespace FEX::Windows diff --git a/Source/Windows/Common/SHMStats.h b/Source/Windows/Common/SHMStats.h index c8bbc6172..786efc2c6 100644 --- a/Source/Windows/Common/SHMStats.h +++ b/Source/Windows/Common/SHMStats.h @@ -23,7 +23,6 @@ public: private: uint32_t FrontendAllocateSlots(uint32_t NewSize) override; - bool UsingNTQueryPath {}; }; } // namespace FEX::Windows diff --git a/Source/Windows/Common/TSOHandlerConfig.h b/Source/Windows/Common/TSOHandlerConfig.h index cbde622da..f63478d79 100644 --- a/Source/Windows/Common/TSOHandlerConfig.h +++ b/Source/Windows/Common/TSOHandlerConfig.h @@ -24,7 +24,7 @@ public: uint64_t Flags = (StrictInProcessSplitLocks() ? FEX_UNALIGN_ATOMIC_STRICT_SPLIT_LOCKS : 0) | (KernelUnalignedAtomicBackpatching() ? FEX_UNALIGN_ATOMIC_BACKPATCH : 0) | FEX_UNALIGN_ATOMIC_EMULATE; - if (NtSetInformationProcess(NtCurrentProcess(), ProcessFexUnalignAtomic, &Flags, sizeof(Flags)) == STATUS_SUCCESS) { + if (UnixLib::SetKernelUnalignedAtomicControl(Flags)) { LogMan::Msg::IFmt("FEX: Kernel unaligned atomics enabled!"); } } diff --git a/Source/Windows/UnixLib/FEXUnixLib.cpp b/Source/Windows/UnixLib/FEXUnixLib.cpp index e5d6014a5..692e459f1 100644 --- a/Source/Windows/UnixLib/FEXUnixLib.cpp +++ b/Source/Windows/UnixLib/FEXUnixLib.cpp @@ -1,7 +1,11 @@ // SPDX-License-Identifier: MIT #include #include +#include +#include +#include #include +#include #include "FEXUnixLib.h" @@ -31,6 +35,15 @@ constexpr NTSTATUS STATUS_INTERNAL_ERROR = 0xC00000E5; #ifndef PR_SET_MEM_MODEL_TSO #define PR_SET_MEM_MODEL_TSO 1 #endif +#ifndef PR_ARM64_SET_UNALIGN_ATOMIC +#define PR_ARM64_SET_UNALIGN_ATOMIC 0x46455849 +#endif +#ifndef PR_SET_VMA +#define PR_SET_VMA 0x53564d41 +#endif +#ifndef PR_SET_VMA_ANON_NAME +#define PR_SET_VMA_ANON_NAME 0 +#endif static NTSTATUS FEXUnixLib_SetHardwareTSOControlHandler(void* _Args) { auto Args = reinterpret_cast(_Args); @@ -46,6 +59,117 @@ static NTSTATUS FEXUnixLib_SetHardwareTSOControlHandler(void* _Args) { return STATUS_SUCCESS; } -extern "C" const unixlib_entry_t __wine_unix_call_funcs[] = {FEXUnixLib_SetHardwareTSOControlHandler}; +static NTSTATUS FEXUnixLib_SetKernelUnalignedAtomicControlHandler(void* _Args) { + auto Args = reinterpret_cast(_Args); + return prctl(PR_ARM64_SET_UNALIGN_ATOMIC, Args->Flags, 0, 0, 0) == 0 ? STATUS_SUCCESS : STATUS_NOT_SUPPORTED; +} + +static NTSTATUS FEXUnixLib_MadviseHandler(void* _Args) { + auto Args = reinterpret_cast(_Args); + return madvise(const_cast(Args->Addr), Args->Size, Args->Advise) == 0 ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR; +} + +static NTSTATUS FEXUnixLib_SetVMANameHandler(void* _Args) { + auto Args = reinterpret_cast(_Args); + return prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast(Args->Addr), Args->Size, Args->Name) == 0 ? + STATUS_SUCCESS : + STATUS_INTERNAL_ERROR; +} + +constexpr static int USER_PERMS = S_IRWXU | S_IRWXG | S_IRWXO; +static inline void* InitializeSHM(uint32_t MapSize, uint32_t MaxSize) { + void* Base {}; + const std::string Name = "fex-" + std::to_string(getpid()) + "-stats"; + int fd = shm_open(Name.c_str(), O_CREAT | O_TRUNC | O_RDWR, USER_PERMS); + + if (fd == -1) { + return nullptr; + } + + if (ftruncate(fd, MapSize) == -1) { + goto err; + } + + // Reserve a region of MAX_STATS_SIZE so we can grow the allocation buffer. + // Number of thread slots when ThreadStatsHeader == 64bytes and ThreadStats == 40bytes: + // 1 page: 99 slots + // 1 MB: 26211 slots + // 128 MB: 3355440 slots + Base = mmap(nullptr, MaxSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); + if (Base == MAP_FAILED) { + Base = nullptr; + goto err; + } + + prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast(Base), MaxSize, "FEXMem_Misc"); + + // Allocate a small working shared space for now, grow as necessary. + { + auto SharedBase = mmap(Base, MapSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0); + if (SharedBase == MAP_FAILED) { + ::munmap(Base, MaxSize); + Base = nullptr; + goto err; + } + } + +err: + close(fd); + + return Base; +} + +static inline bool AllocateMoreSlots(void* SHMBase, uint32_t NewSize) { + bool Result {}; + const std::string Name = "fex-" + std::to_string(getpid()) + "-stats"; + + // When allocating more slots, open the fd without O_TRUNC | O_CREAT. + int fd = shm_open(Name.c_str(), O_RDWR, USER_PERMS); + if (fd == -1) { + return false; + } + + if (ftruncate(fd, NewSize) == -1) { + goto err; + } + + { + auto SharedBase = mmap(SHMBase, NewSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0); + if (SharedBase == MAP_FAILED) { + goto err; + } + } + + Result = true; + +err: + close(fd); + return Result; +} + +static NTSTATUS FEXUnixLib_GetSHMStatsVMAHandler(void* _Args) { + auto Args = reinterpret_cast(_Args); + if (Args->SHMBase == nullptr) { + Args->SHMBase = InitializeSHM(Args->MapSize, Args->MaxSize); + return Args->SHMBase ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR; + } + + return AllocateMoreSlots(Args->SHMBase, Args->MapSize) ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR; +} + +static NTSTATUS FEXUnixLib_DeleteSHMStatsFileHandler(void* _Args) { + const std::string Name = "fex-" + std::to_string(getpid()) + "-stats"; + shm_unlink(Name.c_str()); + return STATUS_SUCCESS; +} + +extern "C" const unixlib_entry_t __wine_unix_call_funcs[] = { + FEXUnixLib_SetHardwareTSOControlHandler, + FEXUnixLib_SetKernelUnalignedAtomicControlHandler, + FEXUnixLib_MadviseHandler, + FEXUnixLib_SetVMANameHandler, + FEXUnixLib_GetSHMStatsVMAHandler, + FEXUnixLib_DeleteSHMStatsFileHandler, +}; static_assert(sizeof(__wine_unix_call_funcs) / sizeof(__wine_unix_call_funcs[0]) == ToUnderlying(FEXUnixLibFunctions::MAX)); diff --git a/Source/Windows/UnixLib/FEXUnixLib.h b/Source/Windows/UnixLib/FEXUnixLib.h index 4fa18fcf5..4795d62c0 100644 --- a/Source/Windows/UnixLib/FEXUnixLib.h +++ b/Source/Windows/UnixLib/FEXUnixLib.h @@ -1,9 +1,15 @@ // SPDX-License-Identifier: MIT #pragma once +#include #include enum class FEXUnixLibFunctions : uint32_t { SetHardwareTSOControl, + SetKernelUnalignedAtomicControl, + Madvise, + SetVMAName, + GetSHMStatsVMA, + DeleteSHMStatsFile, MAX, }; @@ -13,3 +19,35 @@ struct FEXUnixLib_SetHardwareTSOControlArgs { bool Enable; }; static_assert(sizeof(FEXUnixLib_SetHardwareTSOControlArgs) == 1); + +// These match the prctl flag values for PR_ARM64_SET_UNALIGN_ATOMIC +static constexpr uint64_t FEX_UNALIGN_ATOMIC_EMULATE = 1ULL << 0; +static constexpr uint64_t FEX_UNALIGN_ATOMIC_BACKPATCH = 1ULL << 1; +static constexpr uint64_t FEX_UNALIGN_ATOMIC_STRICT_SPLIT_LOCKS = 1ULL << 2; + +struct FEXUnixLib_SetKernelUnalignedAtomicControl { + uint64_t Flags; +}; +static_assert(sizeof(FEXUnixLib_SetKernelUnalignedAtomicControl) == 8); + +struct FEXUnixLib_Madvise { + const void* Addr; + size_t Size; + int32_t Advise; + uint32_t pad; +}; +static_assert(sizeof(FEXUnixLib_Madvise) == 24); + +struct FEXUnixLib_SetVMAName { + const void* Addr; + size_t Size; + const char* Name; +}; +static_assert(sizeof(FEXUnixLib_SetVMAName) == 24); + +struct FEXUnixLib_GetSHMStatsVMA { + void* SHMBase; + uint32_t MapSize; + uint32_t MaxSize; +}; +static_assert(sizeof(FEXUnixLib_GetSHMStatsVMA) == 16); diff --git a/Source/Windows/include/winternl.h b/Source/Windows/include/winternl.h index d15e27c48..de9e28741 100644 --- a/Source/Windows/include/winternl.h +++ b/Source/Windows/include/winternl.h @@ -482,11 +482,6 @@ typedef enum _MEMORY_INFORMATION_CLASS { #define ProcessFexHardwareTso (PROCESSINFOCLASS)2000 #define ProcessFexUnalignAtomic (PROCESSINFOCLASS)2001 -// These match the prctl flag values -#define FEX_UNALIGN_ATOMIC_EMULATE (1ULL << 0) -#define FEX_UNALIGN_ATOMIC_BACKPATCH (1ULL << 1) -#define FEX_UNALIGN_ATOMIC_STRICT_SPLIT_LOCKS (1ULL << 2) - typedef enum _KEY_VALUE_INFORMATION_CLASS { KeyValueBasicInformation, KeyValueFullInformation,