host_memory.cpp 21 KB

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