memory.cpp 32 KB

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