memory.cpp 31 KB

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