memory.cpp 35 KB

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