yuzu/src/core/memory.cpp

558 lines
21 KiB
C++
Raw Normal View History

// Copyright 2015 Citra Emulator Project
2014-12-17 08:38:14 +03:00
// Licensed under GPLv2 or any later version
2014-04-09 03:15:46 +04:00
// Refer to the license.txt file included.
2013-09-19 07:52:51 +04:00
#include <algorithm>
2015-09-10 06:23:44 +03:00
#include <cstring>
#include <optional>
#include <utility>
#include "common/assert.h"
2015-05-06 10:06:12 +03:00
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/page_table.h"
2015-05-06 10:06:12 +03:00
#include "common/swap.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/memory.h"
#include "core/memory_setup.h"
#include "video_core/gpu.h"
2016-04-17 01:57:57 +03:00
2013-09-19 07:52:51 +04:00
namespace Memory {
static Common::PageTable* current_page_table = nullptr;
// Implementation class used to keep the specifics of the memory subsystem hidden
// from outside classes. This also allows modification to the internals of the memory
// subsystem without needing to rebuild all files that make use of the memory interface.
struct Memory::Impl {
explicit Impl(Core::System& system_) : system{system_} {}
Core::System& system;
};
Memory::Memory(Core::System& system) : impl{std::make_unique<Impl>(system)} {}
Memory::~Memory() = default;
core/cpu_core_manager: Create threads separately from initialization. Our initialization process is a little wonky than one would expect when it comes to code flow. We initialize the CPU last, as opposed to hardware, where the CPU obviously needs to be first, otherwise nothing else would work, and we have code that adds checks to get around this. For example, in the page table setting code, we check to see if the system is turned on before we even notify the CPU instances of a page table switch. This results in dead code (at the moment), because the only time a page table switch will occur is when the system is *not* running, preventing the emulated CPU instances from being notified of a page table switch in a convenient manner (technically the code path could be taken, but we don't emulate the process creation svc handlers yet). This moves the threads creation into its own member function of the core manager and restores a little order (and predictability) to our initialization process. Previously, in the multi-threaded cases, we'd kick off several threads before even the main kernel process was created and ready to execute (gross!). Now the initialization process is like so: Initialization: 1. Timers 2. CPU 3. Kernel 4. Filesystem stuff (kind of gross, but can be amended trivially) 5. Applet stuff (ditto in terms of being kind of gross) 6. Main process (will be moved into the loading step in a following change) 7. Telemetry (this should be initialized last in the future). 8. Services (4 and 5 should ideally be alongside this). 9. GDB (gross. Uses namespace scope state. Needs to be refactored into a class or booted altogether). 10. Renderer 11. GPU (will also have its threads created in a separate step in a following change). Which... isn't *ideal* per-se, however getting rid of the wonky intertwining of CPU state initialization out of this mix gets rid of most of the footguns when it comes to our initialization process.
2019-04-09 20:25:54 +03:00
void SetCurrentPageTable(Kernel::Process& process) {
current_page_table = &process.VMManager().page_table;
const std::size_t address_space_width = process.VMManager().GetAddressSpaceWidth();
2018-05-03 05:36:51 +03:00
auto& system = Core::System::GetInstance();
core/cpu_core_manager: Create threads separately from initialization. Our initialization process is a little wonky than one would expect when it comes to code flow. We initialize the CPU last, as opposed to hardware, where the CPU obviously needs to be first, otherwise nothing else would work, and we have code that adds checks to get around this. For example, in the page table setting code, we check to see if the system is turned on before we even notify the CPU instances of a page table switch. This results in dead code (at the moment), because the only time a page table switch will occur is when the system is *not* running, preventing the emulated CPU instances from being notified of a page table switch in a convenient manner (technically the code path could be taken, but we don't emulate the process creation svc handlers yet). This moves the threads creation into its own member function of the core manager and restores a little order (and predictability) to our initialization process. Previously, in the multi-threaded cases, we'd kick off several threads before even the main kernel process was created and ready to execute (gross!). Now the initialization process is like so: Initialization: 1. Timers 2. CPU 3. Kernel 4. Filesystem stuff (kind of gross, but can be amended trivially) 5. Applet stuff (ditto in terms of being kind of gross) 6. Main process (will be moved into the loading step in a following change) 7. Telemetry (this should be initialized last in the future). 8. Services (4 and 5 should ideally be alongside this). 9. GDB (gross. Uses namespace scope state. Needs to be refactored into a class or booted altogether). 10. Renderer 11. GPU (will also have its threads created in a separate step in a following change). Which... isn't *ideal* per-se, however getting rid of the wonky intertwining of CPU state initialization out of this mix gets rid of most of the footguns when it comes to our initialization process.
2019-04-09 20:25:54 +03:00
system.ArmInterface(0).PageTableChanged(*current_page_table, address_space_width);
system.ArmInterface(1).PageTableChanged(*current_page_table, address_space_width);
system.ArmInterface(2).PageTableChanged(*current_page_table, address_space_width);
system.ArmInterface(3).PageTableChanged(*current_page_table, address_space_width);
}
static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory,
Common::PageType type) {
2018-07-02 19:13:26 +03:00
LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE,
2018-07-02 19:20:50 +03:00
(base + size) * PAGE_SIZE);
// During boot, current_page_table might not be set yet, in which case we need not flush
if (Core::System::GetInstance().IsPoweredOn()) {
auto& gpu = Core::System::GetInstance().GPU();
for (u64 i = 0; i < size; i++) {
const auto page = base + i;
if (page_table.attributes[page] == Common::PageType::RasterizerCachedMemory) {
gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE);
}
}
}
VAddr end = base + size;
ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}",
base + page_table.pointers.size());
std::fill(page_table.attributes.begin() + base, page_table.attributes.begin() + end, type);
if (memory == nullptr) {
std::fill(page_table.pointers.begin() + base, page_table.pointers.begin() + end, memory);
} else {
while (base != end) {
page_table.pointers[base] = memory;
base += 1;
2015-07-10 04:47:27 +03:00
memory += PAGE_SIZE;
}
}
2013-09-19 07:52:51 +04:00
}
void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory);
}
2014-04-26 09:27:25 +04:00
void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
Common::MemoryHookPointer mmio_handler) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, Common::PageType::Special);
2016-01-30 21:41:04 +03:00
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
Common::SpecialRegion region{Common::SpecialRegion::Type::IODevice, std::move(mmio_handler)};
page_table.special_regions.add(
std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
}
void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, Common::PageType::Unmapped);
2016-04-17 01:57:57 +03:00
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
page_table.special_regions.erase(interval);
}
2016-04-17 01:57:57 +03:00
void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
Common::MemoryHookPointer hook) {
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)};
page_table.special_regions.add(
std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
2016-04-17 01:57:57 +03:00
}
void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
Common::MemoryHookPointer hook) {
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)};
page_table.special_regions.subtract(
std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
}
/**
* Gets a pointer to the exact memory at the virtual address (i.e. not page aligned)
* using a VMA from the current process
*/
static u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) {
const auto& vm_manager = process.VMManager();
const auto it = vm_manager.FindVMA(vaddr);
DEBUG_ASSERT(vm_manager.IsValidHandle(it));
u8* direct_pointer = nullptr;
const auto& vma = it->second;
switch (vma.type) {
case Kernel::VMAType::AllocatedMemoryBlock:
direct_pointer = vma.backing_block->data() + vma.offset;
break;
case Kernel::VMAType::BackingMemory:
direct_pointer = vma.backing_memory;
break;
case Kernel::VMAType::Free:
return nullptr;
default:
UNREACHABLE();
}
return direct_pointer + (vaddr - vma.base);
}
/**
* Gets a pointer to the exact memory at the virtual address (i.e. not page aligned)
* using a VMA from the current process.
*/
static u8* GetPointerFromVMA(VAddr vaddr) {
return GetPointerFromVMA(*Core::System::GetInstance().CurrentProcess(), vaddr);
}
2016-01-30 21:41:04 +03:00
template <typename T>
T Read(const VAddr vaddr) {
const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
if (page_pointer) {
// NOTE: Avoid adding any extra logic to this fast-path block
T value;
std::memcpy(&value, &page_pointer[vaddr & PAGE_MASK], sizeof(T));
return value;
}
Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
switch (type) {
case Common::PageType::Unmapped:
2018-07-02 19:13:26 +03:00
LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
return 0;
case Common::PageType::Memory:
ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
break;
case Common::PageType::RasterizerCachedMemory: {
auto host_ptr{GetPointerFromVMA(vaddr)};
Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), sizeof(T));
2016-04-17 01:57:57 +03:00
T value;
std::memcpy(&value, host_ptr, sizeof(T));
2016-04-17 01:57:57 +03:00
return value;
}
default:
UNREACHABLE();
}
return {};
2013-09-19 07:52:51 +04:00
}
template <typename T>
void Write(const VAddr vaddr, const T data) {
u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
if (page_pointer) {
// NOTE: Avoid adding any extra logic to this fast-path block
std::memcpy(&page_pointer[vaddr & PAGE_MASK], &data, sizeof(T));
return;
}
Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
switch (type) {
case Common::PageType::Unmapped:
2018-07-02 19:13:26 +03:00
LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
2018-07-02 19:20:50 +03:00
static_cast<u32>(data), vaddr);
return;
case Common::PageType::Memory:
ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
break;
case Common::PageType::RasterizerCachedMemory: {
auto host_ptr{GetPointerFromVMA(vaddr)};
Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T));
std::memcpy(host_ptr, &data, sizeof(T));
break;
2016-04-17 01:57:57 +03:00
}
default:
UNREACHABLE();
}
}
2014-04-26 09:27:25 +04:00
bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) {
const auto& page_table = process.VMManager().page_table;
const u8* page_pointer = page_table.pointers[vaddr >> PAGE_BITS];
if (page_pointer)
return true;
if (page_table.attributes[vaddr >> PAGE_BITS] == Common::PageType::RasterizerCachedMemory)
return true;
if (page_table.attributes[vaddr >> PAGE_BITS] != Common::PageType::Special)
return false;
return false;
}
bool IsValidVirtualAddress(const VAddr vaddr) {
return IsValidVirtualAddress(*Core::System::GetInstance().CurrentProcess(), vaddr);
}
bool IsKernelVirtualAddress(const VAddr vaddr) {
return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END;
}
u8* GetPointer(const VAddr vaddr) {
u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
if (page_pointer) {
return page_pointer + (vaddr & PAGE_MASK);
}
if (current_page_table->attributes[vaddr >> PAGE_BITS] ==
Common::PageType::RasterizerCachedMemory) {
return GetPointerFromVMA(vaddr);
}
2018-07-02 19:13:26 +03:00
LOG_ERROR(HW_Memory, "Unknown GetPointer @ 0x{:016X}", vaddr);
return nullptr;
}
2016-06-27 20:42:42 +03:00
std::string ReadCString(VAddr vaddr, std::size_t max_length) {
std::string string;
string.reserve(max_length);
for (std::size_t i = 0; i < max_length; ++i) {
char c = Read8(vaddr);
if (c == '\0')
break;
string.push_back(c);
++vaddr;
}
string.shrink_to_fit();
return string;
}
void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
if (vaddr == 0) {
return;
}
// Iterate over a contiguous CPU address space, which corresponds to the specified GPU address
// space, marking the region as un/cached. The region is marked un/cached at a granularity of
// CPU pages, hence why we iterate on a CPU page basis (note: GPU page size is different). This
// assumes the specified GPU address region is contiguous as well.
u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
for (unsigned i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
Common::PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
if (cached) {
// Switch page type to cached if now cached
switch (page_type) {
case Common::PageType::Unmapped:
// It is not necessary for a process to have this region mapped into its address
// space, for example, a system module need not have a VRAM mapping.
break;
case Common::PageType::Memory:
page_type = Common::PageType::RasterizerCachedMemory;
current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr;
break;
case Common::PageType::RasterizerCachedMemory:
// There can be more than one GPU region mapped per CPU region, so it's common that
// this area is already marked as cached.
break;
default:
UNREACHABLE();
}
} else {
// Switch page type to uncached if now uncached
switch (page_type) {
case Common::PageType::Unmapped:
// It is not necessary for a process to have this region mapped into its address
// space, for example, a system module need not have a VRAM mapping.
break;
case Common::PageType::Memory:
// There can be more than one GPU region mapped per CPU region, so it's common that
// this area is already unmarked as cached.
break;
case Common::PageType::RasterizerCachedMemory: {
u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK);
if (pointer == nullptr) {
// It's possible that this function has been called while updating the pagetable
// after unmapping a VMA. In that case the underlying VMA will no longer exist,
// and we should just leave the pagetable entry blank.
page_type = Common::PageType::Unmapped;
} else {
page_type = Common::PageType::Memory;
current_page_table->pointers[vaddr >> PAGE_BITS] = pointer;
}
break;
}
default:
UNREACHABLE();
}
}
}
}
u8 Read8(const VAddr addr) {
return Read<u8>(addr);
2013-09-19 07:52:51 +04:00
}
u16 Read16(const VAddr addr) {
return Read<u16_le>(addr);
2013-09-19 07:52:51 +04:00
}
u32 Read32(const VAddr addr) {
return Read<u32_le>(addr);
2013-09-19 07:52:51 +04:00
}
u64 Read64(const VAddr addr) {
return Read<u64_le>(addr);
2013-09-19 07:52:51 +04:00
}
void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer,
const std::size_t size) {
const auto& page_table = process.VMManager().page_table;
std::size_t remaining_size = size;
std::size_t page_index = src_addr >> PAGE_BITS;
std::size_t page_offset = src_addr & PAGE_MASK;
2016-04-16 11:14:18 +03:00
while (remaining_size > 0) {
const std::size_t copy_amount =
std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
2017-09-27 02:26:09 +03:00
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
2016-04-16 11:14:18 +03:00
switch (page_table.attributes[page_index]) {
case Common::PageType::Unmapped: {
2018-07-02 19:13:26 +03:00
LOG_ERROR(HW_Memory,
2018-07-02 19:20:50 +03:00
"Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, src_addr, size);
2016-04-16 11:14:18 +03:00
std::memset(dest_buffer, 0, copy_amount);
break;
}
case Common::PageType::Memory: {
DEBUG_ASSERT(page_table.pointers[page_index]);
2016-04-16 11:14:18 +03:00
const u8* src_ptr = page_table.pointers[page_index] + page_offset;
2016-04-16 11:14:18 +03:00
std::memcpy(dest_buffer, src_ptr, copy_amount);
break;
}
case Common::PageType::RasterizerCachedMemory: {
const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount);
std::memcpy(dest_buffer, host_ptr, copy_amount);
break;
}
2016-04-16 11:14:18 +03:00
default:
UNREACHABLE();
}
page_index++;
page_offset = 0;
dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
2016-04-16 11:14:18 +03:00
remaining_size -= copy_amount;
}
}
void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
ReadBlock(*Core::System::GetInstance().CurrentProcess(), src_addr, dest_buffer, size);
}
void Write8(const VAddr addr, const u8 data) {
Write<u8>(addr, data);
2013-09-19 07:52:51 +04:00
}
void Write16(const VAddr addr, const u16 data) {
Write<u16_le>(addr, data);
2013-09-19 07:52:51 +04:00
}
void Write32(const VAddr addr, const u32 data) {
Write<u32_le>(addr, data);
2013-09-19 07:52:51 +04:00
}
void Write64(const VAddr addr, const u64 data) {
Write<u64_le>(addr, data);
2013-09-19 07:52:51 +04:00
}
void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer,
const std::size_t size) {
const auto& page_table = process.VMManager().page_table;
std::size_t remaining_size = size;
std::size_t page_index = dest_addr >> PAGE_BITS;
std::size_t page_offset = dest_addr & PAGE_MASK;
2016-04-16 11:14:18 +03:00
while (remaining_size > 0) {
const std::size_t copy_amount =
std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
2017-09-27 02:26:09 +03:00
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
2016-04-16 11:14:18 +03:00
switch (page_table.attributes[page_index]) {
case Common::PageType::Unmapped: {
2018-07-02 19:13:26 +03:00
LOG_ERROR(HW_Memory,
2018-07-02 19:20:50 +03:00
"Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, dest_addr, size);
2016-04-16 11:14:18 +03:00
break;
}
case Common::PageType::Memory: {
DEBUG_ASSERT(page_table.pointers[page_index]);
2016-04-16 11:14:18 +03:00
u8* dest_ptr = page_table.pointers[page_index] + page_offset;
2016-04-16 11:14:18 +03:00
std::memcpy(dest_ptr, src_buffer, copy_amount);
break;
}
case Common::PageType::RasterizerCachedMemory: {
const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount);
std::memcpy(host_ptr, src_buffer, copy_amount);
break;
}
2016-04-16 11:14:18 +03:00
default:
UNREACHABLE();
}
page_index++;
page_offset = 0;
src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
2016-04-16 11:14:18 +03:00
remaining_size -= copy_amount;
2015-09-10 06:23:44 +03:00
}
}
void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) {
WriteBlock(*Core::System::GetInstance().CurrentProcess(), dest_addr, src_buffer, size);
}
void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) {
const auto& page_table = process.VMManager().page_table;
std::size_t remaining_size = size;
std::size_t page_index = dest_addr >> PAGE_BITS;
std::size_t page_offset = dest_addr & PAGE_MASK;
2016-04-16 12:21:41 +03:00
while (remaining_size > 0) {
const std::size_t copy_amount =
std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
2017-09-27 02:26:09 +03:00
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
2016-04-16 12:21:41 +03:00
switch (page_table.attributes[page_index]) {
case Common::PageType::Unmapped: {
2018-07-02 19:13:26 +03:00
LOG_ERROR(HW_Memory,
2018-07-02 19:20:50 +03:00
"Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, dest_addr, size);
2016-04-16 12:21:41 +03:00
break;
}
case Common::PageType::Memory: {
DEBUG_ASSERT(page_table.pointers[page_index]);
2016-04-16 12:21:41 +03:00
u8* dest_ptr = page_table.pointers[page_index] + page_offset;
2016-04-16 12:21:41 +03:00
std::memset(dest_ptr, 0, copy_amount);
break;
}
case Common::PageType::RasterizerCachedMemory: {
const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount);
std::memset(host_ptr, 0, copy_amount);
break;
}
2016-04-16 12:21:41 +03:00
default:
UNREACHABLE();
}
page_index++;
page_offset = 0;
remaining_size -= copy_amount;
}
}
void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
const std::size_t size) {
const auto& page_table = process.VMManager().page_table;
std::size_t remaining_size = size;
std::size_t page_index = src_addr >> PAGE_BITS;
std::size_t page_offset = src_addr & PAGE_MASK;
2016-04-16 17:22:45 +03:00
while (remaining_size > 0) {
const std::size_t copy_amount =
std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
2017-09-27 02:26:09 +03:00
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
2016-04-16 17:22:45 +03:00
switch (page_table.attributes[page_index]) {
case Common::PageType::Unmapped: {
2018-07-02 19:13:26 +03:00
LOG_ERROR(HW_Memory,
2018-07-02 19:20:50 +03:00
"Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, src_addr, size);
ZeroBlock(process, dest_addr, copy_amount);
2016-04-16 17:22:45 +03:00
break;
}
case Common::PageType::Memory: {
DEBUG_ASSERT(page_table.pointers[page_index]);
const u8* src_ptr = page_table.pointers[page_index] + page_offset;
WriteBlock(process, dest_addr, src_ptr, copy_amount);
break;
}
case Common::PageType::RasterizerCachedMemory: {
const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount);
WriteBlock(process, dest_addr, host_ptr, copy_amount);
2016-04-16 17:22:45 +03:00
break;
}
default:
UNREACHABLE();
}
page_index++;
page_offset = 0;
2017-09-27 02:26:09 +03:00
dest_addr += static_cast<VAddr>(copy_amount);
src_addr += static_cast<VAddr>(copy_amount);
2016-04-16 17:22:45 +03:00
remaining_size -= copy_amount;
}
}
void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) {
CopyBlock(*Core::System::GetInstance().CurrentProcess(), dest_addr, src_addr, size);
}
} // namespace Memory