host_memory.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  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. // Backing memory initialization
  317. #if defined(__FreeBSD__) && __FreeBSD__ < 13
  318. // XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30
  319. fd = shm_open(SHM_ANON, O_RDWR, 0600);
  320. #else
  321. fd = memfd_create("HostMemory", 0);
  322. #endif
  323. if (fd == -1) {
  324. LOG_CRITICAL(HW_Memory, "memfd_create failed: {}", strerror(errno));
  325. throw std::bad_alloc{};
  326. }
  327. // Defined to extend the file with zeros
  328. int ret = ftruncate(fd, backing_size);
  329. if (ret != 0) {
  330. LOG_CRITICAL(HW_Memory, "ftruncate failed with {}, are you out-of-memory?",
  331. strerror(errno));
  332. throw std::bad_alloc{};
  333. }
  334. backing_base = static_cast<u8*>(
  335. mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
  336. if (backing_base == MAP_FAILED) {
  337. LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
  338. throw std::bad_alloc{};
  339. }
  340. // Virtual memory initialization
  341. virtual_base = static_cast<u8*>(
  342. mmap(nullptr, virtual_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
  343. if (virtual_base == MAP_FAILED) {
  344. LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
  345. throw std::bad_alloc{};
  346. }
  347. good = true;
  348. }
  349. ~Impl() {
  350. Release();
  351. }
  352. void Map(size_t virtual_offset, size_t host_offset, size_t length) {
  353. void* ret = mmap(virtual_base + virtual_offset, length, PROT_READ | PROT_WRITE,
  354. MAP_SHARED | MAP_FIXED, fd, host_offset);
  355. ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
  356. }
  357. void Unmap(size_t virtual_offset, size_t length) {
  358. // The method name is wrong. We're still talking about the virtual range.
  359. // We don't want to unmap, we want to reserve this memory.
  360. void* ret = mmap(virtual_base + virtual_offset, length, PROT_NONE,
  361. MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
  362. ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
  363. }
  364. void Protect(size_t virtual_offset, size_t length, bool read, bool write) {
  365. int flags = 0;
  366. if (read) {
  367. flags |= PROT_READ;
  368. }
  369. if (write) {
  370. flags |= PROT_WRITE;
  371. }
  372. int ret = mprotect(virtual_base + virtual_offset, length, flags);
  373. ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno));
  374. }
  375. const size_t backing_size; ///< Size of the backing memory in bytes
  376. const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
  377. u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)};
  378. u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)};
  379. private:
  380. /// Release all resources in the object
  381. void Release() {
  382. if (virtual_base != MAP_FAILED) {
  383. int ret = munmap(virtual_base, virtual_size);
  384. ASSERT_MSG(ret == 0, "munmap failed: {}", strerror(errno));
  385. }
  386. if (backing_base != MAP_FAILED) {
  387. int ret = munmap(backing_base, backing_size);
  388. ASSERT_MSG(ret == 0, "munmap failed: {}", strerror(errno));
  389. }
  390. if (fd != -1) {
  391. int ret = close(fd);
  392. ASSERT_MSG(ret == 0, "close failed: {}", strerror(errno));
  393. }
  394. }
  395. int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create
  396. };
  397. #else // ^^^ Linux ^^^ vvv Generic vvv
  398. class HostMemory::Impl {
  399. public:
  400. explicit Impl(size_t /*backing_size */, size_t /* virtual_size */) {
  401. // This is just a place holder.
  402. // Please implement fastmem in a propper way on your platform.
  403. throw std::bad_alloc{};
  404. }
  405. void Map(size_t virtual_offset, size_t host_offset, size_t length) {}
  406. void Unmap(size_t virtual_offset, size_t length) {}
  407. void Protect(size_t virtual_offset, size_t length, bool read, bool write) {}
  408. u8* backing_base{nullptr};
  409. u8* virtual_base{nullptr};
  410. };
  411. #endif // ^^^ Generic ^^^
  412. HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
  413. : backing_size(backing_size_), virtual_size(virtual_size_) {
  414. try {
  415. // Try to allocate a fastmem arena.
  416. // The implementation will fail with std::bad_alloc on errors.
  417. impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment),
  418. AlignUp(virtual_size, PageAlignment) +
  419. 3 * HugePageSize);
  420. backing_base = impl->backing_base;
  421. virtual_base = impl->virtual_base;
  422. if (virtual_base) {
  423. virtual_base += 2 * HugePageSize - 1;
  424. virtual_base -= reinterpret_cast<size_t>(virtual_base) & (HugePageSize - 1);
  425. virtual_base_offset = virtual_base - impl->virtual_base;
  426. }
  427. } catch (const std::bad_alloc&) {
  428. LOG_CRITICAL(HW_Memory,
  429. "Fastmem unavailable, falling back to VirtualBuffer for memory allocation");
  430. fallback_buffer = std::make_unique<Common::VirtualBuffer<u8>>(backing_size);
  431. backing_base = fallback_buffer->data();
  432. virtual_base = nullptr;
  433. }
  434. }
  435. HostMemory::~HostMemory() = default;
  436. HostMemory::HostMemory(HostMemory&&) noexcept = default;
  437. HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default;
  438. void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length) {
  439. ASSERT(virtual_offset % PageAlignment == 0);
  440. ASSERT(host_offset % PageAlignment == 0);
  441. ASSERT(length % PageAlignment == 0);
  442. ASSERT(virtual_offset + length <= virtual_size);
  443. ASSERT(host_offset + length <= backing_size);
  444. if (length == 0 || !virtual_base || !impl) {
  445. return;
  446. }
  447. impl->Map(virtual_offset + virtual_base_offset, host_offset, length);
  448. }
  449. void HostMemory::Unmap(size_t virtual_offset, size_t length) {
  450. ASSERT(virtual_offset % PageAlignment == 0);
  451. ASSERT(length % PageAlignment == 0);
  452. ASSERT(virtual_offset + length <= virtual_size);
  453. if (length == 0 || !virtual_base || !impl) {
  454. return;
  455. }
  456. impl->Unmap(virtual_offset + virtual_base_offset, length);
  457. }
  458. void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write) {
  459. ASSERT(virtual_offset % PageAlignment == 0);
  460. ASSERT(length % PageAlignment == 0);
  461. ASSERT(virtual_offset + length <= virtual_size);
  462. if (length == 0 || !virtual_base || !impl) {
  463. return;
  464. }
  465. impl->Protect(virtual_offset + virtual_base_offset, length, read, write);
  466. }
  467. } // namespace Common