host_memory.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  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 <fcntl.h>
  14. #include <sys/mman.h>
  15. #include <unistd.h>
  16. #include "common/scope_exit.h"
  17. #endif // ^^^ Linux ^^^
  18. #include <mutex>
  19. #include "common/alignment.h"
  20. #include "common/assert.h"
  21. #include "common/host_memory.h"
  22. #include "common/logging/log.h"
  23. namespace Common {
  24. constexpr size_t PageAlignment = 0x1000;
  25. constexpr size_t HugePageSize = 0x200000;
  26. #ifdef _WIN32
  27. // Manually imported for MinGW compatibility
  28. #ifndef MEM_RESERVE_PLACEHOLDER
  29. #define MEM_RESERVE_PLACEHOLDER 0x00040000
  30. #endif
  31. #ifndef MEM_REPLACE_PLACEHOLDER
  32. #define MEM_REPLACE_PLACEHOLDER 0x00004000
  33. #endif
  34. #ifndef MEM_COALESCE_PLACEHOLDERS
  35. #define MEM_COALESCE_PLACEHOLDERS 0x00000001
  36. #endif
  37. #ifndef MEM_PRESERVE_PLACEHOLDER
  38. #define MEM_PRESERVE_PLACEHOLDER 0x00000002
  39. #endif
  40. using PFN_CreateFileMapping2 = _Ret_maybenull_ HANDLE(WINAPI*)(
  41. _In_ HANDLE File, _In_opt_ SECURITY_ATTRIBUTES* SecurityAttributes, _In_ ULONG DesiredAccess,
  42. _In_ ULONG PageProtection, _In_ ULONG AllocationAttributes, _In_ ULONG64 MaximumSize,
  43. _In_opt_ PCWSTR Name,
  44. _Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters,
  45. _In_ ULONG ParameterCount);
  46. using PFN_VirtualAlloc2 = _Ret_maybenull_ PVOID(WINAPI*)(
  47. _In_opt_ HANDLE Process, _In_opt_ PVOID BaseAddress, _In_ SIZE_T Size,
  48. _In_ ULONG AllocationType, _In_ ULONG PageProtection,
  49. _Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters,
  50. _In_ ULONG ParameterCount);
  51. using PFN_MapViewOfFile3 = _Ret_maybenull_ PVOID(WINAPI*)(
  52. _In_ HANDLE FileMapping, _In_opt_ HANDLE Process, _In_opt_ PVOID BaseAddress,
  53. _In_ ULONG64 Offset, _In_ SIZE_T ViewSize, _In_ ULONG AllocationType, _In_ ULONG PageProtection,
  54. _Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters,
  55. _In_ ULONG ParameterCount);
  56. using PFN_UnmapViewOfFile2 = BOOL(WINAPI*)(_In_ HANDLE Process, _In_ PVOID BaseAddress,
  57. _In_ ULONG UnmapFlags);
  58. template <typename T>
  59. static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn) {
  60. if (!dll.GetSymbol(name, &pfn)) {
  61. LOG_CRITICAL(HW_Memory, "Failed to load {}", name);
  62. throw std::bad_alloc{};
  63. }
  64. }
  65. class HostMemory::Impl {
  66. public:
  67. explicit Impl(size_t backing_size_, size_t virtual_size_)
  68. : backing_size{backing_size_}, virtual_size{virtual_size_}, process{GetCurrentProcess()},
  69. kernelbase_dll("Kernelbase") {
  70. if (!kernelbase_dll.IsOpen()) {
  71. LOG_CRITICAL(HW_Memory, "Failed to load Kernelbase.dll");
  72. throw std::bad_alloc{};
  73. }
  74. GetFuncAddress(kernelbase_dll, "CreateFileMapping2", pfn_CreateFileMapping2);
  75. GetFuncAddress(kernelbase_dll, "VirtualAlloc2", pfn_VirtualAlloc2);
  76. GetFuncAddress(kernelbase_dll, "MapViewOfFile3", pfn_MapViewOfFile3);
  77. GetFuncAddress(kernelbase_dll, "UnmapViewOfFile2", pfn_UnmapViewOfFile2);
  78. // Allocate backing file map
  79. backing_handle =
  80. pfn_CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr, FILE_MAP_WRITE | FILE_MAP_READ,
  81. PAGE_READWRITE, SEC_COMMIT, backing_size, nullptr, nullptr, 0);
  82. if (!backing_handle) {
  83. LOG_CRITICAL(HW_Memory, "Failed to allocate {} MiB of backing memory",
  84. backing_size >> 20);
  85. throw std::bad_alloc{};
  86. }
  87. // Allocate a virtual memory for the backing file map as placeholder
  88. backing_base = static_cast<u8*>(pfn_VirtualAlloc2(process, nullptr, backing_size,
  89. MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
  90. PAGE_NOACCESS, nullptr, 0));
  91. if (!backing_base) {
  92. Release();
  93. LOG_CRITICAL(HW_Memory, "Failed to reserve {} MiB of virtual memory",
  94. backing_size >> 20);
  95. throw std::bad_alloc{};
  96. }
  97. // Map backing placeholder
  98. void* const ret = pfn_MapViewOfFile3(backing_handle, process, backing_base, 0, backing_size,
  99. MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0);
  100. if (ret != backing_base) {
  101. Release();
  102. LOG_CRITICAL(HW_Memory, "Failed to map {} MiB of virtual memory", backing_size >> 20);
  103. throw std::bad_alloc{};
  104. }
  105. // Allocate virtual address placeholder
  106. virtual_base = static_cast<u8*>(pfn_VirtualAlloc2(process, nullptr, virtual_size,
  107. MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
  108. PAGE_NOACCESS, nullptr, 0));
  109. if (!virtual_base) {
  110. Release();
  111. LOG_CRITICAL(HW_Memory, "Failed to reserve {} GiB of virtual memory",
  112. virtual_size >> 30);
  113. throw std::bad_alloc{};
  114. }
  115. }
  116. ~Impl() {
  117. Release();
  118. }
  119. void Map(size_t virtual_offset, size_t host_offset, size_t length) {
  120. std::unique_lock lock{placeholder_mutex};
  121. if (!IsNiechePlaceholder(virtual_offset, length)) {
  122. Split(virtual_offset, length);
  123. }
  124. ASSERT(placeholders.find({virtual_offset, virtual_offset + length}) == placeholders.end());
  125. TrackPlaceholder(virtual_offset, host_offset, length);
  126. MapView(virtual_offset, host_offset, length);
  127. }
  128. void Unmap(size_t virtual_offset, size_t length) {
  129. std::scoped_lock lock{placeholder_mutex};
  130. // Unmap until there are no more placeholders
  131. while (UnmapOnePlaceholder(virtual_offset, length)) {
  132. }
  133. }
  134. void Protect(size_t virtual_offset, size_t length, bool read, bool write) {
  135. DWORD new_flags{};
  136. if (read && write) {
  137. new_flags = PAGE_READWRITE;
  138. } else if (read && !write) {
  139. new_flags = PAGE_READONLY;
  140. } else if (!read && !write) {
  141. new_flags = PAGE_NOACCESS;
  142. } else {
  143. UNIMPLEMENTED_MSG("Protection flag combination read={} write={}", read, write);
  144. }
  145. const size_t virtual_end = virtual_offset + length;
  146. std::scoped_lock lock{placeholder_mutex};
  147. auto [it, end] = placeholders.equal_range({virtual_offset, virtual_end});
  148. while (it != end) {
  149. const size_t offset = std::max(it->lower(), virtual_offset);
  150. const size_t protect_length = std::min(it->upper(), virtual_end) - offset;
  151. DWORD old_flags{};
  152. if (!VirtualProtect(virtual_base + offset, protect_length, new_flags, &old_flags)) {
  153. LOG_CRITICAL(HW_Memory, "Failed to change virtual memory protect rules");
  154. }
  155. ++it;
  156. }
  157. }
  158. const size_t backing_size; ///< Size of the backing memory in bytes
  159. const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
  160. u8* backing_base{};
  161. u8* virtual_base{};
  162. private:
  163. /// Release all resources in the object
  164. void Release() {
  165. if (!placeholders.empty()) {
  166. for (const auto& placeholder : placeholders) {
  167. if (!pfn_UnmapViewOfFile2(process, virtual_base + placeholder.lower(),
  168. MEM_PRESERVE_PLACEHOLDER)) {
  169. LOG_CRITICAL(HW_Memory, "Failed to unmap virtual memory placeholder");
  170. }
  171. }
  172. Coalesce(0, virtual_size);
  173. }
  174. if (virtual_base) {
  175. if (!VirtualFree(virtual_base, 0, MEM_RELEASE)) {
  176. LOG_CRITICAL(HW_Memory, "Failed to free virtual memory");
  177. }
  178. }
  179. if (backing_base) {
  180. if (!pfn_UnmapViewOfFile2(process, backing_base, MEM_PRESERVE_PLACEHOLDER)) {
  181. LOG_CRITICAL(HW_Memory, "Failed to unmap backing memory placeholder");
  182. }
  183. if (!VirtualFreeEx(process, backing_base, 0, MEM_RELEASE)) {
  184. LOG_CRITICAL(HW_Memory, "Failed to free backing memory");
  185. }
  186. }
  187. if (!CloseHandle(backing_handle)) {
  188. LOG_CRITICAL(HW_Memory, "Failed to free backing memory file handle");
  189. }
  190. }
  191. /// Unmap one placeholder in the given range (partial unmaps are supported)
  192. /// Return true when there are no more placeholders to unmap
  193. bool UnmapOnePlaceholder(size_t virtual_offset, size_t length) {
  194. const auto it = placeholders.find({virtual_offset, virtual_offset + length});
  195. const auto begin = placeholders.begin();
  196. const auto end = placeholders.end();
  197. if (it == end) {
  198. return false;
  199. }
  200. const size_t placeholder_begin = it->lower();
  201. const size_t placeholder_end = it->upper();
  202. const size_t unmap_begin = std::max(virtual_offset, placeholder_begin);
  203. const size_t unmap_end = std::min(virtual_offset + length, placeholder_end);
  204. ASSERT(unmap_begin >= placeholder_begin && unmap_begin < placeholder_end);
  205. ASSERT(unmap_end <= placeholder_end && unmap_end > placeholder_begin);
  206. const auto host_pointer_it = placeholder_host_pointers.find(placeholder_begin);
  207. ASSERT(host_pointer_it != placeholder_host_pointers.end());
  208. const size_t host_offset = host_pointer_it->second;
  209. const bool split_left = unmap_begin > placeholder_begin;
  210. const bool split_right = unmap_end < placeholder_end;
  211. if (!pfn_UnmapViewOfFile2(process, virtual_base + placeholder_begin,
  212. MEM_PRESERVE_PLACEHOLDER)) {
  213. LOG_CRITICAL(HW_Memory, "Failed to unmap placeholder");
  214. }
  215. // If we have to remap memory regions due to partial unmaps, we are in a data race as
  216. // Windows doesn't support remapping memory without unmapping first. Avoid adding any extra
  217. // logic within the panic region described below.
  218. // Panic region, we are in a data race right now
  219. if (split_left || split_right) {
  220. Split(unmap_begin, unmap_end - unmap_begin);
  221. }
  222. if (split_left) {
  223. MapView(placeholder_begin, host_offset, unmap_begin - placeholder_begin);
  224. }
  225. if (split_right) {
  226. MapView(unmap_end, host_offset + unmap_end - placeholder_begin,
  227. placeholder_end - unmap_end);
  228. }
  229. // End panic region
  230. size_t coalesce_begin = unmap_begin;
  231. if (!split_left) {
  232. // Try to coalesce pages to the left
  233. coalesce_begin = it == begin ? 0 : std::prev(it)->upper();
  234. if (coalesce_begin != placeholder_begin) {
  235. Coalesce(coalesce_begin, unmap_end - coalesce_begin);
  236. }
  237. }
  238. if (!split_right) {
  239. // Try to coalesce pages to the right
  240. const auto next = std::next(it);
  241. const size_t next_begin = next == end ? virtual_size : next->lower();
  242. if (placeholder_end != next_begin) {
  243. // We can coalesce to the right
  244. Coalesce(coalesce_begin, next_begin - coalesce_begin);
  245. }
  246. }
  247. // Remove and reinsert placeholder trackers
  248. UntrackPlaceholder(it);
  249. if (split_left) {
  250. TrackPlaceholder(placeholder_begin, host_offset, unmap_begin - placeholder_begin);
  251. }
  252. if (split_right) {
  253. TrackPlaceholder(unmap_end, host_offset + unmap_end - placeholder_begin,
  254. placeholder_end - unmap_end);
  255. }
  256. return true;
  257. }
  258. void MapView(size_t virtual_offset, size_t host_offset, size_t length) {
  259. if (!pfn_MapViewOfFile3(backing_handle, process, virtual_base + virtual_offset, host_offset,
  260. length, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0)) {
  261. LOG_CRITICAL(HW_Memory, "Failed to map placeholder");
  262. }
  263. }
  264. void Split(size_t virtual_offset, size_t length) {
  265. if (!VirtualFreeEx(process, reinterpret_cast<LPVOID>(virtual_base + virtual_offset), length,
  266. MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER)) {
  267. LOG_CRITICAL(HW_Memory, "Failed to split placeholder");
  268. }
  269. }
  270. void Coalesce(size_t virtual_offset, size_t length) {
  271. if (!VirtualFreeEx(process, reinterpret_cast<LPVOID>(virtual_base + virtual_offset), length,
  272. MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS)) {
  273. LOG_CRITICAL(HW_Memory, "Failed to coalesce placeholders");
  274. }
  275. }
  276. void TrackPlaceholder(size_t virtual_offset, size_t host_offset, size_t length) {
  277. placeholders.insert({virtual_offset, virtual_offset + length});
  278. placeholder_host_pointers.emplace(virtual_offset, host_offset);
  279. }
  280. void UntrackPlaceholder(boost::icl::separate_interval_set<size_t>::iterator it) {
  281. placeholder_host_pointers.erase(it->lower());
  282. placeholders.erase(it);
  283. }
  284. /// Return true when a given memory region is a "nieche" and the placeholders don't have to be
  285. /// splitted.
  286. bool IsNiechePlaceholder(size_t virtual_offset, size_t length) const {
  287. const auto it = placeholders.upper_bound({virtual_offset, virtual_offset + length});
  288. if (it != placeholders.end() && it->lower() == virtual_offset + length) {
  289. return it == placeholders.begin() ? virtual_offset == 0
  290. : std::prev(it)->upper() == virtual_offset;
  291. }
  292. return false;
  293. }
  294. HANDLE process{}; ///< Current process handle
  295. HANDLE backing_handle{}; ///< File based backing memory
  296. DynamicLibrary kernelbase_dll;
  297. PFN_CreateFileMapping2 pfn_CreateFileMapping2{};
  298. PFN_VirtualAlloc2 pfn_VirtualAlloc2{};
  299. PFN_MapViewOfFile3 pfn_MapViewOfFile3{};
  300. PFN_UnmapViewOfFile2 pfn_UnmapViewOfFile2{};
  301. std::mutex placeholder_mutex; ///< Mutex for placeholders
  302. boost::icl::separate_interval_set<size_t> placeholders; ///< Mapped placeholders
  303. std::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset
  304. };
  305. #elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv
  306. class HostMemory::Impl {
  307. public:
  308. explicit Impl(size_t backing_size_, size_t virtual_size_)
  309. : backing_size{backing_size_}, virtual_size{virtual_size_} {
  310. bool good = false;
  311. SCOPE_EXIT({
  312. if (!good) {
  313. Release();
  314. }
  315. });
  316. long page_size = sysconf(_SC_PAGESIZE);
  317. if (page_size != 0x1000) {
  318. LOG_CRITICAL(HW_Memory, "page size {:#x} is incompatible with 4K paging", page_size);
  319. throw std::bad_alloc{};
  320. }
  321. // Backing memory initialization
  322. #if defined(__FreeBSD__) && __FreeBSD__ < 13
  323. // XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30
  324. fd = shm_open(SHM_ANON, O_RDWR, 0600);
  325. #else
  326. fd = memfd_create("HostMemory", 0);
  327. #endif
  328. if (fd == -1) {
  329. LOG_CRITICAL(HW_Memory, "memfd_create failed: {}", strerror(errno));
  330. throw std::bad_alloc{};
  331. }
  332. // Defined to extend the file with zeros
  333. int ret = ftruncate(fd, backing_size);
  334. if (ret != 0) {
  335. LOG_CRITICAL(HW_Memory, "ftruncate failed with {}, are you out-of-memory?",
  336. strerror(errno));
  337. throw std::bad_alloc{};
  338. }
  339. backing_base = static_cast<u8*>(
  340. mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
  341. if (backing_base == MAP_FAILED) {
  342. LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
  343. throw std::bad_alloc{};
  344. }
  345. // Virtual memory initialization
  346. virtual_base = static_cast<u8*>(
  347. mmap(nullptr, virtual_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
  348. if (virtual_base == MAP_FAILED) {
  349. LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
  350. throw std::bad_alloc{};
  351. }
  352. good = true;
  353. }
  354. ~Impl() {
  355. Release();
  356. }
  357. void Map(size_t virtual_offset, size_t host_offset, size_t length) {
  358. void* ret = mmap(virtual_base + virtual_offset, length, PROT_READ | PROT_WRITE,
  359. MAP_SHARED | MAP_FIXED, fd, host_offset);
  360. ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
  361. }
  362. void Unmap(size_t virtual_offset, size_t length) {
  363. // The method name is wrong. We're still talking about the virtual range.
  364. // We don't want to unmap, we want to reserve this memory.
  365. void* ret = mmap(virtual_base + virtual_offset, length, PROT_NONE,
  366. MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
  367. ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
  368. }
  369. void Protect(size_t virtual_offset, size_t length, bool read, bool write) {
  370. int flags = 0;
  371. if (read) {
  372. flags |= PROT_READ;
  373. }
  374. if (write) {
  375. flags |= PROT_WRITE;
  376. }
  377. int ret = mprotect(virtual_base + virtual_offset, length, flags);
  378. ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno));
  379. }
  380. const size_t backing_size; ///< Size of the backing memory in bytes
  381. const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
  382. u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)};
  383. u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)};
  384. private:
  385. /// Release all resources in the object
  386. void Release() {
  387. if (virtual_base != MAP_FAILED) {
  388. int ret = munmap(virtual_base, virtual_size);
  389. ASSERT_MSG(ret == 0, "munmap failed: {}", strerror(errno));
  390. }
  391. if (backing_base != MAP_FAILED) {
  392. int ret = munmap(backing_base, backing_size);
  393. ASSERT_MSG(ret == 0, "munmap failed: {}", strerror(errno));
  394. }
  395. if (fd != -1) {
  396. int ret = close(fd);
  397. ASSERT_MSG(ret == 0, "close failed: {}", strerror(errno));
  398. }
  399. }
  400. int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create
  401. };
  402. #else // ^^^ Linux ^^^ vvv Generic vvv
  403. class HostMemory::Impl {
  404. public:
  405. explicit Impl(size_t /*backing_size */, size_t /* virtual_size */) {
  406. // This is just a place holder.
  407. // Please implement fastmem in a propper way on your platform.
  408. throw std::bad_alloc{};
  409. }
  410. void Map(size_t virtual_offset, size_t host_offset, size_t length) {}
  411. void Unmap(size_t virtual_offset, size_t length) {}
  412. void Protect(size_t virtual_offset, size_t length, bool read, bool write) {}
  413. u8* backing_base{nullptr};
  414. u8* virtual_base{nullptr};
  415. };
  416. #endif // ^^^ Generic ^^^
  417. HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
  418. : backing_size(backing_size_), virtual_size(virtual_size_) {
  419. try {
  420. // Try to allocate a fastmem arena.
  421. // The implementation will fail with std::bad_alloc on errors.
  422. impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment),
  423. AlignUp(virtual_size, PageAlignment) +
  424. 3 * HugePageSize);
  425. backing_base = impl->backing_base;
  426. virtual_base = impl->virtual_base;
  427. if (virtual_base) {
  428. virtual_base += 2 * HugePageSize - 1;
  429. virtual_base -= reinterpret_cast<size_t>(virtual_base) & (HugePageSize - 1);
  430. virtual_base_offset = virtual_base - impl->virtual_base;
  431. }
  432. } catch (const std::bad_alloc&) {
  433. LOG_CRITICAL(HW_Memory,
  434. "Fastmem unavailable, falling back to VirtualBuffer for memory allocation");
  435. fallback_buffer = std::make_unique<Common::VirtualBuffer<u8>>(backing_size);
  436. backing_base = fallback_buffer->data();
  437. virtual_base = nullptr;
  438. }
  439. }
  440. HostMemory::~HostMemory() = default;
  441. HostMemory::HostMemory(HostMemory&&) noexcept = default;
  442. HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default;
  443. void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length) {
  444. ASSERT(virtual_offset % PageAlignment == 0);
  445. ASSERT(host_offset % PageAlignment == 0);
  446. ASSERT(length % PageAlignment == 0);
  447. ASSERT(virtual_offset + length <= virtual_size);
  448. ASSERT(host_offset + length <= backing_size);
  449. if (length == 0 || !virtual_base || !impl) {
  450. return;
  451. }
  452. impl->Map(virtual_offset + virtual_base_offset, host_offset, length);
  453. }
  454. void HostMemory::Unmap(size_t virtual_offset, size_t length) {
  455. ASSERT(virtual_offset % PageAlignment == 0);
  456. ASSERT(length % PageAlignment == 0);
  457. ASSERT(virtual_offset + length <= virtual_size);
  458. if (length == 0 || !virtual_base || !impl) {
  459. return;
  460. }
  461. impl->Unmap(virtual_offset + virtual_base_offset, length);
  462. }
  463. void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write) {
  464. ASSERT(virtual_offset % PageAlignment == 0);
  465. ASSERT(length % PageAlignment == 0);
  466. ASSERT(virtual_offset + length <= virtual_size);
  467. if (length == 0 || !virtual_base || !impl) {
  468. return;
  469. }
  470. impl->Protect(virtual_offset + virtual_base_offset, length, read, write);
  471. }
  472. } // namespace Common