host_memory.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728
  1. // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #ifdef _WIN32
  4. #include <iterator>
  5. #include <unordered_map>
  6. #include <boost/icl/separate_interval_set.hpp>
  7. #include <windows.h>
  8. #include "common/dynamic_library.h"
  9. #elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv
  10. #ifndef _GNU_SOURCE
  11. #define _GNU_SOURCE
  12. #endif
  13. #include <boost/icl/interval_set.hpp>
  14. #include <fcntl.h>
  15. #include <sys/mman.h>
  16. #include <sys/random.h>
  17. #include <unistd.h>
  18. #include "common/scope_exit.h"
  19. #ifndef MAP_NORESERVE
  20. #define MAP_NORESERVE 0
  21. #endif
  22. #endif // ^^^ Linux ^^^
  23. #include <mutex>
  24. #include <random>
  25. #include "common/alignment.h"
  26. #include "common/assert.h"
  27. #include "common/free_region_manager.h"
  28. #include "common/host_memory.h"
  29. #include "common/logging/log.h"
  30. namespace Common {
  31. constexpr size_t PageAlignment = 0x1000;
  32. constexpr size_t HugePageSize = 0x200000;
  33. #ifdef _WIN32
  34. // Manually imported for MinGW compatibility
  35. #ifndef MEM_RESERVE_PLACEHOLDER
  36. #define MEM_RESERVE_PLACEHOLDER 0x00040000
  37. #endif
  38. #ifndef MEM_REPLACE_PLACEHOLDER
  39. #define MEM_REPLACE_PLACEHOLDER 0x00004000
  40. #endif
  41. #ifndef MEM_COALESCE_PLACEHOLDERS
  42. #define MEM_COALESCE_PLACEHOLDERS 0x00000001
  43. #endif
  44. #ifndef MEM_PRESERVE_PLACEHOLDER
  45. #define MEM_PRESERVE_PLACEHOLDER 0x00000002
  46. #endif
  47. using PFN_CreateFileMapping2 = _Ret_maybenull_ HANDLE(WINAPI*)(
  48. _In_ HANDLE File, _In_opt_ SECURITY_ATTRIBUTES* SecurityAttributes, _In_ ULONG DesiredAccess,
  49. _In_ ULONG PageProtection, _In_ ULONG AllocationAttributes, _In_ ULONG64 MaximumSize,
  50. _In_opt_ PCWSTR Name,
  51. _Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters,
  52. _In_ ULONG ParameterCount);
  53. using PFN_VirtualAlloc2 = _Ret_maybenull_ PVOID(WINAPI*)(
  54. _In_opt_ HANDLE Process, _In_opt_ PVOID BaseAddress, _In_ SIZE_T Size,
  55. _In_ ULONG AllocationType, _In_ ULONG PageProtection,
  56. _Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters,
  57. _In_ ULONG ParameterCount);
  58. using PFN_MapViewOfFile3 = _Ret_maybenull_ PVOID(WINAPI*)(
  59. _In_ HANDLE FileMapping, _In_opt_ HANDLE Process, _In_opt_ PVOID BaseAddress,
  60. _In_ ULONG64 Offset, _In_ SIZE_T ViewSize, _In_ ULONG AllocationType, _In_ ULONG PageProtection,
  61. _Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters,
  62. _In_ ULONG ParameterCount);
  63. using PFN_UnmapViewOfFile2 = BOOL(WINAPI*)(_In_ HANDLE Process, _In_ PVOID BaseAddress,
  64. _In_ ULONG UnmapFlags);
  65. template <typename T>
  66. static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn) {
  67. if (!dll.GetSymbol(name, &pfn)) {
  68. LOG_CRITICAL(HW_Memory, "Failed to load {}", name);
  69. throw std::bad_alloc{};
  70. }
  71. }
  72. class HostMemory::Impl {
  73. public:
  74. explicit Impl(size_t backing_size_, size_t virtual_size_)
  75. : backing_size{backing_size_}, virtual_size{virtual_size_}, process{GetCurrentProcess()},
  76. kernelbase_dll("Kernelbase") {
  77. if (!kernelbase_dll.IsOpen()) {
  78. LOG_CRITICAL(HW_Memory, "Failed to load Kernelbase.dll");
  79. throw std::bad_alloc{};
  80. }
  81. GetFuncAddress(kernelbase_dll, "CreateFileMapping2", pfn_CreateFileMapping2);
  82. GetFuncAddress(kernelbase_dll, "VirtualAlloc2", pfn_VirtualAlloc2);
  83. GetFuncAddress(kernelbase_dll, "MapViewOfFile3", pfn_MapViewOfFile3);
  84. GetFuncAddress(kernelbase_dll, "UnmapViewOfFile2", pfn_UnmapViewOfFile2);
  85. // Allocate backing file map
  86. backing_handle =
  87. pfn_CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr, FILE_MAP_WRITE | FILE_MAP_READ,
  88. PAGE_READWRITE, SEC_COMMIT, backing_size, nullptr, nullptr, 0);
  89. if (!backing_handle) {
  90. LOG_CRITICAL(HW_Memory, "Failed to allocate {} MiB of backing memory",
  91. backing_size >> 20);
  92. throw std::bad_alloc{};
  93. }
  94. // Allocate a virtual memory for the backing file map as placeholder
  95. backing_base = static_cast<u8*>(pfn_VirtualAlloc2(process, nullptr, backing_size,
  96. MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
  97. PAGE_NOACCESS, nullptr, 0));
  98. if (!backing_base) {
  99. Release();
  100. LOG_CRITICAL(HW_Memory, "Failed to reserve {} MiB of virtual memory",
  101. backing_size >> 20);
  102. throw std::bad_alloc{};
  103. }
  104. // Map backing placeholder
  105. void* const ret = pfn_MapViewOfFile3(backing_handle, process, backing_base, 0, backing_size,
  106. MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0);
  107. if (ret != backing_base) {
  108. Release();
  109. LOG_CRITICAL(HW_Memory, "Failed to map {} MiB of virtual memory", backing_size >> 20);
  110. throw std::bad_alloc{};
  111. }
  112. // Allocate virtual address placeholder
  113. virtual_base = static_cast<u8*>(pfn_VirtualAlloc2(process, nullptr, virtual_size,
  114. MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
  115. PAGE_NOACCESS, nullptr, 0));
  116. if (!virtual_base) {
  117. Release();
  118. LOG_CRITICAL(HW_Memory, "Failed to reserve {} GiB of virtual memory",
  119. virtual_size >> 30);
  120. throw std::bad_alloc{};
  121. }
  122. }
  123. ~Impl() {
  124. Release();
  125. }
  126. void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
  127. std::unique_lock lock{placeholder_mutex};
  128. if (!IsNiechePlaceholder(virtual_offset, length)) {
  129. Split(virtual_offset, length);
  130. }
  131. ASSERT(placeholders.find({virtual_offset, virtual_offset + length}) == placeholders.end());
  132. TrackPlaceholder(virtual_offset, host_offset, length);
  133. MapView(virtual_offset, host_offset, length);
  134. }
  135. void Unmap(size_t virtual_offset, size_t length) {
  136. std::scoped_lock lock{placeholder_mutex};
  137. // Unmap until there are no more placeholders
  138. while (UnmapOnePlaceholder(virtual_offset, length)) {
  139. }
  140. }
  141. void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {
  142. DWORD new_flags{};
  143. if (read && write) {
  144. new_flags = PAGE_READWRITE;
  145. } else if (read && !write) {
  146. new_flags = PAGE_READONLY;
  147. } else if (!read && !write) {
  148. new_flags = PAGE_NOACCESS;
  149. } else {
  150. UNIMPLEMENTED_MSG("Protection flag combination read={} write={}", read, write);
  151. }
  152. const size_t virtual_end = virtual_offset + length;
  153. std::scoped_lock lock{placeholder_mutex};
  154. auto [it, end] = placeholders.equal_range({virtual_offset, virtual_end});
  155. while (it != end) {
  156. const size_t offset = std::max(it->lower(), virtual_offset);
  157. const size_t protect_length = std::min(it->upper(), virtual_end) - offset;
  158. DWORD old_flags{};
  159. if (!VirtualProtect(virtual_base + offset, protect_length, new_flags, &old_flags)) {
  160. LOG_CRITICAL(HW_Memory, "Failed to change virtual memory protect rules");
  161. }
  162. ++it;
  163. }
  164. }
  165. bool ClearBackingRegion(size_t physical_offset, size_t length) {
  166. // TODO: This does not seem to be possible on Windows.
  167. return false;
  168. }
  169. void EnableDirectMappedAddress() {
  170. // TODO
  171. UNREACHABLE();
  172. }
  173. const size_t backing_size; ///< Size of the backing memory in bytes
  174. const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
  175. u8* backing_base{};
  176. u8* virtual_base{};
  177. private:
  178. /// Release all resources in the object
  179. void Release() {
  180. if (!placeholders.empty()) {
  181. for (const auto& placeholder : placeholders) {
  182. if (!pfn_UnmapViewOfFile2(process, virtual_base + placeholder.lower(),
  183. MEM_PRESERVE_PLACEHOLDER)) {
  184. LOG_CRITICAL(HW_Memory, "Failed to unmap virtual memory placeholder");
  185. }
  186. }
  187. Coalesce(0, virtual_size);
  188. }
  189. if (virtual_base) {
  190. if (!VirtualFree(virtual_base, 0, MEM_RELEASE)) {
  191. LOG_CRITICAL(HW_Memory, "Failed to free virtual memory");
  192. }
  193. }
  194. if (backing_base) {
  195. if (!pfn_UnmapViewOfFile2(process, backing_base, MEM_PRESERVE_PLACEHOLDER)) {
  196. LOG_CRITICAL(HW_Memory, "Failed to unmap backing memory placeholder");
  197. }
  198. if (!VirtualFreeEx(process, backing_base, 0, MEM_RELEASE)) {
  199. LOG_CRITICAL(HW_Memory, "Failed to free backing memory");
  200. }
  201. }
  202. if (!CloseHandle(backing_handle)) {
  203. LOG_CRITICAL(HW_Memory, "Failed to free backing memory file handle");
  204. }
  205. }
  206. /// Unmap one placeholder in the given range (partial unmaps are supported)
  207. /// Return true when there are no more placeholders to unmap
  208. bool UnmapOnePlaceholder(size_t virtual_offset, size_t length) {
  209. const auto it = placeholders.find({virtual_offset, virtual_offset + length});
  210. const auto begin = placeholders.begin();
  211. const auto end = placeholders.end();
  212. if (it == end) {
  213. return false;
  214. }
  215. const size_t placeholder_begin = it->lower();
  216. const size_t placeholder_end = it->upper();
  217. const size_t unmap_begin = std::max(virtual_offset, placeholder_begin);
  218. const size_t unmap_end = std::min(virtual_offset + length, placeholder_end);
  219. ASSERT(unmap_begin >= placeholder_begin && unmap_begin < placeholder_end);
  220. ASSERT(unmap_end <= placeholder_end && unmap_end > placeholder_begin);
  221. const auto host_pointer_it = placeholder_host_pointers.find(placeholder_begin);
  222. ASSERT(host_pointer_it != placeholder_host_pointers.end());
  223. const size_t host_offset = host_pointer_it->second;
  224. const bool split_left = unmap_begin > placeholder_begin;
  225. const bool split_right = unmap_end < placeholder_end;
  226. if (!pfn_UnmapViewOfFile2(process, virtual_base + placeholder_begin,
  227. MEM_PRESERVE_PLACEHOLDER)) {
  228. LOG_CRITICAL(HW_Memory, "Failed to unmap placeholder");
  229. }
  230. // If we have to remap memory regions due to partial unmaps, we are in a data race as
  231. // Windows doesn't support remapping memory without unmapping first. Avoid adding any extra
  232. // logic within the panic region described below.
  233. // Panic region, we are in a data race right now
  234. if (split_left || split_right) {
  235. Split(unmap_begin, unmap_end - unmap_begin);
  236. }
  237. if (split_left) {
  238. MapView(placeholder_begin, host_offset, unmap_begin - placeholder_begin);
  239. }
  240. if (split_right) {
  241. MapView(unmap_end, host_offset + unmap_end - placeholder_begin,
  242. placeholder_end - unmap_end);
  243. }
  244. // End panic region
  245. size_t coalesce_begin = unmap_begin;
  246. if (!split_left) {
  247. // Try to coalesce pages to the left
  248. coalesce_begin = it == begin ? 0 : std::prev(it)->upper();
  249. if (coalesce_begin != placeholder_begin) {
  250. Coalesce(coalesce_begin, unmap_end - coalesce_begin);
  251. }
  252. }
  253. if (!split_right) {
  254. // Try to coalesce pages to the right
  255. const auto next = std::next(it);
  256. const size_t next_begin = next == end ? virtual_size : next->lower();
  257. if (placeholder_end != next_begin) {
  258. // We can coalesce to the right
  259. Coalesce(coalesce_begin, next_begin - coalesce_begin);
  260. }
  261. }
  262. // Remove and reinsert placeholder trackers
  263. UntrackPlaceholder(it);
  264. if (split_left) {
  265. TrackPlaceholder(placeholder_begin, host_offset, unmap_begin - placeholder_begin);
  266. }
  267. if (split_right) {
  268. TrackPlaceholder(unmap_end, host_offset + unmap_end - placeholder_begin,
  269. placeholder_end - unmap_end);
  270. }
  271. return true;
  272. }
  273. void MapView(size_t virtual_offset, size_t host_offset, size_t length) {
  274. if (!pfn_MapViewOfFile3(backing_handle, process, virtual_base + virtual_offset, host_offset,
  275. length, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0)) {
  276. LOG_CRITICAL(HW_Memory, "Failed to map placeholder");
  277. }
  278. }
  279. void Split(size_t virtual_offset, size_t length) {
  280. if (!VirtualFreeEx(process, reinterpret_cast<LPVOID>(virtual_base + virtual_offset), length,
  281. MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER)) {
  282. LOG_CRITICAL(HW_Memory, "Failed to split placeholder");
  283. }
  284. }
  285. void Coalesce(size_t virtual_offset, size_t length) {
  286. if (!VirtualFreeEx(process, reinterpret_cast<LPVOID>(virtual_base + virtual_offset), length,
  287. MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS)) {
  288. LOG_CRITICAL(HW_Memory, "Failed to coalesce placeholders");
  289. }
  290. }
  291. void TrackPlaceholder(size_t virtual_offset, size_t host_offset, size_t length) {
  292. placeholders.insert({virtual_offset, virtual_offset + length});
  293. placeholder_host_pointers.emplace(virtual_offset, host_offset);
  294. }
  295. void UntrackPlaceholder(boost::icl::separate_interval_set<size_t>::iterator it) {
  296. placeholder_host_pointers.erase(it->lower());
  297. placeholders.erase(it);
  298. }
  299. /// Return true when a given memory region is a "nieche" and the placeholders don't have to be
  300. /// split.
  301. bool IsNiechePlaceholder(size_t virtual_offset, size_t length) const {
  302. const auto it = placeholders.upper_bound({virtual_offset, virtual_offset + length});
  303. if (it != placeholders.end() && it->lower() == virtual_offset + length) {
  304. return it == placeholders.begin() ? virtual_offset == 0
  305. : std::prev(it)->upper() == virtual_offset;
  306. }
  307. return false;
  308. }
  309. HANDLE process{}; ///< Current process handle
  310. HANDLE backing_handle{}; ///< File based backing memory
  311. DynamicLibrary kernelbase_dll;
  312. PFN_CreateFileMapping2 pfn_CreateFileMapping2{};
  313. PFN_VirtualAlloc2 pfn_VirtualAlloc2{};
  314. PFN_MapViewOfFile3 pfn_MapViewOfFile3{};
  315. PFN_UnmapViewOfFile2 pfn_UnmapViewOfFile2{};
  316. std::mutex placeholder_mutex; ///< Mutex for placeholders
  317. boost::icl::separate_interval_set<size_t> placeholders; ///< Mapped placeholders
  318. std::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset
  319. };
  320. #elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv
  321. #ifdef ARCHITECTURE_arm64
  322. static void* ChooseVirtualBase(size_t virtual_size) {
  323. constexpr uintptr_t Map39BitSize = (1ULL << 39);
  324. constexpr uintptr_t Map36BitSize = (1ULL << 36);
  325. // This is not a cryptographic application, we just want something random.
  326. std::mt19937_64 rng;
  327. // We want to ensure we are allocating at an address aligned to the L2 block size.
  328. // For Qualcomm devices, we must also allocate memory above 36 bits.
  329. const size_t lower = Map36BitSize / HugePageSize;
  330. const size_t upper = (Map39BitSize - virtual_size) / HugePageSize;
  331. const size_t range = upper - lower;
  332. // Try up to 64 times to allocate memory at random addresses in the range.
  333. for (int i = 0; i < 64; i++) {
  334. // Calculate a possible location.
  335. uintptr_t hint_address = ((rng() % range) + lower) * HugePageSize;
  336. // Try to map.
  337. // Note: we may be able to take advantage of MAP_FIXED_NOREPLACE here.
  338. void* map_pointer =
  339. mmap(reinterpret_cast<void*>(hint_address), virtual_size, PROT_READ | PROT_WRITE,
  340. MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
  341. // If we successfully mapped, we're done.
  342. if (reinterpret_cast<uintptr_t>(map_pointer) == hint_address) {
  343. return map_pointer;
  344. }
  345. // Unmap if necessary, and try again.
  346. if (map_pointer != MAP_FAILED) {
  347. munmap(map_pointer, virtual_size);
  348. }
  349. }
  350. return MAP_FAILED;
  351. }
  352. #else
  353. static void* ChooseVirtualBase(size_t virtual_size) {
  354. #if defined(__FreeBSD__)
  355. void* virtual_base =
  356. mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE,
  357. MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER, -1, 0);
  358. if (virtual_base != MAP_FAILED) {
  359. return virtual_base;
  360. }
  361. #endif
  362. return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE,
  363. MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
  364. }
  365. #endif
  366. class HostMemory::Impl {
  367. public:
  368. explicit Impl(size_t backing_size_, size_t virtual_size_)
  369. : backing_size{backing_size_}, virtual_size{virtual_size_} {
  370. bool good = false;
  371. SCOPE_EXIT({
  372. if (!good) {
  373. Release();
  374. }
  375. });
  376. long page_size = sysconf(_SC_PAGESIZE);
  377. if (page_size != 0x1000) {
  378. LOG_CRITICAL(HW_Memory, "page size {:#x} is incompatible with 4K paging", page_size);
  379. throw std::bad_alloc{};
  380. }
  381. // Backing memory initialization
  382. #if defined(__FreeBSD__) && __FreeBSD__ < 13
  383. // XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30
  384. fd = shm_open(SHM_ANON, O_RDWR, 0600);
  385. #else
  386. fd = memfd_create("HostMemory", 0);
  387. #endif
  388. if (fd < 0) {
  389. LOG_CRITICAL(HW_Memory, "memfd_create failed: {}", strerror(errno));
  390. throw std::bad_alloc{};
  391. }
  392. // Defined to extend the file with zeros
  393. int ret = ftruncate(fd, backing_size);
  394. if (ret != 0) {
  395. LOG_CRITICAL(HW_Memory, "ftruncate failed with {}, are you out-of-memory?",
  396. strerror(errno));
  397. throw std::bad_alloc{};
  398. }
  399. backing_base = static_cast<u8*>(
  400. mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
  401. if (backing_base == MAP_FAILED) {
  402. LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
  403. throw std::bad_alloc{};
  404. }
  405. // Virtual memory initialization
  406. virtual_base = virtual_map_base = static_cast<u8*>(ChooseVirtualBase(virtual_size));
  407. if (virtual_base == MAP_FAILED) {
  408. LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
  409. throw std::bad_alloc{};
  410. }
  411. #if defined(__linux__)
  412. madvise(virtual_base, virtual_size, MADV_HUGEPAGE);
  413. #endif
  414. free_manager.SetAddressSpace(virtual_base, virtual_size);
  415. good = true;
  416. }
  417. ~Impl() {
  418. Release();
  419. }
  420. void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
  421. // Intersect the range with our address space.
  422. AdjustMap(&virtual_offset, &length);
  423. // We are removing a placeholder.
  424. free_manager.AllocateBlock(virtual_base + virtual_offset, length);
  425. // Deduce mapping protection flags.
  426. int flags = PROT_NONE;
  427. if (True(perms & MemoryPermission::Read)) {
  428. flags |= PROT_READ;
  429. }
  430. if (True(perms & MemoryPermission::Write)) {
  431. flags |= PROT_WRITE;
  432. }
  433. #ifdef ARCHITECTURE_arm64
  434. if (True(perms & MemoryPermission::Execute)) {
  435. flags |= PROT_EXEC;
  436. }
  437. #endif
  438. void* ret = mmap(virtual_base + virtual_offset, length, flags, MAP_SHARED | MAP_FIXED, fd,
  439. host_offset);
  440. ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
  441. }
  442. void Unmap(size_t virtual_offset, size_t length) {
  443. // The method name is wrong. We're still talking about the virtual range.
  444. // We don't want to unmap, we want to reserve this memory.
  445. // Intersect the range with our address space.
  446. AdjustMap(&virtual_offset, &length);
  447. // Merge with any adjacent placeholder mappings.
  448. auto [merged_pointer, merged_size] =
  449. free_manager.FreeBlock(virtual_base + virtual_offset, length);
  450. void* ret = mmap(merged_pointer, merged_size, PROT_NONE,
  451. MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
  452. ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
  453. }
  454. void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {
  455. // Intersect the range with our address space.
  456. AdjustMap(&virtual_offset, &length);
  457. int flags = PROT_NONE;
  458. if (read) {
  459. flags |= PROT_READ;
  460. }
  461. if (write) {
  462. flags |= PROT_WRITE;
  463. }
  464. #ifdef HAS_NCE
  465. if (execute) {
  466. flags |= PROT_EXEC;
  467. }
  468. #endif
  469. int ret = mprotect(virtual_base + virtual_offset, length, flags);
  470. ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno));
  471. }
  472. bool ClearBackingRegion(size_t physical_offset, size_t length) {
  473. #ifdef __linux__
  474. // Set MADV_REMOVE on backing map to destroy it instantly.
  475. // This also deletes the area from the backing file.
  476. int ret = madvise(backing_base + physical_offset, length, MADV_REMOVE);
  477. ASSERT_MSG(ret == 0, "madvise failed: {}", strerror(errno));
  478. return true;
  479. #else
  480. return false;
  481. #endif
  482. }
  483. void EnableDirectMappedAddress() {
  484. virtual_base = nullptr;
  485. }
  486. const size_t backing_size; ///< Size of the backing memory in bytes
  487. const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
  488. u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)};
  489. u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)};
  490. u8* virtual_map_base{reinterpret_cast<u8*>(MAP_FAILED)};
  491. private:
  492. /// Release all resources in the object
  493. void Release() {
  494. if (virtual_map_base != MAP_FAILED) {
  495. int ret = munmap(virtual_map_base, virtual_size);
  496. ASSERT_MSG(ret == 0, "munmap failed: {}", strerror(errno));
  497. }
  498. if (backing_base != MAP_FAILED) {
  499. int ret = munmap(backing_base, backing_size);
  500. ASSERT_MSG(ret == 0, "munmap failed: {}", strerror(errno));
  501. }
  502. if (fd != -1) {
  503. int ret = close(fd);
  504. ASSERT_MSG(ret == 0, "close failed: {}", strerror(errno));
  505. }
  506. }
  507. void AdjustMap(size_t* virtual_offset, size_t* length) {
  508. if (virtual_base != nullptr) {
  509. return;
  510. }
  511. // If we are direct mapped, we want to make sure we are operating on a region
  512. // that is in range of our virtual mapping.
  513. size_t intended_start = *virtual_offset;
  514. size_t intended_end = intended_start + *length;
  515. size_t address_space_start = reinterpret_cast<size_t>(virtual_map_base);
  516. size_t address_space_end = address_space_start + virtual_size;
  517. if (address_space_start > intended_end || intended_start > address_space_end) {
  518. *virtual_offset = 0;
  519. *length = 0;
  520. } else {
  521. *virtual_offset = std::max(intended_start, address_space_start);
  522. *length = std::min(intended_end, address_space_end) - *virtual_offset;
  523. }
  524. }
  525. int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create
  526. FreeRegionManager free_manager{};
  527. };
  528. #else // ^^^ Linux ^^^ vvv Generic vvv
  529. class HostMemory::Impl {
  530. public:
  531. explicit Impl(size_t /*backing_size */, size_t /* virtual_size */) {
  532. // This is just a place holder.
  533. // Please implement fastmem in a proper way on your platform.
  534. throw std::bad_alloc{};
  535. }
  536. void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perm) {}
  537. void Unmap(size_t virtual_offset, size_t length) {}
  538. void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {}
  539. bool ClearBackingRegion(size_t physical_offset, size_t length) {
  540. return false;
  541. }
  542. void EnableDirectMappedAddress() {}
  543. u8* backing_base{nullptr};
  544. u8* virtual_base{nullptr};
  545. };
  546. #endif // ^^^ Generic ^^^
  547. HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
  548. : backing_size(backing_size_), virtual_size(virtual_size_) {
  549. try {
  550. // Try to allocate a fastmem arena.
  551. // The implementation will fail with std::bad_alloc on errors.
  552. impl =
  553. std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment),
  554. AlignUp(virtual_size, PageAlignment) + HugePageSize);
  555. backing_base = impl->backing_base;
  556. virtual_base = impl->virtual_base;
  557. if (virtual_base) {
  558. // Ensure the virtual base is aligned to the L2 block size.
  559. virtual_base = reinterpret_cast<u8*>(
  560. Common::AlignUp(reinterpret_cast<uintptr_t>(virtual_base), HugePageSize));
  561. virtual_base_offset = virtual_base - impl->virtual_base;
  562. }
  563. } catch (const std::bad_alloc&) {
  564. LOG_CRITICAL(HW_Memory,
  565. "Fastmem unavailable, falling back to VirtualBuffer for memory allocation");
  566. fallback_buffer = std::make_unique<Common::VirtualBuffer<u8>>(backing_size);
  567. backing_base = fallback_buffer->data();
  568. virtual_base = nullptr;
  569. }
  570. }
  571. HostMemory::~HostMemory() = default;
  572. HostMemory::HostMemory(HostMemory&&) noexcept = default;
  573. HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default;
  574. void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length,
  575. MemoryPermission perms) {
  576. ASSERT(virtual_offset % PageAlignment == 0);
  577. ASSERT(host_offset % PageAlignment == 0);
  578. ASSERT(length % PageAlignment == 0);
  579. ASSERT(virtual_offset + length <= virtual_size);
  580. ASSERT(host_offset + length <= backing_size);
  581. if (length == 0 || !virtual_base || !impl) {
  582. return;
  583. }
  584. impl->Map(virtual_offset + virtual_base_offset, host_offset, length, perms);
  585. }
  586. void HostMemory::Unmap(size_t virtual_offset, size_t length) {
  587. ASSERT(virtual_offset % PageAlignment == 0);
  588. ASSERT(length % PageAlignment == 0);
  589. ASSERT(virtual_offset + length <= virtual_size);
  590. if (length == 0 || !virtual_base || !impl) {
  591. return;
  592. }
  593. impl->Unmap(virtual_offset + virtual_base_offset, length);
  594. }
  595. void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write,
  596. bool execute) {
  597. ASSERT(virtual_offset % PageAlignment == 0);
  598. ASSERT(length % PageAlignment == 0);
  599. ASSERT(virtual_offset + length <= virtual_size);
  600. if (length == 0 || !virtual_base || !impl) {
  601. return;
  602. }
  603. impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute);
  604. }
  605. void HostMemory::ClearBackingRegion(size_t physical_offset, size_t length, u32 fill_value) {
  606. if (!impl || fill_value != 0 || !impl->ClearBackingRegion(physical_offset, length)) {
  607. std::memset(backing_base + physical_offset, fill_value, length);
  608. }
  609. }
  610. void HostMemory::EnableDirectMappedAddress() {
  611. if (impl) {
  612. impl->EnableDirectMappedAddress();
  613. virtual_size += reinterpret_cast<uintptr_t>(virtual_base);
  614. }
  615. }
  616. } // namespace Common