memory.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790
  1. // SPDX-FileCopyrightText: 2015 Citra Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <algorithm>
  4. #include <cstring>
  5. #include "common/assert.h"
  6. #include "common/atomic_ops.h"
  7. #include "common/common_types.h"
  8. #include "common/logging/log.h"
  9. #include "common/page_table.h"
  10. #include "common/settings.h"
  11. #include "common/swap.h"
  12. #include "core/core.h"
  13. #include "core/device_memory.h"
  14. #include "core/hle/kernel/k_page_table.h"
  15. #include "core/hle/kernel/k_process.h"
  16. #include "core/memory.h"
  17. #include "video_core/gpu.h"
  18. namespace Core::Memory {
  19. // Implementation class used to keep the specifics of the memory subsystem hidden
  20. // from outside classes. This also allows modification to the internals of the memory
  21. // subsystem without needing to rebuild all files that make use of the memory interface.
  22. struct Memory::Impl {
  23. explicit Impl(Core::System& system_) : system{system_} {}
  24. void SetCurrentPageTable(Kernel::KProcess& process, u32 core_id) {
  25. current_page_table = &process.PageTable().PageTableImpl();
  26. current_page_table->fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer();
  27. const std::size_t address_space_width = process.PageTable().GetAddressSpaceWidth();
  28. system.ArmInterface(core_id).PageTableChanged(*current_page_table, address_space_width);
  29. }
  30. void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) {
  31. ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
  32. ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
  33. ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}", target);
  34. MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory);
  35. if (Settings::IsFastmemEnabled()) {
  36. system.DeviceMemory().buffer.Map(base, target - DramMemoryMap::Base, size);
  37. }
  38. }
  39. void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
  40. ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
  41. ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
  42. MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, 0, Common::PageType::Unmapped);
  43. if (Settings::IsFastmemEnabled()) {
  44. system.DeviceMemory().buffer.Unmap(base, size);
  45. }
  46. }
  47. [[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(VAddr vaddr) const {
  48. const PAddr paddr{current_page_table->backing_addr[vaddr >> PAGE_BITS]};
  49. if (!paddr) {
  50. return {};
  51. }
  52. return system.DeviceMemory().GetPointer(paddr) + vaddr;
  53. }
  54. [[nodiscard]] u8* GetPointerFromDebugMemory(VAddr vaddr) const {
  55. const PAddr paddr{current_page_table->backing_addr[vaddr >> PAGE_BITS]};
  56. if (paddr == 0) {
  57. return {};
  58. }
  59. return system.DeviceMemory().GetPointer(paddr) + vaddr;
  60. }
  61. u8 Read8(const VAddr addr) {
  62. return Read<u8>(addr);
  63. }
  64. u16 Read16(const VAddr addr) {
  65. if ((addr & 1) == 0) {
  66. return Read<u16_le>(addr);
  67. } else {
  68. const u32 a{Read<u8>(addr)};
  69. const u32 b{Read<u8>(addr + sizeof(u8))};
  70. return static_cast<u16>((b << 8) | a);
  71. }
  72. }
  73. u32 Read32(const VAddr addr) {
  74. if ((addr & 3) == 0) {
  75. return Read<u32_le>(addr);
  76. } else {
  77. const u32 a{Read16(addr)};
  78. const u32 b{Read16(addr + sizeof(u16))};
  79. return (b << 16) | a;
  80. }
  81. }
  82. u64 Read64(const VAddr addr) {
  83. if ((addr & 7) == 0) {
  84. return Read<u64_le>(addr);
  85. } else {
  86. const u32 a{Read32(addr)};
  87. const u32 b{Read32(addr + sizeof(u32))};
  88. return (static_cast<u64>(b) << 32) | a;
  89. }
  90. }
  91. void Write8(const VAddr addr, const u8 data) {
  92. Write<u8>(addr, data);
  93. }
  94. void Write16(const VAddr addr, const u16 data) {
  95. if ((addr & 1) == 0) {
  96. Write<u16_le>(addr, data);
  97. } else {
  98. Write<u8>(addr, static_cast<u8>(data));
  99. Write<u8>(addr + sizeof(u8), static_cast<u8>(data >> 8));
  100. }
  101. }
  102. void Write32(const VAddr addr, const u32 data) {
  103. if ((addr & 3) == 0) {
  104. Write<u32_le>(addr, data);
  105. } else {
  106. Write16(addr, static_cast<u16>(data));
  107. Write16(addr + sizeof(u16), static_cast<u16>(data >> 16));
  108. }
  109. }
  110. void Write64(const VAddr addr, const u64 data) {
  111. if ((addr & 7) == 0) {
  112. Write<u64_le>(addr, data);
  113. } else {
  114. Write32(addr, static_cast<u32>(data));
  115. Write32(addr + sizeof(u32), static_cast<u32>(data >> 32));
  116. }
  117. }
  118. bool WriteExclusive8(const VAddr addr, const u8 data, const u8 expected) {
  119. return WriteExclusive<u8>(addr, data, expected);
  120. }
  121. bool WriteExclusive16(const VAddr addr, const u16 data, const u16 expected) {
  122. return WriteExclusive<u16_le>(addr, data, expected);
  123. }
  124. bool WriteExclusive32(const VAddr addr, const u32 data, const u32 expected) {
  125. return WriteExclusive<u32_le>(addr, data, expected);
  126. }
  127. bool WriteExclusive64(const VAddr addr, const u64 data, const u64 expected) {
  128. return WriteExclusive<u64_le>(addr, data, expected);
  129. }
  130. std::string ReadCString(VAddr vaddr, std::size_t max_length) {
  131. std::string string;
  132. string.reserve(max_length);
  133. for (std::size_t i = 0; i < max_length; ++i) {
  134. const char c = Read<s8>(vaddr);
  135. if (c == '\0') {
  136. break;
  137. }
  138. string.push_back(c);
  139. ++vaddr;
  140. }
  141. string.shrink_to_fit();
  142. return string;
  143. }
  144. void WalkBlock(const Kernel::KProcess& process, const VAddr addr, const std::size_t size,
  145. auto on_unmapped, auto on_memory, auto on_rasterizer, auto increment) {
  146. const auto& page_table = process.PageTable().PageTableImpl();
  147. std::size_t remaining_size = size;
  148. std::size_t page_index = addr >> PAGE_BITS;
  149. std::size_t page_offset = addr & PAGE_MASK;
  150. while (remaining_size) {
  151. const std::size_t copy_amount =
  152. std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
  153. const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
  154. const auto [pointer, type] = page_table.pointers[page_index].PointerType();
  155. switch (type) {
  156. case Common::PageType::Unmapped: {
  157. on_unmapped(copy_amount, current_vaddr);
  158. break;
  159. }
  160. case Common::PageType::Memory: {
  161. DEBUG_ASSERT(pointer);
  162. u8* mem_ptr = pointer + page_offset + (page_index << PAGE_BITS);
  163. on_memory(copy_amount, mem_ptr);
  164. break;
  165. }
  166. case Common::PageType::DebugMemory: {
  167. DEBUG_ASSERT(pointer);
  168. u8* const mem_ptr{GetPointerFromDebugMemory(current_vaddr)};
  169. on_memory(copy_amount, mem_ptr);
  170. break;
  171. }
  172. case Common::PageType::RasterizerCachedMemory: {
  173. u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)};
  174. on_rasterizer(current_vaddr, copy_amount, host_ptr);
  175. break;
  176. }
  177. default:
  178. UNREACHABLE();
  179. }
  180. page_index++;
  181. page_offset = 0;
  182. increment(copy_amount);
  183. remaining_size -= copy_amount;
  184. }
  185. }
  186. template <bool UNSAFE>
  187. void ReadBlockImpl(const Kernel::KProcess& process, const VAddr src_addr, void* dest_buffer,
  188. const std::size_t size) {
  189. WalkBlock(
  190. process, src_addr, size,
  191. [src_addr, size, &dest_buffer](const std::size_t copy_amount,
  192. const VAddr current_vaddr) {
  193. LOG_ERROR(HW_Memory,
  194. "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
  195. current_vaddr, src_addr, size);
  196. std::memset(dest_buffer, 0, copy_amount);
  197. },
  198. [&dest_buffer](const std::size_t copy_amount, const u8* const src_ptr) {
  199. std::memcpy(dest_buffer, src_ptr, copy_amount);
  200. },
  201. [&system = system, &dest_buffer](const VAddr current_vaddr,
  202. const std::size_t copy_amount,
  203. const u8* const host_ptr) {
  204. if constexpr (!UNSAFE) {
  205. system.GPU().FlushRegion(current_vaddr, copy_amount);
  206. }
  207. std::memcpy(dest_buffer, host_ptr, copy_amount);
  208. },
  209. [&dest_buffer](const std::size_t copy_amount) {
  210. dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
  211. });
  212. }
  213. void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
  214. ReadBlockImpl<false>(*system.CurrentProcess(), src_addr, dest_buffer, size);
  215. }
  216. void ReadBlockUnsafe(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
  217. ReadBlockImpl<true>(*system.CurrentProcess(), src_addr, dest_buffer, size);
  218. }
  219. template <bool UNSAFE>
  220. void WriteBlockImpl(const Kernel::KProcess& process, const VAddr dest_addr,
  221. const void* src_buffer, const std::size_t size) {
  222. WalkBlock(
  223. process, dest_addr, size,
  224. [dest_addr, size](const std::size_t copy_amount, const VAddr current_vaddr) {
  225. LOG_ERROR(HW_Memory,
  226. "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
  227. current_vaddr, dest_addr, size);
  228. },
  229. [&src_buffer](const std::size_t copy_amount, u8* const dest_ptr) {
  230. std::memcpy(dest_ptr, src_buffer, copy_amount);
  231. },
  232. [&system = system, &src_buffer](const VAddr current_vaddr,
  233. const std::size_t copy_amount, u8* const host_ptr) {
  234. if constexpr (!UNSAFE) {
  235. system.GPU().InvalidateRegion(current_vaddr, copy_amount);
  236. }
  237. std::memcpy(host_ptr, src_buffer, copy_amount);
  238. },
  239. [&src_buffer](const std::size_t copy_amount) {
  240. src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
  241. });
  242. }
  243. void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) {
  244. WriteBlockImpl<false>(*system.CurrentProcess(), dest_addr, src_buffer, size);
  245. }
  246. void WriteBlockUnsafe(const VAddr dest_addr, const void* src_buffer, const std::size_t size) {
  247. WriteBlockImpl<true>(*system.CurrentProcess(), dest_addr, src_buffer, size);
  248. }
  249. void ZeroBlock(const Kernel::KProcess& process, const VAddr dest_addr, const std::size_t size) {
  250. WalkBlock(
  251. process, dest_addr, size,
  252. [dest_addr, size](const std::size_t copy_amount, const VAddr current_vaddr) {
  253. LOG_ERROR(HW_Memory,
  254. "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
  255. current_vaddr, dest_addr, size);
  256. },
  257. [](const std::size_t copy_amount, u8* const dest_ptr) {
  258. std::memset(dest_ptr, 0, copy_amount);
  259. },
  260. [&system = system](const VAddr current_vaddr, const std::size_t copy_amount,
  261. u8* const host_ptr) {
  262. system.GPU().InvalidateRegion(current_vaddr, copy_amount);
  263. std::memset(host_ptr, 0, copy_amount);
  264. },
  265. [](const std::size_t copy_amount) {});
  266. }
  267. void CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr src_addr,
  268. const std::size_t size) {
  269. WalkBlock(
  270. process, dest_addr, size,
  271. [this, &process, &dest_addr, &src_addr, size](const std::size_t copy_amount,
  272. const VAddr current_vaddr) {
  273. LOG_ERROR(HW_Memory,
  274. "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
  275. current_vaddr, src_addr, size);
  276. ZeroBlock(process, dest_addr, copy_amount);
  277. },
  278. [this, &process, &dest_addr](const std::size_t copy_amount, const u8* const src_ptr) {
  279. WriteBlockImpl<false>(process, dest_addr, src_ptr, copy_amount);
  280. },
  281. [this, &system = system, &process, &dest_addr](
  282. const VAddr current_vaddr, const std::size_t copy_amount, u8* const host_ptr) {
  283. system.GPU().FlushRegion(current_vaddr, copy_amount);
  284. WriteBlockImpl<false>(process, dest_addr, host_ptr, copy_amount);
  285. },
  286. [&dest_addr, &src_addr](const std::size_t copy_amount) {
  287. dest_addr += static_cast<VAddr>(copy_amount);
  288. src_addr += static_cast<VAddr>(copy_amount);
  289. });
  290. }
  291. void MarkRegionDebug(VAddr vaddr, u64 size, bool debug) {
  292. if (vaddr == 0) {
  293. return;
  294. }
  295. // Iterate over a contiguous CPU address space, marking/unmarking the region.
  296. // The region is at a granularity of CPU pages.
  297. const u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
  298. for (u64 i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
  299. const Common::PageType page_type{
  300. current_page_table->pointers[vaddr >> PAGE_BITS].Type()};
  301. if (debug) {
  302. // Switch page type to debug if now debug
  303. switch (page_type) {
  304. case Common::PageType::Unmapped:
  305. ASSERT_MSG(false, "Attempted to mark unmapped pages as debug");
  306. break;
  307. case Common::PageType::RasterizerCachedMemory:
  308. case Common::PageType::DebugMemory:
  309. // Page is already marked.
  310. break;
  311. case Common::PageType::Memory:
  312. current_page_table->pointers[vaddr >> PAGE_BITS].Store(
  313. nullptr, Common::PageType::DebugMemory);
  314. break;
  315. default:
  316. UNREACHABLE();
  317. }
  318. } else {
  319. // Switch page type to non-debug if now non-debug
  320. switch (page_type) {
  321. case Common::PageType::Unmapped:
  322. ASSERT_MSG(false, "Attempted to mark unmapped pages as non-debug");
  323. break;
  324. case Common::PageType::RasterizerCachedMemory:
  325. case Common::PageType::Memory:
  326. // Don't mess with already non-debug or rasterizer memory.
  327. break;
  328. case Common::PageType::DebugMemory: {
  329. u8* const pointer{GetPointerFromDebugMemory(vaddr & ~PAGE_MASK)};
  330. current_page_table->pointers[vaddr >> PAGE_BITS].Store(
  331. pointer - (vaddr & ~PAGE_MASK), Common::PageType::Memory);
  332. break;
  333. }
  334. default:
  335. UNREACHABLE();
  336. }
  337. }
  338. }
  339. }
  340. void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
  341. if (vaddr == 0) {
  342. return;
  343. }
  344. if (Settings::IsFastmemEnabled()) {
  345. const bool is_read_enable = Settings::IsGPULevelHigh() || !cached;
  346. system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached);
  347. }
  348. // Iterate over a contiguous CPU address space, which corresponds to the specified GPU
  349. // address space, marking the region as un/cached. The region is marked un/cached at a
  350. // granularity of CPU pages, hence why we iterate on a CPU page basis (note: GPU page size
  351. // is different). This assumes the specified GPU address region is contiguous as well.
  352. const u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
  353. for (u64 i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
  354. const Common::PageType page_type{
  355. current_page_table->pointers[vaddr >> PAGE_BITS].Type()};
  356. if (cached) {
  357. // Switch page type to cached if now cached
  358. switch (page_type) {
  359. case Common::PageType::Unmapped:
  360. // It is not necessary for a process to have this region mapped into its address
  361. // space, for example, a system module need not have a VRAM mapping.
  362. break;
  363. case Common::PageType::DebugMemory:
  364. case Common::PageType::Memory:
  365. current_page_table->pointers[vaddr >> PAGE_BITS].Store(
  366. nullptr, Common::PageType::RasterizerCachedMemory);
  367. break;
  368. case Common::PageType::RasterizerCachedMemory:
  369. // There can be more than one GPU region mapped per CPU region, so it's common
  370. // that this area is already marked as cached.
  371. break;
  372. default:
  373. UNREACHABLE();
  374. }
  375. } else {
  376. // Switch page type to uncached if now uncached
  377. switch (page_type) {
  378. case Common::PageType::Unmapped: // NOLINT(bugprone-branch-clone)
  379. // It is not necessary for a process to have this region mapped into its address
  380. // space, for example, a system module need not have a VRAM mapping.
  381. break;
  382. case Common::PageType::DebugMemory:
  383. case Common::PageType::Memory:
  384. // There can be more than one GPU region mapped per CPU region, so it's common
  385. // that this area is already unmarked as cached.
  386. break;
  387. case Common::PageType::RasterizerCachedMemory: {
  388. u8* const pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~PAGE_MASK)};
  389. if (pointer == nullptr) {
  390. // It's possible that this function has been called while updating the
  391. // pagetable after unmapping a VMA. In that case the underlying VMA will no
  392. // longer exist, and we should just leave the pagetable entry blank.
  393. current_page_table->pointers[vaddr >> PAGE_BITS].Store(
  394. nullptr, Common::PageType::Unmapped);
  395. } else {
  396. current_page_table->pointers[vaddr >> PAGE_BITS].Store(
  397. pointer - (vaddr & ~PAGE_MASK), Common::PageType::Memory);
  398. }
  399. break;
  400. }
  401. default:
  402. UNREACHABLE();
  403. }
  404. }
  405. }
  406. }
  407. /**
  408. * Maps a region of pages as a specific type.
  409. *
  410. * @param page_table The page table to use to perform the mapping.
  411. * @param base The base address to begin mapping at.
  412. * @param size The total size of the range in bytes.
  413. * @param target The target address to begin mapping from.
  414. * @param type The page type to map the memory as.
  415. */
  416. void MapPages(Common::PageTable& page_table, VAddr base, u64 size, PAddr target,
  417. Common::PageType type) {
  418. LOG_DEBUG(HW_Memory, "Mapping {:016X} onto {:016X}-{:016X}", target, base * PAGE_SIZE,
  419. (base + size) * PAGE_SIZE);
  420. // During boot, current_page_table might not be set yet, in which case we need not flush
  421. if (system.IsPoweredOn()) {
  422. auto& gpu = system.GPU();
  423. for (u64 i = 0; i < size; i++) {
  424. const auto page = base + i;
  425. if (page_table.pointers[page].Type() == Common::PageType::RasterizerCachedMemory) {
  426. gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE);
  427. }
  428. }
  429. }
  430. const VAddr end = base + size;
  431. ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}",
  432. base + page_table.pointers.size());
  433. if (!target) {
  434. ASSERT_MSG(type != Common::PageType::Memory,
  435. "Mapping memory page without a pointer @ {:016x}", base * PAGE_SIZE);
  436. while (base != end) {
  437. page_table.pointers[base].Store(nullptr, type);
  438. page_table.backing_addr[base] = 0;
  439. base += 1;
  440. }
  441. } else {
  442. while (base != end) {
  443. page_table.pointers[base].Store(
  444. system.DeviceMemory().GetPointer(target) - (base << PAGE_BITS), type);
  445. page_table.backing_addr[base] = target - (base << PAGE_BITS);
  446. ASSERT_MSG(page_table.pointers[base].Pointer(),
  447. "memory mapping base yield a nullptr within the table");
  448. base += 1;
  449. target += PAGE_SIZE;
  450. }
  451. }
  452. }
  453. [[nodiscard]] u8* GetPointerImpl(VAddr vaddr, auto on_unmapped, auto on_rasterizer) const {
  454. // AARCH64 masks the upper 16 bit of all memory accesses
  455. vaddr &= 0xffffffffffffULL;
  456. if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) {
  457. on_unmapped();
  458. return nullptr;
  459. }
  460. // Avoid adding any extra logic to this fast-path block
  461. const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
  462. if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
  463. return &pointer[vaddr];
  464. }
  465. switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
  466. case Common::PageType::Unmapped:
  467. on_unmapped();
  468. return nullptr;
  469. case Common::PageType::Memory:
  470. ASSERT_MSG(false, "Mapped memory page without a pointer @ 0x{:016X}", vaddr);
  471. return nullptr;
  472. case Common::PageType::DebugMemory:
  473. return GetPointerFromDebugMemory(vaddr);
  474. case Common::PageType::RasterizerCachedMemory: {
  475. u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
  476. on_rasterizer();
  477. return host_ptr;
  478. }
  479. default:
  480. UNREACHABLE();
  481. }
  482. return nullptr;
  483. }
  484. [[nodiscard]] u8* GetPointer(const VAddr vaddr) const {
  485. return GetPointerImpl(
  486. vaddr, [vaddr]() { LOG_ERROR(HW_Memory, "Unmapped GetPointer @ 0x{:016X}", vaddr); },
  487. []() {});
  488. }
  489. /**
  490. * Reads a particular data type out of memory at the given virtual address.
  491. *
  492. * @param vaddr The virtual address to read the data type from.
  493. *
  494. * @tparam T The data type to read out of memory. This type *must* be
  495. * trivially copyable, otherwise the behavior of this function
  496. * is undefined.
  497. *
  498. * @returns The instance of T read from the specified virtual address.
  499. */
  500. template <typename T>
  501. T Read(VAddr vaddr) {
  502. T result = 0;
  503. const u8* const ptr = GetPointerImpl(
  504. vaddr,
  505. [vaddr]() {
  506. LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, vaddr);
  507. },
  508. [&system = system, vaddr]() { system.GPU().FlushRegion(vaddr, sizeof(T)); });
  509. if (ptr) {
  510. std::memcpy(&result, ptr, sizeof(T));
  511. }
  512. return result;
  513. }
  514. /**
  515. * Writes a particular data type to memory at the given virtual address.
  516. *
  517. * @param vaddr The virtual address to write the data type to.
  518. *
  519. * @tparam T The data type to write to memory. This type *must* be
  520. * trivially copyable, otherwise the behavior of this function
  521. * is undefined.
  522. */
  523. template <typename T>
  524. void Write(VAddr vaddr, const T data) {
  525. u8* const ptr = GetPointerImpl(
  526. vaddr,
  527. [vaddr, data]() {
  528. LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8,
  529. vaddr, static_cast<u64>(data));
  530. },
  531. [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); });
  532. if (ptr) {
  533. std::memcpy(ptr, &data, sizeof(T));
  534. }
  535. }
  536. template <typename T>
  537. bool WriteExclusive(VAddr vaddr, const T data, const T expected) {
  538. u8* const ptr = GetPointerImpl(
  539. vaddr,
  540. [vaddr, data]() {
  541. LOG_ERROR(HW_Memory, "Unmapped WriteExclusive{} @ 0x{:016X} = 0x{:016X}",
  542. sizeof(T) * 8, vaddr, static_cast<u64>(data));
  543. },
  544. [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); });
  545. if (ptr) {
  546. const auto volatile_pointer = reinterpret_cast<volatile T*>(ptr);
  547. return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
  548. }
  549. return true;
  550. }
  551. bool WriteExclusive128(VAddr vaddr, const u128 data, const u128 expected) {
  552. u8* const ptr = GetPointerImpl(
  553. vaddr,
  554. [vaddr, data]() {
  555. LOG_ERROR(HW_Memory, "Unmapped WriteExclusive128 @ 0x{:016X} = 0x{:016X}{:016X}",
  556. vaddr, static_cast<u64>(data[1]), static_cast<u64>(data[0]));
  557. },
  558. [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(u128)); });
  559. if (ptr) {
  560. const auto volatile_pointer = reinterpret_cast<volatile u64*>(ptr);
  561. return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
  562. }
  563. return true;
  564. }
  565. Common::PageTable* current_page_table = nullptr;
  566. Core::System& system;
  567. };
  568. Memory::Memory(Core::System& system_) : system{system_} {
  569. Reset();
  570. }
  571. Memory::~Memory() = default;
  572. void Memory::Reset() {
  573. impl = std::make_unique<Impl>(system);
  574. }
  575. void Memory::SetCurrentPageTable(Kernel::KProcess& process, u32 core_id) {
  576. impl->SetCurrentPageTable(process, core_id);
  577. }
  578. void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) {
  579. impl->MapMemoryRegion(page_table, base, size, target);
  580. }
  581. void Memory::UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
  582. impl->UnmapRegion(page_table, base, size);
  583. }
  584. bool Memory::IsValidVirtualAddress(const VAddr vaddr) const {
  585. const Kernel::KProcess& process = *system.CurrentProcess();
  586. const auto& page_table = process.PageTable().PageTableImpl();
  587. const size_t page = vaddr >> PAGE_BITS;
  588. if (page >= page_table.pointers.size()) {
  589. return false;
  590. }
  591. const auto [pointer, type] = page_table.pointers[page].PointerType();
  592. return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory ||
  593. type == Common::PageType::DebugMemory;
  594. }
  595. bool Memory::IsValidVirtualAddressRange(VAddr base, u64 size) const {
  596. VAddr end = base + size;
  597. VAddr page = Common::AlignDown(base, PAGE_SIZE);
  598. for (; page < end; page += PAGE_SIZE) {
  599. if (!IsValidVirtualAddress(page)) {
  600. return false;
  601. }
  602. }
  603. return true;
  604. }
  605. u8* Memory::GetPointer(VAddr vaddr) {
  606. return impl->GetPointer(vaddr);
  607. }
  608. const u8* Memory::GetPointer(VAddr vaddr) const {
  609. return impl->GetPointer(vaddr);
  610. }
  611. u8 Memory::Read8(const VAddr addr) {
  612. return impl->Read8(addr);
  613. }
  614. u16 Memory::Read16(const VAddr addr) {
  615. return impl->Read16(addr);
  616. }
  617. u32 Memory::Read32(const VAddr addr) {
  618. return impl->Read32(addr);
  619. }
  620. u64 Memory::Read64(const VAddr addr) {
  621. return impl->Read64(addr);
  622. }
  623. void Memory::Write8(VAddr addr, u8 data) {
  624. impl->Write8(addr, data);
  625. }
  626. void Memory::Write16(VAddr addr, u16 data) {
  627. impl->Write16(addr, data);
  628. }
  629. void Memory::Write32(VAddr addr, u32 data) {
  630. impl->Write32(addr, data);
  631. }
  632. void Memory::Write64(VAddr addr, u64 data) {
  633. impl->Write64(addr, data);
  634. }
  635. bool Memory::WriteExclusive8(VAddr addr, u8 data, u8 expected) {
  636. return impl->WriteExclusive8(addr, data, expected);
  637. }
  638. bool Memory::WriteExclusive16(VAddr addr, u16 data, u16 expected) {
  639. return impl->WriteExclusive16(addr, data, expected);
  640. }
  641. bool Memory::WriteExclusive32(VAddr addr, u32 data, u32 expected) {
  642. return impl->WriteExclusive32(addr, data, expected);
  643. }
  644. bool Memory::WriteExclusive64(VAddr addr, u64 data, u64 expected) {
  645. return impl->WriteExclusive64(addr, data, expected);
  646. }
  647. bool Memory::WriteExclusive128(VAddr addr, u128 data, u128 expected) {
  648. return impl->WriteExclusive128(addr, data, expected);
  649. }
  650. std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) {
  651. return impl->ReadCString(vaddr, max_length);
  652. }
  653. void Memory::ReadBlock(const Kernel::KProcess& process, const VAddr src_addr, void* dest_buffer,
  654. const std::size_t size) {
  655. impl->ReadBlockImpl<false>(process, src_addr, dest_buffer, size);
  656. }
  657. void Memory::ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
  658. impl->ReadBlock(src_addr, dest_buffer, size);
  659. }
  660. void Memory::ReadBlockUnsafe(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
  661. impl->ReadBlockUnsafe(src_addr, dest_buffer, size);
  662. }
  663. void Memory::WriteBlock(const Kernel::KProcess& process, VAddr dest_addr, const void* src_buffer,
  664. std::size_t size) {
  665. impl->WriteBlockImpl<false>(process, dest_addr, src_buffer, size);
  666. }
  667. void Memory::WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) {
  668. impl->WriteBlock(dest_addr, src_buffer, size);
  669. }
  670. void Memory::WriteBlockUnsafe(const VAddr dest_addr, const void* src_buffer,
  671. const std::size_t size) {
  672. impl->WriteBlockUnsafe(dest_addr, src_buffer, size);
  673. }
  674. void Memory::CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr src_addr,
  675. const std::size_t size) {
  676. impl->CopyBlock(process, dest_addr, src_addr, size);
  677. }
  678. void Memory::ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, const std::size_t size) {
  679. impl->ZeroBlock(process, dest_addr, size);
  680. }
  681. void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
  682. impl->RasterizerMarkRegionCached(vaddr, size, cached);
  683. }
  684. void Memory::MarkRegionDebug(VAddr vaddr, u64 size, bool debug) {
  685. impl->MarkRegionDebug(vaddr, size, debug);
  686. }
  687. } // namespace Core::Memory