memory.cpp 36 KB

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