diff --git a/.github/workflows/wine_build/action.yml b/.github/workflows/wine_build/action.yml index 321ff931eb..50d50b641c 100644 --- a/.github/workflows/wine_build/action.yml +++ b/.github/workflows/wine_build/action.yml @@ -33,3 +33,17 @@ runs: - name: Install shell: bash run: DESTDIR="$PWD"/install cmake --build build_${{ inputs.target }} -t install + + - name: Configure Unixlib + shell: bash + run: | + cmake -S Source/Windows/Unixlib -B build_unixlib_${{ inputs.target }} -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ + -G Ninja -DCMAKE_INSTALL_LIBDIR=/usr/lib/wine/aarch64-unix -DCMAKE_INSTALL_PREFIX=/usr + + - name: Build Unixlib + shell: bash + run: cmake --build build_unixlib_${{ inputs.target }} + + - name: Install Unixlib + shell: bash + run: DESTDIR="$PWD"/install cmake --build build_unixlib_${{ inputs.target }} -t install diff --git a/.github/workflows/wine_dll_artifacts.yml b/.github/workflows/wine_dll_artifacts.yml index 364b6a3e81..7a4ca69a06 100644 --- a/.github/workflows/wine_dll_artifacts.yml +++ b/.github/workflows/wine_dll_artifacts.yml @@ -50,6 +50,8 @@ jobs: with: overwrite: true name: wine_dll_artifacts - path: ${{ github.workspace }}/install/usr/lib/wine/aarch64-windows/lib*.dll + path: | + ${{ github.workspace }}/install/usr/lib/wine/aarch64-windows/lib*.dll + ${{ github.workspace }}/install/usr/lib/wine/aarch64-unix/lib*.so retention-days: 60 compression-level: 9 diff --git a/Source/Windows/ARM64EC/Module.cpp b/Source/Windows/ARM64EC/Module.cpp index 241da10388..df48544fed 100644 --- a/Source/Windows/ARM64EC/Module.cpp +++ b/Source/Windows/ARM64EC/Module.cpp @@ -59,6 +59,9 @@ desc: Implements the ARM64EC BT module API using FEXCore #include #include #include +#include + +#include "Unixlib/FEXUnixlib.h" namespace Exception { class ECSyscallHandler; @@ -597,8 +600,8 @@ NTSTATUS ProcessInit() { OvercommitTracker.emplace(IsWine); FEX::Windows::SetupEnvironmentVariableValues(NtDll); - - FEX::Windows::Allocator::SetupHooks(NtDll); + FEX::Windows::Allocator::SetupHooks(IsWine); + FEX::Windows::Unixlib::Init(NtDll); { auto HostFeatures = FEX::Windows::CPUFeatures::FetchHostFeatures(IsWine); @@ -627,15 +630,6 @@ NTSTATUS ProcessInit() { const uintptr_t KiUserExceptionDispatcherFFS = reinterpret_cast(GetProcAddress(NtDll, "KiUserExceptionDispatcher")); Exception::KiUserExceptionDispatcher = NtDllRedirectionLUT[KiUserExceptionDispatcherFFS - NtDllBase] + NtDllBase; - FEX_CONFIG_OPT(TSOEnabled, TSOENABLED); - if (TSOEnabled()) { - BOOL Enable = TRUE; - NTSTATUS Status = NtSetInformationProcess(NtCurrentProcess(), ProcessFexHardwareTso, &Enable, sizeof(Enable)); - if (Status == STATUS_SUCCESS) { - CTX->SetHardwareTSOSupport(true); - } - } - FEX_CONFIG_OPT(ProfileStats, PROFILESTATS); FEX_CONFIG_OPT(StartupSleep, STARTUPSLEEP); FEX_CONFIG_OPT(StartupSleepProcName, STARTUPSLEEPPROCNAME); diff --git a/Source/Windows/Common/Allocator.cpp b/Source/Windows/Common/Allocator.cpp index 0643e062f4..01ca18223a 100644 --- a/Source/Windows/Common/Allocator.cpp +++ b/Source/Windows/Common/Allocator.cpp @@ -2,242 +2,51 @@ #include #include -#include -#include -#include #include -#include #include #include -#include -#include +#include -namespace FEX::Windows::Allocator { -#define PR_SET_VMA 0x53564d41 -#define PR_SET_VMA_ANON_NAME 0 +#include "Unixlib/FEXUnixlib.h" #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; - } +namespace FEX::Windows::Allocator { - static uint64_t wine_madvise_trampoline(const void* addr, size_t size, int advice) { - madvise_data d { - .addr = addr, - .size = size, - .advise = advice, +static void VirtualName(const char* Name, const void* Ptr, size_t Size) { + static bool Supports {true}; + if (Supports) { + FexPrctlSetVMAParams Params { + .addr = const_cast(Ptr), + .size = Size, + .name = Name, }; - 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; - } + if (WINE_UNIX_CALL(fex_unix_prctl_set_vma, &Params) || Params.result != 0) { + 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"); +static void VirtualTHPControl(const void* Ptr, size_t Size, FEXCore::Allocator::THPControl Control) { + FexMadviseParams Params { + .addr = const_cast(Ptr), + .size = Size, + .advise = Control == FEXCore::Allocator::THPControl::Disable ? MADV_NOHUGEPAGE : MADV_HUGEPAGE, + }; + WINE_UNIX_CALL(fex_unix_madvise, &Params); +} - if (!Sym) { +void SetupHooks(bool IsWine) { + if (!IsWine) { 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, - }; - } - SYSTEM_INFO system_info {}; GetSystemInfo(&system_info); - FEXCore::Allocator::SetupHooks(system_info.dwPageSize, Ptrs); + FEXCore::Allocator::SetupHooks(system_info.dwPageSize, { + .VirtualName = VirtualName, + .VirtualTHPControl = VirtualTHPControl, + }); } } // namespace FEX::Windows::Allocator diff --git a/Source/Windows/Common/Allocator.h b/Source/Windows/Common/Allocator.h index 082ebd1cf9..36038f3fbb 100644 --- a/Source/Windows/Common/Allocator.h +++ b/Source/Windows/Common/Allocator.h @@ -3,5 +3,5 @@ #include namespace FEX::Windows::Allocator { -void SetupHooks(HMODULE ntdll); +void SetupHooks(bool IsWine); } diff --git a/Source/Windows/Common/CMakeLists.txt b/Source/Windows/Common/CMakeLists.txt index 3a0a71c070..b401ecfeff 100644 --- a/Source/Windows/Common/CMakeLists.txt +++ b/Source/Windows/Common/CMakeLists.txt @@ -13,7 +13,8 @@ add_library(CommonWindows STATIC SHMStats.cpp InvalidationTracker.cpp ImageTracker.cpp - Logging.cpp) + Logging.cpp + Unixlib.cpp) target_link_libraries(CommonWindows FEXCore_Base) -target_include_directories(CommonWindows PRIVATE "${CMAKE_SOURCE_DIR}/Source/Windows/include/") +target_include_directories(CommonWindows PRIVATE "${CMAKE_SOURCE_DIR}/Source/Windows/include/" "${CMAKE_SOURCE_DIR}/Source/Windows/") diff --git a/Source/Windows/Common/SHMStats.cpp b/Source/Windows/Common/SHMStats.cpp index 9d844b3a36..08a804a314 100644 --- a/Source/Windows/Common/SHMStats.cpp +++ b/Source/Windows/Common/SHMStats.cpp @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT #include "Windows/Common/SHMStats.h" -#include #include #include @@ -9,91 +8,45 @@ #include #include #include -#include +#include + +#include "Unixlib/FEXUnixlib.h" 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 { + FexStatsSHMParams Params { .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; + if (!WINE_UNIX_CALL(fex_unix_get_stats_shm, &Params)) { + CurrentSize = Params.map_size; } return CurrentSize; } StatAlloc::StatAlloc(FEXCore::SHMStats::AppType AppType) { - // Try wine+fex magic path. - - { - 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); - 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); + FexStatsSHMParams Params { + .shm_base = nullptr, + .map_size = FEXCore::Utils::FEX_PAGE_SIZE, + .max_size = MAX_STATS_SIZE, + }; + if (WINE_UNIX_CALL(fex_unix_get_stats_shm, &Params)) { 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. + CurrentSize = Params.map_size; + Base = Params.shm_base; SaveHeader(AppType); } -StatAlloc::~StatAlloc() { - DeleteFile(fextl::fmt::format("/dev/shm/fex-{}-stats", linux_getpid()).c_str()); -} + +StatAlloc::~StatAlloc() {} } // namespace FEX::Windows diff --git a/Source/Windows/Common/SHMStats.h b/Source/Windows/Common/SHMStats.h index c8bbc61728..786efc2c60 100644 --- a/Source/Windows/Common/SHMStats.h +++ b/Source/Windows/Common/SHMStats.h @@ -23,7 +23,6 @@ class StatAlloc final : public FEX::SHMStats::StatAllocBase { 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 1a0a888ab6..1efb01e100 100644 --- a/Source/Windows/Common/TSOHandlerConfig.h +++ b/Source/Windows/Common/TSOHandlerConfig.h @@ -5,6 +5,9 @@ #include #include +#include "Unixlib/FEXUnixlib.h" +#include + namespace FEX::Windows { class TSOHandlerConfig final { public: @@ -16,9 +19,8 @@ class TSOHandlerConfig final { } if (TSOEnabled()) { - BOOL Enable = TRUE; - NTSTATUS Status = NtSetInformationProcess(NtCurrentProcess(), ProcessFexHardwareTso, &Enable, sizeof(Enable)); - if (Status == STATUS_SUCCESS) { + FexSetHardwareTSOParams Params {.enable = 1}; + if (WINE_UNIX_CALL(fex_unix_set_hardware_tso, &Params) == STATUS_SUCCESS) { CTX.SetHardwareTSOSupport(true); } } @@ -26,7 +28,8 @@ class TSOHandlerConfig final { 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) { + FexSetUnalignAtomicParams Params {.flags = Flags}; + if (WINE_UNIX_CALL(fex_unix_set_unalign_atomic, &Params) == STATUS_SUCCESS) { LogMan::Msg::IFmt("FEX: Kernel unaligned atomics enabled!"); } } diff --git a/Source/Windows/Common/Unixlib.cpp b/Source/Windows/Common/Unixlib.cpp new file mode 100644 index 0000000000..c4edf390e9 --- /dev/null +++ b/Source/Windows/Common/Unixlib.cpp @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MIT +#include +#include +#include +#include + +extern "C" IMAGE_DOS_HEADER __ImageBase; + +namespace FEX::Windows::Unixlib { + +static unixlib_handle_t UnixlibHandle; + +#ifdef ARCHITECTURE_arm64ec +// On ARM64EC, indirect calls go through __os_arm64x_dispatch_icall which invokes +// FEX's custom call checker. Use a naked trampoline to bypass the dispatch mechanism +// and call the unix dispatcher directly via a register branch. +static CallDispatcher DispatcherDirect; + +static NTSTATUS __attribute__((naked)) TrampolineCall(unixlib_handle_t, unsigned int, void*) { + asm("adrp x16, %[disp]\n\t" + "ldr x16, [x16, #:lo12:%[disp]]\n\t" + "br x16" ::[disp] "S"(&DispatcherDirect)); +} +#endif + +void Init(HMODULE NtDll) { + auto* DispatcherPtr = reinterpret_cast(GetProcAddress(NtDll, "__wine_unix_call_dispatcher")); + if (!DispatcherPtr) { + return; + } + +#ifdef ARCHITECTURE_arm64ec + DispatcherDirect = *DispatcherPtr; + Dispatcher = TrampolineCall; +#else + Dispatcher = *DispatcherPtr; +#endif + + if (NtQueryVirtualMemory(NtCurrentProcess(), &__ImageBase, MemoryWineUnixFuncs, &UnixlibHandle, sizeof(UnixlibHandle), nullptr)) { + Dispatcher = nullptr; + return; + } + Handle = &UnixlibHandle; +} +} // namespace FEX::Windows::Unixlib diff --git a/Source/Windows/Unixlib/CMakeLists.txt b/Source/Windows/Unixlib/CMakeLists.txt new file mode 100644 index 0000000000..f19828204b --- /dev/null +++ b/Source/Windows/Unixlib/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.14) +project(FEXUnixlib CXX) + +include(GNUInstallDirs) + +add_library(wow64fex_unixlib SHARED FEXUnixlib.cpp) +set_target_properties(wow64fex_unixlib PROPERTIES OUTPUT_NAME "wow64fex" PREFIX "lib" SUFFIX ".so") +target_link_libraries(wow64fex_unixlib PRIVATE rt) +install(TARGETS wow64fex_unixlib LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Runtime) + +add_library(arm64ecfex_unixlib SHARED FEXUnixlib.cpp) +set_target_properties(arm64ecfex_unixlib PROPERTIES OUTPUT_NAME "arm64ecfex" PREFIX "lib" SUFFIX ".so") +target_link_libraries(arm64ecfex_unixlib PRIVATE rt) +install(TARGETS arm64ecfex_unixlib LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Runtime) diff --git a/Source/Windows/Unixlib/FEXUnixlib.cpp b/Source/Windows/Unixlib/FEXUnixlib.cpp new file mode 100644 index 0000000000..9e60798605 --- /dev/null +++ b/Source/Windows/Unixlib/FEXUnixlib.cpp @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: MIT + +#include "FEXUnixlib.h" + +#include +#include +#include +#include +#include +#include +#include + +using NTSTATUS = int32_t; +using unixlib_entry_t = NTSTATUS (*)(void*); + +#define STATUS_SUCCESS ((NTSTATUS)0x00000000) +#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BB) +#define STATUS_INTERNAL_ERROR ((NTSTATUS)0xC00000E5) + +#ifndef PR_GET_MEM_MODEL +#define PR_GET_MEM_MODEL 0x6d4d444c +#endif +#ifndef PR_SET_MEM_MODEL +#define PR_SET_MEM_MODEL 0x4d4d444c +#endif +#ifndef PR_SET_MEM_MODEL_DEFAULT +#define PR_SET_MEM_MODEL_DEFAULT 0 +#endif +#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 constexpr size_t FEX_STATS_SHM_MAX_SIZE = 128 * 1024 * 1024; +static void* stats_shm_base = nullptr; + +static NTSTATUS unix_set_hardware_tso(void* args) { + auto* params = static_cast(args); + if (params->enable) { + int ret = prctl(PR_GET_MEM_MODEL, 0, 0, 0, 0); + if (ret == PR_SET_MEM_MODEL_DEFAULT) { + return prctl(PR_SET_MEM_MODEL, PR_SET_MEM_MODEL_TSO, 0, 0, 0) ? STATUS_NOT_SUPPORTED : STATUS_SUCCESS; + } + return ret == PR_SET_MEM_MODEL_TSO ? STATUS_SUCCESS : STATUS_NOT_SUPPORTED; + } + + prctl(PR_SET_MEM_MODEL, PR_SET_MEM_MODEL_DEFAULT, 0, 0, 0); + return STATUS_SUCCESS; +} + +static NTSTATUS unix_set_unalign_atomic(void* args) { + auto* params = static_cast(args); + return prctl(PR_ARM64_SET_UNALIGN_ATOMIC, params->flags, 0, 0, 0) ? STATUS_NOT_SUPPORTED : STATUS_SUCCESS; +} + +static NTSTATUS unix_get_stats_shm(void* args) { + auto* params = static_cast(args); + + auto name = "fex-" + std::to_string(getpid()) + "-stats"; + int oflag = O_RDWR; + + if (!stats_shm_base) { + stats_shm_base = mmap(nullptr, FEX_STATS_SHM_MAX_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); + if (stats_shm_base == MAP_FAILED) { + stats_shm_base = nullptr; + return STATUS_INTERNAL_ERROR; + } + oflag |= O_CREAT | O_TRUNC; + } + + int fd = shm_open(name.c_str(), oflag, S_IRWXU | S_IRWXG | S_IRWXO); + if (fd == -1) { + return STATUS_INTERNAL_ERROR; + } + + if (ftruncate(fd, params->map_size)) { + close(fd); + return STATUS_INTERNAL_ERROR; + } + + if (mmap(stats_shm_base, params->map_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0) == MAP_FAILED) { + close(fd); + return STATUS_INTERNAL_ERROR; + } + + close(fd); + + params->shm_base = stats_shm_base; + return STATUS_SUCCESS; +} + +static NTSTATUS unix_madvise(void* args) { + auto* params = static_cast(args); + madvise(params->addr, params->size, params->advise); + return STATUS_SUCCESS; +} + +static NTSTATUS unix_prctl_set_vma(void* args) { + auto* params = static_cast(args); + params->result = prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast(params->addr), params->size, params->name); + return STATUS_SUCCESS; +} + +extern "C" const unixlib_entry_t __wine_unix_call_funcs[] = { + unix_set_hardware_tso, unix_set_unalign_atomic, unix_get_stats_shm, unix_madvise, unix_prctl_set_vma, +}; + +static_assert(sizeof(__wine_unix_call_funcs) / sizeof(__wine_unix_call_funcs[0]) == fex_unix_func_count); diff --git a/Source/Windows/Unixlib/FEXUnixlib.h b/Source/Windows/Unixlib/FEXUnixlib.h new file mode 100644 index 0000000000..cd47eb19a2 --- /dev/null +++ b/Source/Windows/Unixlib/FEXUnixlib.h @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MIT +#pragma once + +#include +#include + +enum FexUnixFuncs { + fex_unix_set_hardware_tso, + fex_unix_set_unalign_atomic, + fex_unix_get_stats_shm, + fex_unix_madvise, + fex_unix_prctl_set_vma, + fex_unix_func_count, +}; + +struct FexSetHardwareTSOParams { + int enable; +}; + +// 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 FexSetUnalignAtomicParams { + uint64_t flags; +}; + +struct FexStatsSHMParams { + void* shm_base; + uint32_t map_size; + uint32_t max_size; +}; + +struct FexMadviseParams { + void* addr; + size_t size; + int advise; +}; + +struct FexPrctlSetVMAParams { + void* addr; + size_t size; + const char* name; + int64_t result; +}; diff --git a/Source/Windows/WOW64/Module.cpp b/Source/Windows/WOW64/Module.cpp index d5ba1853e8..d7a76cac73 100644 --- a/Source/Windows/WOW64/Module.cpp +++ b/Source/Windows/WOW64/Module.cpp @@ -60,6 +60,8 @@ desc: Implements the WOW64 BT module API using FEXCore #include #include +#include "Unixlib/FEXUnixlib.h" + namespace ControlBits { // When this is unset, a thread can be safely interrupted and have its context recovered // IMPORTANT: This can only safely be written by the owning thread @@ -136,7 +138,7 @@ std::mutex ThreadCreationMutex; // Map of TIDs to their FEX thread state, `ThreadCreationMutex` must be locked when accessing std::unordered_map Threads; -decltype(__wine_unix_call_dispatcher) WineUnixCall; +FEX::Windows::Unixlib::CallDispatcher WineUnixCall; std::pair GetThreadTLS(HANDLE Thread) { THREAD_BASIC_INFORMATION Info; @@ -526,7 +528,8 @@ void BTCpuProcessInit() { const bool IsWine = !!GetProcAddress(NtDll, "wine_get_version"); OvercommitTracker.emplace(IsWine); - FEX::Windows::Allocator::SetupHooks(NtDll); + FEX::Windows::Allocator::SetupHooks(IsWine); + FEX::Windows::Unixlib::Init(NtDll); { auto HostFeatures = FEX::Windows::CPUFeatures::FetchHostFeatures(IsWine); @@ -569,15 +572,6 @@ void BTCpuProcessInit() { // wow64.dll will only initialise the cross-process queue if this is set GetTLS().Wow64Info().CpuFlags = WOW64_CPUFLAGS_SOFTWARE; - FEX_CONFIG_OPT(TSOEnabled, TSOENABLED); - if (TSOEnabled()) { - BOOL Enable = TRUE; - NTSTATUS Status = NtSetInformationProcess(NtCurrentProcess(), ProcessFexHardwareTso, &Enable, sizeof(Enable)); - if (Status == STATUS_SUCCESS) { - CTX->SetHardwareTSOSupport(true); - } - } - FEX_CONFIG_OPT(ProfileStats, PROFILESTATS); FEX_CONFIG_OPT(StartupSleep, STARTUPSLEEP); FEX_CONFIG_OPT(StartupSleepProcName, STARTUPSLEEPPROCNAME); diff --git a/Source/Windows/include/wine/unixlib.h b/Source/Windows/include/wine/unixlib.h index fd77405b6b..57c930e0bf 100644 --- a/Source/Windows/include/wine/unixlib.h +++ b/Source/Windows/include/wine/unixlib.h @@ -3,22 +3,29 @@ #pragma once +#include #include -#ifdef __cplusplus -extern "C" { -#endif - typedef UINT64 unixlib_handle_t; extern NTSTATUS(WINAPI* __wine_set_unix_env)(const char*, const char*); extern NTSTATUS(WINAPI* __wine_unix_call_dispatcher)(unixlib_handle_t, unsigned int, void*); -static inline NTSTATUS __wine_unix_call(unixlib_handle_t handle, unsigned int code, void* args) { - return __wine_unix_call_dispatcher(handle, code, args); -} +namespace FEX::Windows::Unixlib { +using CallDispatcher = NTSTATUS(WINAPI*)(unixlib_handle_t, unsigned int, void*); + +inline unixlib_handle_t* Handle; +inline CallDispatcher Dispatcher; -#ifdef __cplusplus +void Init(HMODULE NtDll); + +inline NTSTATUS Call(unsigned int code, void* args) { + if (!Dispatcher) { + return STATUS_NOT_SUPPORTED; + } + return Dispatcher(*Handle, code, args); } -#endif +} // namespace FEX::Windows::Unixlib + +#define WINE_UNIX_CALL(code, args) FEX::Windows::Unixlib::Call((code), (args)) diff --git a/Source/Windows/include/winternl.h b/Source/Windows/include/winternl.h index 03f3c4497b..91662b1d92 100644 --- a/Source/Windows/include/winternl.h +++ b/Source/Windows/include/winternl.h @@ -457,23 +457,12 @@ typedef enum _MEMORY_INFORMATION_CLASS { MemoryPhysicalContiguityInformation, MemoryBadInformation, MemoryBadInformationAllProcesses, -#ifdef __WINESRC__ MemoryWineUnixFuncs = 1000, MemoryWineUnixWow64Funcs, -#endif - MemoryFexStatsShm = 2000, } MEMORY_INFORMATION_CLASS; #define SystemEmulationBasicInformation (SYSTEM_INFORMATION_CLASS)62 -#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, @@ -490,12 +479,6 @@ typedef struct _KEY_VALUE_PARTIAL_INFORMATION { UCHAR Data[1]; } KEY_VALUE_PARTIAL_INFORMATION, *PKEY_VALUE_PARTIAL_INFORMATION; -typedef struct _MEMORY_FEX_STATS_SHM_INFORMATION { - void* shm_base; - DWORD map_size; - DWORD max_size; -} MEMORY_FEX_STATS_SHM_INFORMATION, *PMEMORY_FEX_STATS_SHM_INFORMATION; - typedef struct _MEMORY_SECTION_NAME { UNICODE_STRING SectionFileName; } MEMORY_SECTION_NAME, *PMEMORY_SECTION_NAME;