diff --git a/README.md b/README.md index 84bb03b64..d3585366c 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 1763. +This is the source code for early-access 1766. ## Legal Notice diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index 4b974f4d2..0f8fe3a3d 100755 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp @@ -6,20 +6,11 @@ #include #include #elif defined(_WIN32) // ^^^ Linux ^^^ vvv Windows vvv -#ifdef _WIN32_WINNT -#undef _WIN32_WINNT -#endif -#define _WIN32_WINNT 0x0A00 // Windows 10 - -#include - -#include - #include #include - -#pragma comment(lib, "mincore.lib") - +#include +#include +#include "common/dynamic_library.h" #endif // ^^^ Windows ^^^ #include @@ -31,43 +22,99 @@ namespace Common { -static constexpr size_t PageAlignment = 0x1000; -static constexpr size_t HugePageSize = 0x200000; +constexpr size_t PageAlignment = 0x1000; +constexpr size_t HugePageSize = 0x200000; #ifdef _WIN32 +// Manually imported for MinGW compatibility +#ifndef MEM_RESERVE_PLACEHOLDER +#define MEM_RESERVE_PLACEHOLDER 0x0004000 +#endif +#ifndef MEM_REPLACE_PLACEHOLDER +#define MEM_REPLACE_PLACEHOLDER 0x00004000 +#endif + +using PFN_CreateFileMapping2 = _Ret_maybenull_ HANDLE(WINAPI*)( + _In_ HANDLE File, _In_opt_ SECURITY_ATTRIBUTES* SecurityAttributes, _In_ ULONG DesiredAccess, + _In_ ULONG PageProtection, _In_ ULONG AllocationAttributes, _In_ ULONG64 MaximumSize, + _In_opt_ PCWSTR Name, + _Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters, + _In_ ULONG ParameterCount); + +using PFN_VirtualAlloc2 = _Ret_maybenull_ PVOID(WINAPI*)( + _In_opt_ HANDLE Process, _In_opt_ PVOID BaseAddress, _In_ SIZE_T Size, + _In_ ULONG AllocationType, _In_ ULONG PageProtection, + _Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters, + _In_ ULONG ParameterCount); + +using PFN_MapViewOfFile3 = _Ret_maybenull_ __out_data_source(FILE) + PVOID(WINAPI*)(_In_ HANDLE FileMapping, _In_opt_ HANDLE Process, _In_opt_ PVOID BaseAddress, + _In_ ULONG64 Offset, _In_ SIZE_T ViewSize, _In_ ULONG AllocationType, + _In_ ULONG PageProtection, + _Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters, + _In_ ULONG ParameterCount); + +using PFN_UnmapViewOfFile2 = BOOL(WINAPI*)(_In_ HANDLE Process, _In_ PVOID BaseAddress, + _In_ ULONG UnmapFlags); + +template +static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn) { + if (!dll.GetSymbol(name, &pfn)) { + LOG_CRITICAL(HW_Memory, "Failed to load {}", name); + throw std::bad_alloc{}; + } +} + class HostMemory::Impl { public: explicit Impl(size_t backing_size_, size_t virtual_size_) - : backing_size{backing_size_}, virtual_size{virtual_size_}, process{GetCurrentProcess()} { + : backing_size{backing_size_}, virtual_size{virtual_size_}, process{GetCurrentProcess()}, + kernelbase_dll("Kernelbase") { + if (!kernelbase_dll.IsOpen()) { + LOG_CRITICAL(HW_Memory, "Failed to load Kernelbase.dll"); + throw std::bad_alloc{}; + } + GetFuncAddress(kernelbase_dll, "CreateFileMapping2", pfn_CreateFileMapping2); + GetFuncAddress(kernelbase_dll, "VirtualAlloc2", pfn_VirtualAlloc2); + GetFuncAddress(kernelbase_dll, "MapViewOfFile3", pfn_MapViewOfFile3); + GetFuncAddress(kernelbase_dll, "UnmapViewOfFile2", pfn_UnmapViewOfFile2); + // Allocate backing file map backing_handle = - CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr, FILE_MAP_WRITE | FILE_MAP_READ, - PAGE_READWRITE, SEC_COMMIT, backing_size, nullptr, nullptr, 0); + pfn_CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr, FILE_MAP_WRITE | FILE_MAP_READ, + PAGE_READWRITE, SEC_COMMIT, backing_size, nullptr, nullptr, 0); if (!backing_handle) { + LOG_CRITICAL(HW_Memory, "Failed to allocate {} MiB of backing memory", + backing_size >> 20); throw std::bad_alloc{}; } // Allocate a virtual memory for the backing file map as placeholder - backing_base = static_cast(VirtualAlloc2(process, nullptr, backing_size, - MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, - PAGE_NOACCESS, nullptr, 0)); + backing_base = static_cast(pfn_VirtualAlloc2(process, nullptr, backing_size, + MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, + PAGE_NOACCESS, nullptr, 0)); if (!backing_base) { Release(); + LOG_CRITICAL(HW_Memory, "Failed to reserve {} MiB of virtual memory", + backing_size >> 20); throw std::bad_alloc{}; } // Map backing placeholder - void* const ret = MapViewOfFile3(backing_handle, process, backing_base, 0, backing_size, - MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0); + void* const ret = pfn_MapViewOfFile3(backing_handle, process, backing_base, 0, backing_size, + MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0); if (ret != backing_base) { Release(); + LOG_CRITICAL(HW_Memory, "Failed to map {} MiB of virtual memory", backing_size >> 20); throw std::bad_alloc{}; } // Allocate virtual address placeholder - virtual_base = static_cast(VirtualAlloc2(process, nullptr, virtual_size, - MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, - PAGE_NOACCESS, nullptr, 0)); + virtual_base = static_cast(pfn_VirtualAlloc2(process, nullptr, virtual_size, + MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, + PAGE_NOACCESS, nullptr, 0)); if (!virtual_base) { Release(); + LOG_CRITICAL(HW_Memory, "Failed to reserve {} GiB of virtual memory", + virtual_size >> 30); throw std::bad_alloc{}; } } @@ -132,8 +179,8 @@ private: void Release() { if (!placeholders.empty()) { for (const auto& placeholder : placeholders) { - if (!UnmapViewOfFile2(process, virtual_base + placeholder.lower(), - MEM_PRESERVE_PLACEHOLDER)) { + if (!pfn_UnmapViewOfFile2(process, virtual_base + placeholder.lower(), + MEM_PRESERVE_PLACEHOLDER)) { LOG_CRITICAL(HW_Memory, "Failed to unmap virtual memory placeholder"); } } @@ -145,7 +192,7 @@ private: } } if (backing_base) { - if (!UnmapViewOfFile2(process, backing_base, MEM_PRESERVE_PLACEHOLDER)) { + if (!pfn_UnmapViewOfFile2(process, backing_base, MEM_PRESERVE_PLACEHOLDER)) { LOG_CRITICAL(HW_Memory, "Failed to unmap backing memory placeholder"); } if (!VirtualFreeEx(process, backing_base, 0, MEM_RELEASE)) { @@ -180,8 +227,8 @@ private: const bool split_left = unmap_begin > placeholder_begin; const bool split_right = unmap_end < placeholder_end; - if (!UnmapViewOfFile2(process, virtual_base + placeholder_begin, - MEM_PRESERVE_PLACEHOLDER)) { + if (!pfn_UnmapViewOfFile2(process, virtual_base + placeholder_begin, + MEM_PRESERVE_PLACEHOLDER)) { LOG_CRITICAL(HW_Memory, "Failed to unmap placeholder"); } // If we have to remap memory regions due to partial unmaps, we are in a data race as @@ -231,8 +278,8 @@ private: } void MapView(size_t virtual_offset, size_t host_offset, size_t length) { - if (!MapViewOfFile3(backing_handle, process, virtual_base + virtual_offset, host_offset, - length, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0)) { + if (!pfn_MapViewOfFile3(backing_handle, process, virtual_base + virtual_offset, host_offset, + length, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0)) { LOG_CRITICAL(HW_Memory, "Failed to map placeholder"); } } @@ -275,6 +322,12 @@ private: HANDLE process{}; ///< Current process handle HANDLE backing_handle{}; ///< File based backing memory + DynamicLibrary kernelbase_dll; + PFN_CreateFileMapping2 pfn_CreateFileMapping2{}; + PFN_VirtualAlloc2 pfn_VirtualAlloc2{}; + PFN_MapViewOfFile3 pfn_MapViewOfFile3{}; + PFN_UnmapViewOfFile2 pfn_UnmapViewOfFile2{}; + std::mutex placeholder_mutex; ///< Mutex for placeholders boost::icl::separate_interval_set placeholders; ///< Mapped placeholders std::unordered_map placeholder_host_pointers; ///< Placeholder backing offset diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index dd62706a8..5c3c13ce6 100755 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp @@ -8,6 +8,7 @@ #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "common/scope_exit.h" #include "core/core_timing.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/hle_ipc.h" @@ -119,11 +120,20 @@ ResultCode KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memor context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); + // In the event that something fails here, stub a result to prevent the game from crashing. + // This is a work-around in the event that somehow we process a service request after the + // session has been closed by the game. This has been observed to happen rarely in Pokemon + // Sword/Shield and is likely a result of us using host threads/scheduling for services. + // TODO(bunnei): Find a better solution here. + auto error_guard = SCOPE_GUARD({ CompleteSyncRequest(*context); }); + // Ensure we have a session request handler if (manager->HasSessionRequestHandler(*context)) { if (auto strong_ptr = manager->GetServiceThread().lock()) { strong_ptr->QueueSyncRequest(*parent, std::move(context)); - return ResultSuccess; + + // We succeeded. + error_guard.Cancel(); } else { ASSERT_MSG(false, "strong_ptr is nullptr!"); } @@ -136,13 +146,20 @@ ResultCode KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memor ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) { ResultCode result = ResultSuccess; + // If the session has been converted to a domain, handle the domain request - if (IsDomain() && context.HasDomainMessageHeader()) { - result = HandleDomainSyncRequest(context); - // If there is no domain header, the regular session handler is used - } else if (manager->HasSessionHandler()) { - // If this ServerSession has an associated HLE handler, forward the request to it. - result = manager->SessionHandler().HandleSyncRequest(*this, context); + if (manager->HasSessionRequestHandler(context)) { + if (IsDomain() && context.HasDomainMessageHeader()) { + result = HandleDomainSyncRequest(context); + // If there is no domain header, the regular session handler is used + } else if (manager->HasSessionHandler()) { + // If this ServerSession has an associated HLE handler, forward the request to it. + result = manager->SessionHandler().HandleSyncRequest(*this, context); + } + } else { + ASSERT_MSG(false, "Session handler is invalid, stubbing response!"); + IPC::ResponseBuilder rb(context, 2); + rb.Push(ResultSuccess); } if (convert_to_domain) {