host_memory.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  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. #ifdef ANDROID
  11. #include <android/sharedmem.h>
  12. #endif
  13. #ifndef _GNU_SOURCE
  14. #define _GNU_SOURCE
  15. #endif
  16. #include <boost/icl/interval_set.hpp>
  17. #include <fcntl.h>
  18. #include <sys/mman.h>
  19. #include <sys/random.h>
  20. #include <unistd.h>
  21. #include "common/scope_exit.h"
  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. void EnableDirectMappedAddress() {
  166. // TODO
  167. UNREACHABLE();
  168. }
  169. const size_t backing_size; ///< Size of the backing memory in bytes
  170. const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
  171. u8* backing_base{};
  172. u8* virtual_base{};
  173. private:
  174. /// Release all resources in the object
  175. void Release() {
  176. if (!placeholders.empty()) {
  177. for (const auto& placeholder : placeholders) {
  178. if (!pfn_UnmapViewOfFile2(process, virtual_base + placeholder.lower(),
  179. MEM_PRESERVE_PLACEHOLDER)) {
  180. LOG_CRITICAL(HW_Memory, "Failed to unmap virtual memory placeholder");
  181. }
  182. }
  183. Coalesce(0, virtual_size);
  184. }
  185. if (virtual_base) {
  186. if (!VirtualFree(virtual_base, 0, MEM_RELEASE)) {
  187. LOG_CRITICAL(HW_Memory, "Failed to free virtual memory");
  188. }
  189. }
  190. if (backing_base) {
  191. if (!pfn_UnmapViewOfFile2(process, backing_base, MEM_PRESERVE_PLACEHOLDER)) {
  192. LOG_CRITICAL(HW_Memory, "Failed to unmap backing memory placeholder");
  193. }
  194. if (!VirtualFreeEx(process, backing_base, 0, MEM_RELEASE)) {
  195. LOG_CRITICAL(HW_Memory, "Failed to free backing memory");
  196. }
  197. }
  198. if (!CloseHandle(backing_handle)) {
  199. LOG_CRITICAL(HW_Memory, "Failed to free backing memory file handle");
  200. }
  201. }
  202. /// Unmap one placeholder in the given range (partial unmaps are supported)
  203. /// Return true when there are no more placeholders to unmap
  204. bool UnmapOnePlaceholder(size_t virtual_offset, size_t length) {
  205. const auto it = placeholders.find({virtual_offset, virtual_offset + length});
  206. const auto begin = placeholders.begin();
  207. const auto end = placeholders.end();
  208. if (it == end) {
  209. return false;
  210. }
  211. const size_t placeholder_begin = it->lower();
  212. const size_t placeholder_end = it->upper();
  213. const size_t unmap_begin = std::max(virtual_offset, placeholder_begin);
  214. const size_t unmap_end = std::min(virtual_offset + length, placeholder_end);
  215. ASSERT(unmap_begin >= placeholder_begin && unmap_begin < placeholder_end);
  216. ASSERT(unmap_end <= placeholder_end && unmap_end > placeholder_begin);
  217. const auto host_pointer_it = placeholder_host_pointers.find(placeholder_begin);
  218. ASSERT(host_pointer_it != placeholder_host_pointers.end());
  219. const size_t host_offset = host_pointer_it->second;
  220. const bool split_left = unmap_begin > placeholder_begin;
  221. const bool split_right = unmap_end < placeholder_end;
  222. if (!pfn_UnmapViewOfFile2(process, virtual_base + placeholder_begin,
  223. MEM_PRESERVE_PLACEHOLDER)) {
  224. LOG_CRITICAL(HW_Memory, "Failed to unmap placeholder");
  225. }
  226. // If we have to remap memory regions due to partial unmaps, we are in a data race as
  227. // Windows doesn't support remapping memory without unmapping first. Avoid adding any extra
  228. // logic within the panic region described below.
  229. // Panic region, we are in a data race right now
  230. if (split_left || split_right) {
  231. Split(unmap_begin, unmap_end - unmap_begin);
  232. }
  233. if (split_left) {
  234. MapView(placeholder_begin, host_offset, unmap_begin - placeholder_begin);
  235. }
  236. if (split_right) {
  237. MapView(unmap_end, host_offset + unmap_end - placeholder_begin,
  238. placeholder_end - unmap_end);
  239. }
  240. // End panic region
  241. size_t coalesce_begin = unmap_begin;
  242. if (!split_left) {
  243. // Try to coalesce pages to the left
  244. coalesce_begin = it == begin ? 0 : std::prev(it)->upper();
  245. if (coalesce_begin != placeholder_begin) {
  246. Coalesce(coalesce_begin, unmap_end - coalesce_begin);
  247. }
  248. }
  249. if (!split_right) {
  250. // Try to coalesce pages to the right
  251. const auto next = std::next(it);
  252. const size_t next_begin = next == end ? virtual_size : next->lower();
  253. if (placeholder_end != next_begin) {
  254. // We can coalesce to the right
  255. Coalesce(coalesce_begin, next_begin - coalesce_begin);
  256. }
  257. }
  258. // Remove and reinsert placeholder trackers
  259. UntrackPlaceholder(it);
  260. if (split_left) {
  261. TrackPlaceholder(placeholder_begin, host_offset, unmap_begin - placeholder_begin);
  262. }
  263. if (split_right) {
  264. TrackPlaceholder(unmap_end, host_offset + unmap_end - placeholder_begin,
  265. placeholder_end - unmap_end);
  266. }
  267. return true;
  268. }
  269. void MapView(size_t virtual_offset, size_t host_offset, size_t length) {
  270. if (!pfn_MapViewOfFile3(backing_handle, process, virtual_base + virtual_offset, host_offset,
  271. length, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0)) {
  272. LOG_CRITICAL(HW_Memory, "Failed to map placeholder");
  273. }
  274. }
  275. void Split(size_t virtual_offset, size_t length) {
  276. if (!VirtualFreeEx(process, reinterpret_cast<LPVOID>(virtual_base + virtual_offset), length,
  277. MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER)) {
  278. LOG_CRITICAL(HW_Memory, "Failed to split placeholder");
  279. }
  280. }
  281. void Coalesce(size_t virtual_offset, size_t length) {
  282. if (!VirtualFreeEx(process, reinterpret_cast<LPVOID>(virtual_base + virtual_offset), length,
  283. MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS)) {
  284. LOG_CRITICAL(HW_Memory, "Failed to coalesce placeholders");
  285. }
  286. }
  287. void TrackPlaceholder(size_t virtual_offset, size_t host_offset, size_t length) {
  288. placeholders.insert({virtual_offset, virtual_offset + length});
  289. placeholder_host_pointers.emplace(virtual_offset, host_offset);
  290. }
  291. void UntrackPlaceholder(boost::icl::separate_interval_set<size_t>::iterator it) {
  292. placeholder_host_pointers.erase(it->lower());
  293. placeholders.erase(it);
  294. }
  295. /// Return true when a given memory region is a "nieche" and the placeholders don't have to be
  296. /// split.
  297. bool IsNiechePlaceholder(size_t virtual_offset, size_t length) const {
  298. const auto it = placeholders.upper_bound({virtual_offset, virtual_offset + length});
  299. if (it != placeholders.end() && it->lower() == virtual_offset + length) {
  300. return it == placeholders.begin() ? virtual_offset == 0
  301. : std::prev(it)->upper() == virtual_offset;
  302. }
  303. return false;
  304. }
  305. HANDLE process{}; ///< Current process handle
  306. HANDLE backing_handle{}; ///< File based backing memory
  307. DynamicLibrary kernelbase_dll;
  308. PFN_CreateFileMapping2 pfn_CreateFileMapping2{};
  309. PFN_VirtualAlloc2 pfn_VirtualAlloc2{};
  310. PFN_MapViewOfFile3 pfn_MapViewOfFile3{};
  311. PFN_UnmapViewOfFile2 pfn_UnmapViewOfFile2{};
  312. std::mutex placeholder_mutex; ///< Mutex for placeholders
  313. boost::icl::separate_interval_set<size_t> placeholders; ///< Mapped placeholders
  314. std::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset
  315. };
  316. #elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv
  317. #ifdef ARCHITECTURE_arm64
  318. uint64_t GetRandomU64() {
  319. uint64_t ret;
  320. ASSERT(getrandom(&ret, sizeof(ret), 0) == 0);
  321. return ret;
  322. }
  323. void* ChooseVirtualBase(size_t virtual_size) {
  324. constexpr uintptr_t Map39BitSize = (1ULL << 39);
  325. constexpr uintptr_t Map36BitSize = (1ULL << 36);
  326. // Seed the MT with some initial strong randomness.
  327. //
  328. // This is not a cryptographic application, we just want something more
  329. // random than the current time.
  330. std::mt19937_64 rng(GetRandomU64());
  331. // We want to ensure we are allocating at an address aligned to the L2 block size.
  332. // For Qualcomm devices, we must also allocate memory above 36 bits.
  333. const size_t lower = Map36BitSize / HugePageSize;
  334. const size_t upper = (Map39BitSize - virtual_size) / HugePageSize;
  335. const size_t range = upper - lower;
  336. // Try up to 64 times to allocate memory at random addresses in the range.
  337. for (int i = 0; i < 64; i++) {
  338. // Calculate a possible location.
  339. uintptr_t hint_address = ((rng() % range) + lower) * HugePageSize;
  340. // Try to map.
  341. // Note: we may be able to take advantage of MAP_FIXED_NOREPLACE here.
  342. void* map_pointer =
  343. mmap(reinterpret_cast<void*>(hint_address), virtual_size, PROT_READ | PROT_WRITE,
  344. MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
  345. // If we successfully mapped, we're done.
  346. if (reinterpret_cast<uintptr_t>(map_pointer) == hint_address) {
  347. return map_pointer;
  348. }
  349. // Unmap if necessary, and try again.
  350. if (map_pointer != MAP_FAILED) {
  351. munmap(map_pointer, virtual_size);
  352. }
  353. }
  354. return MAP_FAILED;
  355. }
  356. #else
  357. void* ChooseVirtualBase(size_t virtual_size) {
  358. return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE,
  359. MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
  360. }
  361. #endif
  362. class HostMemory::Impl {
  363. public:
  364. explicit Impl(size_t backing_size_, size_t virtual_size_)
  365. : backing_size{backing_size_}, virtual_size{virtual_size_} {
  366. bool good = false;
  367. SCOPE_EXIT({
  368. if (!good) {
  369. Release();
  370. }
  371. });
  372. long page_size = sysconf(_SC_PAGESIZE);
  373. if (page_size != 0x1000) {
  374. LOG_CRITICAL(HW_Memory, "page size {:#x} is incompatible with 4K paging", page_size);
  375. throw std::bad_alloc{};
  376. }
  377. // Backing memory initialization
  378. #ifdef ANDROID
  379. fd = ASharedMemory_create("HostMemory", backing_size);
  380. #elif defined(__FreeBSD__) && __FreeBSD__ < 13
  381. // XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30
  382. fd = shm_open(SHM_ANON, O_RDWR, 0600);
  383. #else
  384. fd = memfd_create("HostMemory", 0);
  385. #endif
  386. if (fd < 0) {
  387. LOG_CRITICAL(HW_Memory, "memfd_create failed: {}", strerror(errno));
  388. throw std::bad_alloc{};
  389. }
  390. #ifndef ANDROID
  391. // Defined to extend the file with zeros
  392. int ret = ftruncate(fd, backing_size);
  393. if (ret != 0) {
  394. LOG_CRITICAL(HW_Memory, "ftruncate failed with {}, are you out-of-memory?",
  395. strerror(errno));
  396. throw std::bad_alloc{};
  397. }
  398. #endif
  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. #if defined(__FreeBSD__)
  407. virtual_base =
  408. static_cast<u8*>(mmap(nullptr, virtual_size, PROT_NONE,
  409. MAP_PRIVATE | MAP_ANONYMOUS | MAP_ALIGNED_SUPER, -1, 0));
  410. if (virtual_base == MAP_FAILED) {
  411. virtual_base = static_cast<u8*>(
  412. mmap(nullptr, virtual_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
  413. if (virtual_base == MAP_FAILED) {
  414. LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
  415. throw std::bad_alloc{};
  416. }
  417. }
  418. #else
  419. virtual_base = virtual_map_base = static_cast<u8*>(ChooseVirtualBase(virtual_size));
  420. if (virtual_base == MAP_FAILED) {
  421. LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
  422. throw std::bad_alloc{};
  423. }
  424. madvise(virtual_base, virtual_size, MADV_HUGEPAGE);
  425. #endif
  426. free_manager.SetAddressSpace(virtual_base, virtual_size);
  427. good = true;
  428. }
  429. ~Impl() {
  430. Release();
  431. }
  432. void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
  433. // Intersect the range with our address space.
  434. AdjustMap(&virtual_offset, &length);
  435. // We are removing a placeholder.
  436. free_manager.AllocateBlock(virtual_base + virtual_offset, length);
  437. // Deduce mapping protection flags.
  438. int flags = PROT_NONE;
  439. if (True(perms & MemoryPermission::Read)) {
  440. flags |= PROT_READ;
  441. }
  442. if (True(perms & MemoryPermission::Write)) {
  443. flags |= PROT_WRITE;
  444. }
  445. #ifdef ARCHITECTURE_arm64
  446. if (True(perms & MemoryPermission::Execute)) {
  447. flags |= PROT_EXEC;
  448. }
  449. #endif
  450. void* ret = mmap(virtual_base + virtual_offset, length, flags, MAP_SHARED | MAP_FIXED, fd,
  451. host_offset);
  452. ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
  453. }
  454. void Unmap(size_t virtual_offset, size_t length) {
  455. // The method name is wrong. We're still talking about the virtual range.
  456. // We don't want to unmap, we want to reserve this memory.
  457. // Intersect the range with our address space.
  458. AdjustMap(&virtual_offset, &length);
  459. // Merge with any adjacent placeholder mappings.
  460. auto [merged_pointer, merged_size] =
  461. free_manager.FreeBlock(virtual_base + virtual_offset, length);
  462. void* ret = mmap(merged_pointer, merged_size, PROT_NONE,
  463. MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
  464. ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
  465. }
  466. void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {
  467. // Intersect the range with our address space.
  468. AdjustMap(&virtual_offset, &length);
  469. int flags = PROT_NONE;
  470. if (read) {
  471. flags |= PROT_READ;
  472. }
  473. if (write) {
  474. flags |= PROT_WRITE;
  475. }
  476. #ifdef ARCHITECTURE_arm64
  477. if (execute) {
  478. flags |= PROT_EXEC;
  479. }
  480. #endif
  481. int ret = mprotect(virtual_base + virtual_offset, length, flags);
  482. ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno));
  483. }
  484. void EnableDirectMappedAddress() {
  485. virtual_base = nullptr;
  486. }
  487. const size_t backing_size; ///< Size of the backing memory in bytes
  488. const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
  489. u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)};
  490. u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)};
  491. u8* virtual_map_base{reinterpret_cast<u8*>(MAP_FAILED)};
  492. private:
  493. /// Release all resources in the object
  494. void Release() {
  495. if (virtual_map_base != MAP_FAILED) {
  496. int ret = munmap(virtual_map_base, virtual_size);
  497. ASSERT_MSG(ret == 0, "munmap failed: {}", strerror(errno));
  498. }
  499. if (backing_base != MAP_FAILED) {
  500. int ret = munmap(backing_base, backing_size);
  501. ASSERT_MSG(ret == 0, "munmap failed: {}", strerror(errno));
  502. }
  503. if (fd != -1) {
  504. int ret = close(fd);
  505. ASSERT_MSG(ret == 0, "close failed: {}", strerror(errno));
  506. }
  507. }
  508. void AdjustMap(size_t* virtual_offset, size_t* length) {
  509. if (virtual_base != nullptr) {
  510. return;
  511. }
  512. // If we are direct mapped, we want to make sure we are operating on a region
  513. // that is in range of our virtual mapping.
  514. size_t intended_start = *virtual_offset;
  515. size_t intended_end = intended_start + *length;
  516. size_t address_space_start = reinterpret_cast<size_t>(virtual_map_base);
  517. size_t address_space_end = address_space_start + virtual_size;
  518. if (address_space_start > intended_end || intended_start > address_space_end) {
  519. *virtual_offset = 0;
  520. *length = 0;
  521. } else {
  522. *virtual_offset = std::max(intended_start, address_space_start);
  523. *length = std::min(intended_end, address_space_end) - *virtual_offset;
  524. }
  525. }
  526. int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create
  527. FreeRegionManager free_manager{};
  528. };
  529. #else // ^^^ Linux ^^^ vvv Generic vvv
  530. class HostMemory::Impl {
  531. public:
  532. explicit Impl(size_t /*backing_size */, size_t /* virtual_size */) {
  533. // This is just a place holder.
  534. // Please implement fastmem in a proper way on your platform.
  535. throw std::bad_alloc{};
  536. }
  537. void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perm) {}
  538. void Unmap(size_t virtual_offset, size_t length) {}
  539. void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {}
  540. u8* backing_base{nullptr};
  541. u8* virtual_base{nullptr};
  542. };
  543. #endif // ^^^ Generic ^^^
  544. HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
  545. : backing_size(backing_size_), virtual_size(virtual_size_) {
  546. try {
  547. // Try to allocate a fastmem arena.
  548. // The implementation will fail with std::bad_alloc on errors.
  549. impl =
  550. std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment),
  551. AlignUp(virtual_size, PageAlignment) + HugePageSize);
  552. backing_base = impl->backing_base;
  553. virtual_base = impl->virtual_base;
  554. if (virtual_base) {
  555. // Ensure the virtual base is aligned to the L2 block size.
  556. virtual_base = reinterpret_cast<u8*>(
  557. Common::AlignUp(reinterpret_cast<uintptr_t>(virtual_base), HugePageSize));
  558. virtual_base_offset = virtual_base - impl->virtual_base;
  559. }
  560. } catch (const std::bad_alloc&) {
  561. LOG_CRITICAL(HW_Memory,
  562. "Fastmem unavailable, falling back to VirtualBuffer for memory allocation");
  563. fallback_buffer = std::make_unique<Common::VirtualBuffer<u8>>(backing_size);
  564. backing_base = fallback_buffer->data();
  565. virtual_base = nullptr;
  566. }
  567. }
  568. HostMemory::~HostMemory() = default;
  569. HostMemory::HostMemory(HostMemory&&) noexcept = default;
  570. HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default;
  571. void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length,
  572. MemoryPermission perms) {
  573. ASSERT(virtual_offset % PageAlignment == 0);
  574. ASSERT(host_offset % PageAlignment == 0);
  575. ASSERT(length % PageAlignment == 0);
  576. ASSERT(virtual_offset + length <= virtual_size);
  577. ASSERT(host_offset + length <= backing_size);
  578. if (length == 0 || !virtual_base || !impl) {
  579. return;
  580. }
  581. impl->Map(virtual_offset + virtual_base_offset, host_offset, length, perms);
  582. }
  583. void HostMemory::Unmap(size_t virtual_offset, size_t length) {
  584. ASSERT(virtual_offset % PageAlignment == 0);
  585. ASSERT(length % PageAlignment == 0);
  586. ASSERT(virtual_offset + length <= virtual_size);
  587. if (length == 0 || !virtual_base || !impl) {
  588. return;
  589. }
  590. impl->Unmap(virtual_offset + virtual_base_offset, length);
  591. }
  592. void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write,
  593. bool execute) {
  594. ASSERT(virtual_offset % PageAlignment == 0);
  595. ASSERT(length % PageAlignment == 0);
  596. ASSERT(virtual_offset + length <= virtual_size);
  597. if (length == 0 || !virtual_base || !impl) {
  598. return;
  599. }
  600. impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute);
  601. }
  602. void HostMemory::EnableDirectMappedAddress() {
  603. if (impl) {
  604. impl->EnableDirectMappedAddress();
  605. virtual_size += reinterpret_cast<uintptr_t>(virtual_base);
  606. }
  607. }
  608. } // namespace Common