host_memory.cpp 21 KB

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