memory.cpp 34 KB

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