memory.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  1. // Copyright 2015 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <array>
  5. #include <cstring>
  6. #include "common/assert.h"
  7. #include "common/common_types.h"
  8. #include "common/logging/log.h"
  9. #include "common/swap.h"
  10. #include "core/hle/kernel/process.h"
  11. #include "core/memory.h"
  12. #include "core/memory_setup.h"
  13. #include "core/mmio.h"
  14. #include "video_core/renderer_base.h"
  15. #include "video_core/video_core.h"
  16. namespace Memory {
  17. enum class PageType {
  18. /// Page is unmapped and should cause an access error.
  19. Unmapped,
  20. /// Page is mapped to regular memory. This is the only type you can get pointers to.
  21. Memory,
  22. /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
  23. /// invalidation
  24. RasterizerCachedMemory,
  25. /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
  26. Special,
  27. /// Page is mapped to a I/O region, but also needs to check for rasterizer cache flushing and
  28. /// invalidation
  29. RasterizerCachedSpecial,
  30. };
  31. struct SpecialRegion {
  32. VAddr base;
  33. u32 size;
  34. MMIORegionPointer handler;
  35. };
  36. /**
  37. * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
  38. * mimics the way a real CPU page table works, but instead is optimized for minimal decoding and
  39. * fetching requirements when accessing. In the usual case of an access to regular memory, it only
  40. * requires an indexed fetch and a check for NULL.
  41. */
  42. struct PageTable {
  43. /**
  44. * Array of memory pointers backing each page. An entry can only be non-null if the
  45. * corresponding entry in the `attributes` array is of type `Memory`.
  46. */
  47. std::array<u8*, PAGE_TABLE_NUM_ENTRIES> pointers;
  48. /**
  49. * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of
  50. * type `Special`.
  51. */
  52. std::vector<SpecialRegion> special_regions;
  53. /**
  54. * Array of fine grained page attributes. If it is set to any value other than `Memory`, then
  55. * the corresponding entry in `pointers` MUST be set to null.
  56. */
  57. std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes;
  58. /**
  59. * Indicates the number of externally cached resources touching a page that should be
  60. * flushed before the memory is accessed
  61. */
  62. std::array<u8, PAGE_TABLE_NUM_ENTRIES> cached_res_count;
  63. };
  64. /// Singular page table used for the singleton process
  65. static PageTable main_page_table;
  66. /// Currently active page table
  67. static PageTable* current_page_table = &main_page_table;
  68. std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers() {
  69. return &current_page_table->pointers;
  70. }
  71. static void MapPages(u32 base, u32 size, u8* memory, PageType type) {
  72. LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE,
  73. (base + size) * PAGE_SIZE);
  74. u32 end = base + size;
  75. while (base != end) {
  76. ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %08X", base);
  77. // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be
  78. // null here
  79. if (current_page_table->attributes[base] == PageType::RasterizerCachedMemory ||
  80. current_page_table->attributes[base] == PageType::RasterizerCachedSpecial) {
  81. RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(base << PAGE_BITS),
  82. PAGE_SIZE);
  83. }
  84. current_page_table->attributes[base] = type;
  85. current_page_table->pointers[base] = memory;
  86. current_page_table->cached_res_count[base] = 0;
  87. base += 1;
  88. if (memory != nullptr)
  89. memory += PAGE_SIZE;
  90. }
  91. }
  92. void InitMemoryMap() {
  93. main_page_table.pointers.fill(nullptr);
  94. main_page_table.attributes.fill(PageType::Unmapped);
  95. main_page_table.cached_res_count.fill(0);
  96. }
  97. void MapMemoryRegion(VAddr base, u32 size, u8* target) {
  98. ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size);
  99. ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base);
  100. MapPages(base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory);
  101. }
  102. void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler) {
  103. ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size);
  104. ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base);
  105. MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special);
  106. current_page_table->special_regions.emplace_back(SpecialRegion{base, size, mmio_handler});
  107. }
  108. void UnmapRegion(VAddr base, u32 size) {
  109. ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size);
  110. ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base);
  111. MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped);
  112. }
  113. /**
  114. * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned)
  115. * using a VMA from the current process
  116. */
  117. static u8* GetPointerFromVMA(VAddr vaddr) {
  118. u8* direct_pointer = nullptr;
  119. auto& vma = Kernel::g_current_process->vm_manager.FindVMA(vaddr)->second;
  120. switch (vma.type) {
  121. case Kernel::VMAType::AllocatedMemoryBlock:
  122. direct_pointer = vma.backing_block->data() + vma.offset;
  123. break;
  124. case Kernel::VMAType::BackingMemory:
  125. direct_pointer = vma.backing_memory;
  126. break;
  127. default:
  128. UNREACHABLE();
  129. }
  130. return direct_pointer + (vaddr - vma.base);
  131. }
  132. /**
  133. * This function should only be called for virtual addreses with attribute `PageType::Special`.
  134. */
  135. static MMIORegionPointer GetMMIOHandler(VAddr vaddr) {
  136. for (const auto& region : current_page_table->special_regions) {
  137. if (vaddr >= region.base && vaddr < (region.base + region.size)) {
  138. return region.handler;
  139. }
  140. }
  141. ASSERT_MSG(false, "Mapped IO page without a handler @ %08X", vaddr);
  142. return nullptr; // Should never happen
  143. }
  144. template <typename T>
  145. T ReadMMIO(MMIORegionPointer mmio_handler, VAddr addr);
  146. template <typename T>
  147. T Read(const VAddr vaddr) {
  148. const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
  149. if (page_pointer) {
  150. // NOTE: Avoid adding any extra logic to this fast-path block
  151. T value;
  152. std::memcpy(&value, &page_pointer[vaddr & PAGE_MASK], sizeof(T));
  153. return value;
  154. }
  155. PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
  156. switch (type) {
  157. case PageType::Unmapped:
  158. LOG_ERROR(HW_Memory, "unmapped Read%lu @ 0x%08X", sizeof(T) * 8, vaddr);
  159. return 0;
  160. case PageType::Memory:
  161. ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr);
  162. break;
  163. case PageType::RasterizerCachedMemory: {
  164. RasterizerFlushRegion(VirtualToPhysicalAddress(vaddr), sizeof(T));
  165. T value;
  166. std::memcpy(&value, GetPointerFromVMA(vaddr), sizeof(T));
  167. return value;
  168. }
  169. case PageType::Special:
  170. return ReadMMIO<T>(GetMMIOHandler(vaddr), vaddr);
  171. case PageType::RasterizerCachedSpecial: {
  172. RasterizerFlushRegion(VirtualToPhysicalAddress(vaddr), sizeof(T));
  173. return ReadMMIO<T>(GetMMIOHandler(vaddr), vaddr);
  174. }
  175. default:
  176. UNREACHABLE();
  177. }
  178. }
  179. template <typename T>
  180. void WriteMMIO(MMIORegionPointer mmio_handler, VAddr addr, const T data);
  181. template <typename T>
  182. void Write(const VAddr vaddr, const T data) {
  183. u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
  184. if (page_pointer) {
  185. // NOTE: Avoid adding any extra logic to this fast-path block
  186. std::memcpy(&page_pointer[vaddr & PAGE_MASK], &data, sizeof(T));
  187. return;
  188. }
  189. PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
  190. switch (type) {
  191. case PageType::Unmapped:
  192. LOG_ERROR(HW_Memory, "unmapped Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data,
  193. vaddr);
  194. return;
  195. case PageType::Memory:
  196. ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr);
  197. break;
  198. case PageType::RasterizerCachedMemory: {
  199. RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(vaddr), sizeof(T));
  200. std::memcpy(GetPointerFromVMA(vaddr), &data, sizeof(T));
  201. break;
  202. }
  203. case PageType::Special:
  204. WriteMMIO<T>(GetMMIOHandler(vaddr), vaddr, data);
  205. break;
  206. case PageType::RasterizerCachedSpecial: {
  207. RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(vaddr), sizeof(T));
  208. WriteMMIO<T>(GetMMIOHandler(vaddr), vaddr, data);
  209. break;
  210. }
  211. default:
  212. UNREACHABLE();
  213. }
  214. }
  215. bool IsValidVirtualAddress(const VAddr vaddr) {
  216. const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
  217. if (page_pointer)
  218. return true;
  219. if (current_page_table->attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory)
  220. return true;
  221. if (current_page_table->attributes[vaddr >> PAGE_BITS] != PageType::Special)
  222. return false;
  223. MMIORegionPointer mmio_region = GetMMIOHandler(vaddr);
  224. if (mmio_region) {
  225. return mmio_region->IsValidAddress(vaddr);
  226. }
  227. return false;
  228. }
  229. bool IsValidPhysicalAddress(const PAddr paddr) {
  230. return IsValidVirtualAddress(PhysicalToVirtualAddress(paddr));
  231. }
  232. u8* GetPointer(const VAddr vaddr) {
  233. u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
  234. if (page_pointer) {
  235. return page_pointer + (vaddr & PAGE_MASK);
  236. }
  237. if (current_page_table->attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory) {
  238. return GetPointerFromVMA(vaddr);
  239. }
  240. LOG_ERROR(HW_Memory, "unknown GetPointer @ 0x%08x", vaddr);
  241. return nullptr;
  242. }
  243. std::string ReadCString(VAddr vaddr, std::size_t max_length) {
  244. std::string string;
  245. string.reserve(max_length);
  246. for (std::size_t i = 0; i < max_length; ++i) {
  247. char c = Read8(vaddr);
  248. if (c == '\0')
  249. break;
  250. string.push_back(c);
  251. ++vaddr;
  252. }
  253. string.shrink_to_fit();
  254. return string;
  255. }
  256. u8* GetPhysicalPointer(PAddr address) {
  257. // TODO(Subv): This call should not go through the application's memory mapping.
  258. return GetPointer(PhysicalToVirtualAddress(address));
  259. }
  260. void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta) {
  261. if (start == 0) {
  262. return;
  263. }
  264. u32 num_pages = ((start + size - 1) >> PAGE_BITS) - (start >> PAGE_BITS) + 1;
  265. PAddr paddr = start;
  266. for (unsigned i = 0; i < num_pages; ++i) {
  267. VAddr vaddr = PhysicalToVirtualAddress(paddr);
  268. u8& res_count = current_page_table->cached_res_count[vaddr >> PAGE_BITS];
  269. ASSERT_MSG(count_delta <= UINT8_MAX - res_count,
  270. "Rasterizer resource cache counter overflow!");
  271. ASSERT_MSG(count_delta >= -res_count, "Rasterizer resource cache counter underflow!");
  272. // Switch page type to cached if now cached
  273. if (res_count == 0) {
  274. PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
  275. switch (page_type) {
  276. case PageType::Memory:
  277. page_type = PageType::RasterizerCachedMemory;
  278. current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr;
  279. break;
  280. case PageType::Special:
  281. page_type = PageType::RasterizerCachedSpecial;
  282. break;
  283. default:
  284. UNREACHABLE();
  285. }
  286. }
  287. res_count += count_delta;
  288. // Switch page type to uncached if now uncached
  289. if (res_count == 0) {
  290. PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
  291. switch (page_type) {
  292. case PageType::RasterizerCachedMemory:
  293. page_type = PageType::Memory;
  294. current_page_table->pointers[vaddr >> PAGE_BITS] =
  295. GetPointerFromVMA(vaddr & ~PAGE_MASK);
  296. break;
  297. case PageType::RasterizerCachedSpecial:
  298. page_type = PageType::Special;
  299. break;
  300. default:
  301. UNREACHABLE();
  302. }
  303. }
  304. paddr += PAGE_SIZE;
  305. }
  306. }
  307. void RasterizerFlushRegion(PAddr start, u32 size) {
  308. if (VideoCore::g_renderer != nullptr) {
  309. VideoCore::g_renderer->Rasterizer()->FlushRegion(start, size);
  310. }
  311. }
  312. void RasterizerFlushAndInvalidateRegion(PAddr start, u32 size) {
  313. if (VideoCore::g_renderer != nullptr) {
  314. VideoCore::g_renderer->Rasterizer()->FlushAndInvalidateRegion(start, size);
  315. }
  316. }
  317. u8 Read8(const VAddr addr) {
  318. return Read<u8>(addr);
  319. }
  320. u16 Read16(const VAddr addr) {
  321. return Read<u16_le>(addr);
  322. }
  323. u32 Read32(const VAddr addr) {
  324. return Read<u32_le>(addr);
  325. }
  326. u64 Read64(const VAddr addr) {
  327. return Read<u64_le>(addr);
  328. }
  329. void ReadBlock(const VAddr src_addr, void* dest_buffer, const size_t size) {
  330. size_t remaining_size = size;
  331. size_t page_index = src_addr >> PAGE_BITS;
  332. size_t page_offset = src_addr & PAGE_MASK;
  333. while (remaining_size > 0) {
  334. const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
  335. const VAddr current_vaddr = (page_index << PAGE_BITS) + page_offset;
  336. switch (current_page_table->attributes[page_index]) {
  337. case PageType::Unmapped: {
  338. LOG_ERROR(HW_Memory, "unmapped ReadBlock @ 0x%08X (start address = 0x%08X, size = %zu)",
  339. current_vaddr, src_addr, size);
  340. std::memset(dest_buffer, 0, copy_amount);
  341. break;
  342. }
  343. case PageType::Memory: {
  344. DEBUG_ASSERT(current_page_table->pointers[page_index]);
  345. const u8* src_ptr = current_page_table->pointers[page_index] + page_offset;
  346. std::memcpy(dest_buffer, src_ptr, copy_amount);
  347. break;
  348. }
  349. case PageType::Special: {
  350. DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
  351. GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, dest_buffer, copy_amount);
  352. break;
  353. }
  354. case PageType::RasterizerCachedMemory: {
  355. RasterizerFlushRegion(VirtualToPhysicalAddress(current_vaddr), copy_amount);
  356. std::memcpy(dest_buffer, GetPointerFromVMA(current_vaddr), copy_amount);
  357. break;
  358. }
  359. case PageType::RasterizerCachedSpecial: {
  360. DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
  361. RasterizerFlushRegion(VirtualToPhysicalAddress(current_vaddr), copy_amount);
  362. GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, dest_buffer, copy_amount);
  363. break;
  364. }
  365. default:
  366. UNREACHABLE();
  367. }
  368. page_index++;
  369. page_offset = 0;
  370. dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
  371. remaining_size -= copy_amount;
  372. }
  373. }
  374. void Write8(const VAddr addr, const u8 data) {
  375. Write<u8>(addr, data);
  376. }
  377. void Write16(const VAddr addr, const u16 data) {
  378. Write<u16_le>(addr, data);
  379. }
  380. void Write32(const VAddr addr, const u32 data) {
  381. Write<u32_le>(addr, data);
  382. }
  383. void Write64(const VAddr addr, const u64 data) {
  384. Write<u64_le>(addr, data);
  385. }
  386. void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size) {
  387. size_t remaining_size = size;
  388. size_t page_index = dest_addr >> PAGE_BITS;
  389. size_t page_offset = dest_addr & PAGE_MASK;
  390. while (remaining_size > 0) {
  391. const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
  392. const VAddr current_vaddr = (page_index << PAGE_BITS) + page_offset;
  393. switch (current_page_table->attributes[page_index]) {
  394. case PageType::Unmapped: {
  395. LOG_ERROR(HW_Memory,
  396. "unmapped WriteBlock @ 0x%08X (start address = 0x%08X, size = %zu)",
  397. current_vaddr, dest_addr, size);
  398. break;
  399. }
  400. case PageType::Memory: {
  401. DEBUG_ASSERT(current_page_table->pointers[page_index]);
  402. u8* dest_ptr = current_page_table->pointers[page_index] + page_offset;
  403. std::memcpy(dest_ptr, src_buffer, copy_amount);
  404. break;
  405. }
  406. case PageType::Special: {
  407. DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
  408. GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, src_buffer, copy_amount);
  409. break;
  410. }
  411. case PageType::RasterizerCachedMemory: {
  412. RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(current_vaddr),
  413. copy_amount);
  414. std::memcpy(GetPointerFromVMA(current_vaddr), src_buffer, copy_amount);
  415. break;
  416. }
  417. case PageType::RasterizerCachedSpecial: {
  418. DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
  419. RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(current_vaddr),
  420. copy_amount);
  421. GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, src_buffer, copy_amount);
  422. break;
  423. }
  424. default:
  425. UNREACHABLE();
  426. }
  427. page_index++;
  428. page_offset = 0;
  429. src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
  430. remaining_size -= copy_amount;
  431. }
  432. }
  433. void ZeroBlock(const VAddr dest_addr, const size_t size) {
  434. size_t remaining_size = size;
  435. size_t page_index = dest_addr >> PAGE_BITS;
  436. size_t page_offset = dest_addr & PAGE_MASK;
  437. static const std::array<u8, PAGE_SIZE> zeros = {};
  438. while (remaining_size > 0) {
  439. const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
  440. const VAddr current_vaddr = (page_index << PAGE_BITS) + page_offset;
  441. switch (current_page_table->attributes[page_index]) {
  442. case PageType::Unmapped: {
  443. LOG_ERROR(HW_Memory, "unmapped ZeroBlock @ 0x%08X (start address = 0x%08X, size = %zu)",
  444. current_vaddr, dest_addr, size);
  445. break;
  446. }
  447. case PageType::Memory: {
  448. DEBUG_ASSERT(current_page_table->pointers[page_index]);
  449. u8* dest_ptr = current_page_table->pointers[page_index] + page_offset;
  450. std::memset(dest_ptr, 0, copy_amount);
  451. break;
  452. }
  453. case PageType::Special: {
  454. DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
  455. GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, zeros.data(), copy_amount);
  456. break;
  457. }
  458. case PageType::RasterizerCachedMemory: {
  459. RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(current_vaddr),
  460. copy_amount);
  461. std::memset(GetPointerFromVMA(current_vaddr), 0, copy_amount);
  462. break;
  463. }
  464. case PageType::RasterizerCachedSpecial: {
  465. DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
  466. RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(current_vaddr),
  467. copy_amount);
  468. GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, zeros.data(), copy_amount);
  469. break;
  470. }
  471. default:
  472. UNREACHABLE();
  473. }
  474. page_index++;
  475. page_offset = 0;
  476. remaining_size -= copy_amount;
  477. }
  478. }
  479. void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) {
  480. size_t remaining_size = size;
  481. size_t page_index = src_addr >> PAGE_BITS;
  482. size_t page_offset = src_addr & PAGE_MASK;
  483. while (remaining_size > 0) {
  484. const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
  485. const VAddr current_vaddr = (page_index << PAGE_BITS) + page_offset;
  486. switch (current_page_table->attributes[page_index]) {
  487. case PageType::Unmapped: {
  488. LOG_ERROR(HW_Memory, "unmapped CopyBlock @ 0x%08X (start address = 0x%08X, size = %zu)",
  489. current_vaddr, src_addr, size);
  490. ZeroBlock(dest_addr, copy_amount);
  491. break;
  492. }
  493. case PageType::Memory: {
  494. DEBUG_ASSERT(current_page_table->pointers[page_index]);
  495. const u8* src_ptr = current_page_table->pointers[page_index] + page_offset;
  496. WriteBlock(dest_addr, src_ptr, copy_amount);
  497. break;
  498. }
  499. case PageType::Special: {
  500. DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
  501. std::vector<u8> buffer(copy_amount);
  502. GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, buffer.data(), buffer.size());
  503. WriteBlock(dest_addr, buffer.data(), buffer.size());
  504. break;
  505. }
  506. case PageType::RasterizerCachedMemory: {
  507. RasterizerFlushRegion(VirtualToPhysicalAddress(current_vaddr), copy_amount);
  508. WriteBlock(dest_addr, GetPointerFromVMA(current_vaddr), copy_amount);
  509. break;
  510. }
  511. case PageType::RasterizerCachedSpecial: {
  512. DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
  513. RasterizerFlushRegion(VirtualToPhysicalAddress(current_vaddr), copy_amount);
  514. std::vector<u8> buffer(copy_amount);
  515. GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, buffer.data(), buffer.size());
  516. WriteBlock(dest_addr, buffer.data(), buffer.size());
  517. break;
  518. }
  519. default:
  520. UNREACHABLE();
  521. }
  522. page_index++;
  523. page_offset = 0;
  524. dest_addr += copy_amount;
  525. src_addr += copy_amount;
  526. remaining_size -= copy_amount;
  527. }
  528. }
  529. template <>
  530. u8 ReadMMIO<u8>(MMIORegionPointer mmio_handler, VAddr addr) {
  531. return mmio_handler->Read8(addr);
  532. }
  533. template <>
  534. u16 ReadMMIO<u16>(MMIORegionPointer mmio_handler, VAddr addr) {
  535. return mmio_handler->Read16(addr);
  536. }
  537. template <>
  538. u32 ReadMMIO<u32>(MMIORegionPointer mmio_handler, VAddr addr) {
  539. return mmio_handler->Read32(addr);
  540. }
  541. template <>
  542. u64 ReadMMIO<u64>(MMIORegionPointer mmio_handler, VAddr addr) {
  543. return mmio_handler->Read64(addr);
  544. }
  545. template <>
  546. void WriteMMIO<u8>(MMIORegionPointer mmio_handler, VAddr addr, const u8 data) {
  547. mmio_handler->Write8(addr, data);
  548. }
  549. template <>
  550. void WriteMMIO<u16>(MMIORegionPointer mmio_handler, VAddr addr, const u16 data) {
  551. mmio_handler->Write16(addr, data);
  552. }
  553. template <>
  554. void WriteMMIO<u32>(MMIORegionPointer mmio_handler, VAddr addr, const u32 data) {
  555. mmio_handler->Write32(addr, data);
  556. }
  557. template <>
  558. void WriteMMIO<u64>(MMIORegionPointer mmio_handler, VAddr addr, const u64 data) {
  559. mmio_handler->Write64(addr, data);
  560. }
  561. PAddr VirtualToPhysicalAddress(const VAddr addr) {
  562. if (addr == 0) {
  563. return 0;
  564. } else if (addr >= VRAM_VADDR && addr < VRAM_VADDR_END) {
  565. return addr - VRAM_VADDR + VRAM_PADDR;
  566. } else if (addr >= LINEAR_HEAP_VADDR && addr < LINEAR_HEAP_VADDR_END) {
  567. return addr - LINEAR_HEAP_VADDR + FCRAM_PADDR;
  568. } else if (addr >= DSP_RAM_VADDR && addr < DSP_RAM_VADDR_END) {
  569. return addr - DSP_RAM_VADDR + DSP_RAM_PADDR;
  570. } else if (addr >= IO_AREA_VADDR && addr < IO_AREA_VADDR_END) {
  571. return addr - IO_AREA_VADDR + IO_AREA_PADDR;
  572. } else if (addr >= NEW_LINEAR_HEAP_VADDR && addr < NEW_LINEAR_HEAP_VADDR_END) {
  573. return addr - NEW_LINEAR_HEAP_VADDR + FCRAM_PADDR;
  574. }
  575. LOG_ERROR(HW_Memory, "Unknown virtual address @ 0x%08X", addr);
  576. // To help with debugging, set bit on address so that it's obviously invalid.
  577. return addr | 0x80000000;
  578. }
  579. VAddr PhysicalToVirtualAddress(const PAddr addr) {
  580. if (addr == 0) {
  581. return 0;
  582. } else if (addr >= VRAM_PADDR && addr < VRAM_PADDR_END) {
  583. return addr - VRAM_PADDR + VRAM_VADDR;
  584. } else if (addr >= FCRAM_PADDR && addr < FCRAM_PADDR_END) {
  585. return addr - FCRAM_PADDR + Kernel::g_current_process->GetLinearHeapAreaAddress();
  586. } else if (addr >= DSP_RAM_PADDR && addr < DSP_RAM_PADDR_END) {
  587. return addr - DSP_RAM_PADDR + DSP_RAM_VADDR;
  588. } else if (addr >= IO_AREA_PADDR && addr < IO_AREA_PADDR_END) {
  589. return addr - IO_AREA_PADDR + IO_AREA_VADDR;
  590. }
  591. LOG_ERROR(HW_Memory, "Unknown physical address @ 0x%08X", addr);
  592. // To help with debugging, set bit on address so that it's obviously invalid.
  593. return addr | 0x80000000;
  594. }
  595. } // namespace