diff options
Diffstat (limited to 'src/common/host_memory.cpp')
| -rw-r--r-- | src/common/host_memory.cpp | 117 |
1 files changed, 88 insertions, 29 deletions
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index c6d65aab9..9ae384f01 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp | |||
| @@ -1,18 +1,10 @@ | |||
| 1 | #ifdef _WIN32 | 1 | #ifdef _WIN32 |
| 2 | 2 | ||
| 3 | #ifdef _WIN32_WINNT | ||
| 4 | #undef _WIN32_WINNT | ||
| 5 | #endif | ||
| 6 | #define _WIN32_WINNT 0x0A00 // Windows 10 | ||
| 7 | |||
| 8 | #include <windows.h> | ||
| 9 | |||
| 10 | #include <boost/icl/separate_interval_set.hpp> | ||
| 11 | |||
| 12 | #include <iterator> | 3 | #include <iterator> |
| 13 | #include <unordered_map> | 4 | #include <unordered_map> |
| 14 | 5 | #include <boost/icl/separate_interval_set.hpp> | |
| 15 | #pragma comment(lib, "mincore.lib") | 6 | #include <windows.h> |
| 7 | #include "common/dynamic_library.h" | ||
| 16 | 8 | ||
| 17 | #elif defined(__linux__) // ^^^ Windows ^^^ vvv Linux vvv | 9 | #elif defined(__linux__) // ^^^ Windows ^^^ vvv Linux vvv |
| 18 | 10 | ||
| @@ -40,38 +32,99 @@ constexpr size_t HugePageSize = 0x200000; | |||
| 40 | 32 | ||
| 41 | #ifdef _WIN32 | 33 | #ifdef _WIN32 |
| 42 | 34 | ||
| 35 | // Manually imported for MinGW compatibility | ||
| 36 | #ifndef MEM_RESERVE_PLACEHOLDER | ||
| 37 | #define MEM_RESERVE_PLACEHOLDER 0x0004000 | ||
| 38 | #endif | ||
| 39 | #ifndef MEM_REPLACE_PLACEHOLDER | ||
| 40 | #define MEM_REPLACE_PLACEHOLDER 0x00004000 | ||
| 41 | #endif | ||
| 42 | #ifndef MEM_COALESCE_PLACEHOLDERS | ||
| 43 | #define MEM_COALESCE_PLACEHOLDERS 0x00000001 | ||
| 44 | #endif | ||
| 45 | #ifndef MEM_PRESERVE_PLACEHOLDER | ||
| 46 | #define MEM_PRESERVE_PLACEHOLDER 0x00000002 | ||
| 47 | #endif | ||
| 48 | |||
| 49 | using PFN_CreateFileMapping2 = _Ret_maybenull_ HANDLE(WINAPI*)( | ||
| 50 | _In_ HANDLE File, _In_opt_ SECURITY_ATTRIBUTES* SecurityAttributes, _In_ ULONG DesiredAccess, | ||
| 51 | _In_ ULONG PageProtection, _In_ ULONG AllocationAttributes, _In_ ULONG64 MaximumSize, | ||
| 52 | _In_opt_ PCWSTR Name, | ||
| 53 | _Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters, | ||
| 54 | _In_ ULONG ParameterCount); | ||
| 55 | |||
| 56 | using PFN_VirtualAlloc2 = _Ret_maybenull_ PVOID(WINAPI*)( | ||
| 57 | _In_opt_ HANDLE Process, _In_opt_ PVOID BaseAddress, _In_ SIZE_T Size, | ||
| 58 | _In_ ULONG AllocationType, _In_ ULONG PageProtection, | ||
| 59 | _Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters, | ||
| 60 | _In_ ULONG ParameterCount); | ||
| 61 | |||
| 62 | using PFN_MapViewOfFile3 = _Ret_maybenull_ PVOID(WINAPI*)( | ||
| 63 | _In_ HANDLE FileMapping, _In_opt_ HANDLE Process, _In_opt_ PVOID BaseAddress, | ||
| 64 | _In_ ULONG64 Offset, _In_ SIZE_T ViewSize, _In_ ULONG AllocationType, _In_ ULONG PageProtection, | ||
| 65 | _Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters, | ||
| 66 | _In_ ULONG ParameterCount); | ||
| 67 | |||
| 68 | using PFN_UnmapViewOfFile2 = BOOL(WINAPI*)(_In_ HANDLE Process, _In_ PVOID BaseAddress, | ||
| 69 | _In_ ULONG UnmapFlags); | ||
| 70 | |||
| 71 | template <typename T> | ||
| 72 | static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn) { | ||
| 73 | if (!dll.GetSymbol(name, &pfn)) { | ||
| 74 | LOG_CRITICAL(HW_Memory, "Failed to load {}", name); | ||
| 75 | throw std::bad_alloc{}; | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 43 | class HostMemory::Impl { | 79 | class HostMemory::Impl { |
| 44 | public: | 80 | public: |
| 45 | explicit Impl(size_t backing_size_, size_t virtual_size_) | 81 | explicit Impl(size_t backing_size_, size_t virtual_size_) |
| 46 | : backing_size{backing_size_}, virtual_size{virtual_size_}, process{GetCurrentProcess()} { | 82 | : backing_size{backing_size_}, virtual_size{virtual_size_}, process{GetCurrentProcess()}, |
| 83 | kernelbase_dll("Kernelbase") { | ||
| 84 | if (!kernelbase_dll.IsOpen()) { | ||
| 85 | LOG_CRITICAL(HW_Memory, "Failed to load Kernelbase.dll"); | ||
| 86 | throw std::bad_alloc{}; | ||
| 87 | } | ||
| 88 | GetFuncAddress(kernelbase_dll, "CreateFileMapping2", pfn_CreateFileMapping2); | ||
| 89 | GetFuncAddress(kernelbase_dll, "VirtualAlloc2", pfn_VirtualAlloc2); | ||
| 90 | GetFuncAddress(kernelbase_dll, "MapViewOfFile3", pfn_MapViewOfFile3); | ||
| 91 | GetFuncAddress(kernelbase_dll, "UnmapViewOfFile2", pfn_UnmapViewOfFile2); | ||
| 92 | |||
| 47 | // Allocate backing file map | 93 | // Allocate backing file map |
| 48 | backing_handle = | 94 | backing_handle = |
| 49 | CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr, FILE_MAP_WRITE | FILE_MAP_READ, | 95 | pfn_CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr, FILE_MAP_WRITE | FILE_MAP_READ, |
| 50 | PAGE_READWRITE, SEC_COMMIT, backing_size, nullptr, nullptr, 0); | 96 | PAGE_READWRITE, SEC_COMMIT, backing_size, nullptr, nullptr, 0); |
| 51 | if (!backing_handle) { | 97 | if (!backing_handle) { |
| 98 | LOG_CRITICAL(HW_Memory, "Failed to allocate {} MiB of backing memory", | ||
| 99 | backing_size >> 20); | ||
| 52 | throw std::bad_alloc{}; | 100 | throw std::bad_alloc{}; |
| 53 | } | 101 | } |
| 54 | // Allocate a virtual memory for the backing file map as placeholder | 102 | // Allocate a virtual memory for the backing file map as placeholder |
| 55 | backing_base = static_cast<u8*>(VirtualAlloc2(process, nullptr, backing_size, | 103 | backing_base = static_cast<u8*>(pfn_VirtualAlloc2(process, nullptr, backing_size, |
| 56 | MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, | 104 | MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, |
| 57 | PAGE_NOACCESS, nullptr, 0)); | 105 | PAGE_NOACCESS, nullptr, 0)); |
| 58 | if (!backing_base) { | 106 | if (!backing_base) { |
| 59 | Release(); | 107 | Release(); |
| 108 | LOG_CRITICAL(HW_Memory, "Failed to reserve {} MiB of virtual memory", | ||
| 109 | backing_size >> 20); | ||
| 60 | throw std::bad_alloc{}; | 110 | throw std::bad_alloc{}; |
| 61 | } | 111 | } |
| 62 | // Map backing placeholder | 112 | // Map backing placeholder |
| 63 | void* const ret = MapViewOfFile3(backing_handle, process, backing_base, 0, backing_size, | 113 | void* const ret = pfn_MapViewOfFile3(backing_handle, process, backing_base, 0, backing_size, |
| 64 | MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0); | 114 | MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0); |
| 65 | if (ret != backing_base) { | 115 | if (ret != backing_base) { |
| 66 | Release(); | 116 | Release(); |
| 117 | LOG_CRITICAL(HW_Memory, "Failed to map {} MiB of virtual memory", backing_size >> 20); | ||
| 67 | throw std::bad_alloc{}; | 118 | throw std::bad_alloc{}; |
| 68 | } | 119 | } |
| 69 | // Allocate virtual address placeholder | 120 | // Allocate virtual address placeholder |
| 70 | virtual_base = static_cast<u8*>(VirtualAlloc2(process, nullptr, virtual_size, | 121 | virtual_base = static_cast<u8*>(pfn_VirtualAlloc2(process, nullptr, virtual_size, |
| 71 | MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, | 122 | MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, |
| 72 | PAGE_NOACCESS, nullptr, 0)); | 123 | PAGE_NOACCESS, nullptr, 0)); |
| 73 | if (!virtual_base) { | 124 | if (!virtual_base) { |
| 74 | Release(); | 125 | Release(); |
| 126 | LOG_CRITICAL(HW_Memory, "Failed to reserve {} GiB of virtual memory", | ||
| 127 | virtual_size >> 30); | ||
| 75 | throw std::bad_alloc{}; | 128 | throw std::bad_alloc{}; |
| 76 | } | 129 | } |
| 77 | } | 130 | } |
| @@ -136,8 +189,8 @@ private: | |||
| 136 | void Release() { | 189 | void Release() { |
| 137 | if (!placeholders.empty()) { | 190 | if (!placeholders.empty()) { |
| 138 | for (const auto& placeholder : placeholders) { | 191 | for (const auto& placeholder : placeholders) { |
| 139 | if (!UnmapViewOfFile2(process, virtual_base + placeholder.lower(), | 192 | if (!pfn_UnmapViewOfFile2(process, virtual_base + placeholder.lower(), |
| 140 | MEM_PRESERVE_PLACEHOLDER)) { | 193 | MEM_PRESERVE_PLACEHOLDER)) { |
| 141 | LOG_CRITICAL(HW_Memory, "Failed to unmap virtual memory placeholder"); | 194 | LOG_CRITICAL(HW_Memory, "Failed to unmap virtual memory placeholder"); |
| 142 | } | 195 | } |
| 143 | } | 196 | } |
| @@ -149,7 +202,7 @@ private: | |||
| 149 | } | 202 | } |
| 150 | } | 203 | } |
| 151 | if (backing_base) { | 204 | if (backing_base) { |
| 152 | if (!UnmapViewOfFile2(process, backing_base, MEM_PRESERVE_PLACEHOLDER)) { | 205 | if (!pfn_UnmapViewOfFile2(process, backing_base, MEM_PRESERVE_PLACEHOLDER)) { |
| 153 | LOG_CRITICAL(HW_Memory, "Failed to unmap backing memory placeholder"); | 206 | LOG_CRITICAL(HW_Memory, "Failed to unmap backing memory placeholder"); |
| 154 | } | 207 | } |
| 155 | if (!VirtualFreeEx(process, backing_base, 0, MEM_RELEASE)) { | 208 | if (!VirtualFreeEx(process, backing_base, 0, MEM_RELEASE)) { |
| @@ -184,8 +237,8 @@ private: | |||
| 184 | const bool split_left = unmap_begin > placeholder_begin; | 237 | const bool split_left = unmap_begin > placeholder_begin; |
| 185 | const bool split_right = unmap_end < placeholder_end; | 238 | const bool split_right = unmap_end < placeholder_end; |
| 186 | 239 | ||
| 187 | if (!UnmapViewOfFile2(process, virtual_base + placeholder_begin, | 240 | if (!pfn_UnmapViewOfFile2(process, virtual_base + placeholder_begin, |
| 188 | MEM_PRESERVE_PLACEHOLDER)) { | 241 | MEM_PRESERVE_PLACEHOLDER)) { |
| 189 | LOG_CRITICAL(HW_Memory, "Failed to unmap placeholder"); | 242 | LOG_CRITICAL(HW_Memory, "Failed to unmap placeholder"); |
| 190 | } | 243 | } |
| 191 | // If we have to remap memory regions due to partial unmaps, we are in a data race as | 244 | // If we have to remap memory regions due to partial unmaps, we are in a data race as |
| @@ -235,8 +288,8 @@ private: | |||
| 235 | } | 288 | } |
| 236 | 289 | ||
| 237 | void MapView(size_t virtual_offset, size_t host_offset, size_t length) { | 290 | void MapView(size_t virtual_offset, size_t host_offset, size_t length) { |
| 238 | if (!MapViewOfFile3(backing_handle, process, virtual_base + virtual_offset, host_offset, | 291 | if (!pfn_MapViewOfFile3(backing_handle, process, virtual_base + virtual_offset, host_offset, |
| 239 | length, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0)) { | 292 | length, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0)) { |
| 240 | LOG_CRITICAL(HW_Memory, "Failed to map placeholder"); | 293 | LOG_CRITICAL(HW_Memory, "Failed to map placeholder"); |
| 241 | } | 294 | } |
| 242 | } | 295 | } |
| @@ -279,6 +332,12 @@ private: | |||
| 279 | HANDLE process{}; ///< Current process handle | 332 | HANDLE process{}; ///< Current process handle |
| 280 | HANDLE backing_handle{}; ///< File based backing memory | 333 | HANDLE backing_handle{}; ///< File based backing memory |
| 281 | 334 | ||
| 335 | DynamicLibrary kernelbase_dll; | ||
| 336 | PFN_CreateFileMapping2 pfn_CreateFileMapping2{}; | ||
| 337 | PFN_VirtualAlloc2 pfn_VirtualAlloc2{}; | ||
| 338 | PFN_MapViewOfFile3 pfn_MapViewOfFile3{}; | ||
| 339 | PFN_UnmapViewOfFile2 pfn_UnmapViewOfFile2{}; | ||
| 340 | |||
| 282 | std::mutex placeholder_mutex; ///< Mutex for placeholders | 341 | std::mutex placeholder_mutex; ///< Mutex for placeholders |
| 283 | boost::icl::separate_interval_set<size_t> placeholders; ///< Mapped placeholders | 342 | boost::icl::separate_interval_set<size_t> placeholders; ///< Mapped placeholders |
| 284 | std::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset | 343 | std::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset |