From 38a5cc634ff8fc2811ec083f16918033d17ed9ef Mon Sep 17 00:00:00 2001 From: emufan4568 Date: Thu, 8 Sep 2022 22:47:37 +0300 Subject: [PATCH] core: memory: Refactor block functions and general cleanup * Also drop usage of std::vector in CopyBlock in favour of a plain std::array. Now all block functions use the common WalkBlockImpl the implementation of which is very similar to yuzu --- src/core/gdbstub/gdbstub.cpp | 12 +- src/core/hle/kernel/svc.cpp | 8 +- src/core/hle/kernel/thread.cpp | 3 +- src/core/memory.cpp | 414 +++++++++++++++---------------- src/core/memory.h | 257 +++++++++++++++++-- src/tests/core/memory/memory.cpp | 29 +-- 6 files changed, 459 insertions(+), 264 deletions(-) diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 37733339d..a37c5f905 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -849,14 +849,14 @@ static void ReadMemory() { SendReply("E01"); } - if (!Memory::IsValidVirtualAddress(*Core::System::GetInstance().Kernel().GetCurrentProcess(), + auto& memory = Core::System::GetInstance().Memory(); + if (!memory.IsValidVirtualAddress(*Core::System::GetInstance().Kernel().GetCurrentProcess(), addr)) { return SendReply("E00"); } std::vector data(len); - Core::System::GetInstance().Memory().ReadBlock( - *Core::System::GetInstance().Kernel().GetCurrentProcess(), addr, data.data(), len); + memory.ReadBlock(addr, data.data(), len); MemToGdbHex(reply, data.data(), len); reply[len * 2] = '\0'; @@ -873,7 +873,8 @@ static void WriteMemory() { auto len_pos = std::find(start_offset, command_buffer + command_length, ':'); u32 len = HexToInt(start_offset, static_cast(len_pos - start_offset)); - if (!Memory::IsValidVirtualAddress(*Core::System::GetInstance().Kernel().GetCurrentProcess(), + auto& memory = Core::System::GetInstance().Memory(); + if (!memory.IsValidVirtualAddress(*Core::System::GetInstance().Kernel().GetCurrentProcess(), addr)) { return SendReply("E00"); } @@ -881,8 +882,7 @@ static void WriteMemory() { std::vector data(len); GdbHexToMem(data.data(), len_pos + 1, len); - Core::System::GetInstance().Memory().WriteBlock( - *Core::System::GetInstance().Kernel().GetCurrentProcess(), addr, data.data(), len); + memory.WriteBlock(addr, data.data(), len); Core::GetRunningCore().ClearInstructionCache(); SendReply("OK"); } diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index b649d1581..e38791145 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -10,7 +10,6 @@ #include "common/logging/log.h" #include "common/microprofile.h" #include "common/scm_rev.h" -#include "common/scope_exit.h" #include "core/arm/arm_interface.h" #include "core/core.h" #include "core/core_timing.h" @@ -39,7 +38,6 @@ #include "core/hle/kernel/wait_object.h" #include "core/hle/lock.h" #include "core/hle/result.h" -#include "core/hle/service/service.h" namespace Kernel { @@ -374,7 +372,7 @@ ResultCode SVC::UnmapMemoryBlock(Handle handle, u32 addr) { /// Connect to an OS service given the port name, returns the handle to the port to out ResultCode SVC::ConnectToPort(Handle* out_handle, VAddr port_name_address) { - if (!Memory::IsValidVirtualAddress(*kernel.GetCurrentProcess(), port_name_address)) + if (!memory.IsValidVirtualAddress(*kernel.GetCurrentProcess(), port_name_address)) return ERR_NOT_FOUND; static constexpr std::size_t PortNameMaxLength = 11; @@ -541,7 +539,7 @@ ResultCode SVC::WaitSynchronizationN(s32* out, VAddr handles_address, s32 handle bool wait_all, s64 nano_seconds) { Thread* thread = kernel.GetCurrentThreadManager().GetCurrentThread(); - if (!Memory::IsValidVirtualAddress(*kernel.GetCurrentProcess(), handles_address)) + if (!memory.IsValidVirtualAddress(*kernel.GetCurrentProcess(), handles_address)) return ERR_INVALID_POINTER; // NOTE: on real hardware, there is no nullptr check for 'out' (tested with firmware 4.4). If @@ -687,7 +685,7 @@ static ResultCode ReceiveIPCRequest(Kernel::KernelSystem& kernel, Memory::Memory /// In a single operation, sends a IPC reply and waits for a new request. ResultCode SVC::ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_count, Handle reply_target) { - if (!Memory::IsValidVirtualAddress(*kernel.GetCurrentProcess(), handles_address)) + if (!memory.IsValidVirtualAddress(*kernel.GetCurrentProcess(), handles_address)) return ERR_INVALID_POINTER; // Check if 'handle_count' is invalid diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 0d1d8d6ff..a354aff71 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -337,8 +337,7 @@ ResultVal> KernelSystem::CreateThread( } // TODO(yuriks): Other checks, returning 0xD9001BEA - - if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) { + if (!memory.IsValidVirtualAddress(*owner_process, entry_point)) { LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:08x}", name, entry_point); // TODO: Verify error return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 80e9688a9..481e04868 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include #include #include #include @@ -15,9 +14,7 @@ #include "core/arm/arm_interface.h" #include "core/core.h" #include "core/global.h" -#include "core/hle/kernel/memory.h" #include "core/hle/kernel/process.h" -#include "core/hle/lock.h" #include "core/memory.h" #include "core/settings.h" #include "video_core/renderer_base.h" @@ -145,6 +142,145 @@ public: } } + void WalkBlock(const Kernel::Process& process, const VAddr src_addr, const std::size_t size, + auto on_unmapped, auto on_memory, auto on_special, auto on_rasterizer, auto increment) { + auto& page_table = *process.vm_manager.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; + + while (remaining_size > 0) { + const std::size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size); + const VAddr current_vaddr = static_cast((page_index << PAGE_BITS) + page_offset); + + switch (page_table.attributes[page_index]) { + case PageType::Unmapped: { + on_unmapped(copy_amount, current_vaddr); + break; + } + case PageType::Memory: { + DEBUG_ASSERT(page_table.pointers[page_index]); + u8* const src_ptr = page_table.pointers[page_index] + page_offset; + on_memory(copy_amount, src_ptr); + break; + } + case PageType::Special: { + MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr); + DEBUG_ASSERT(handler); + on_special(handler, copy_amount, current_vaddr); + break; + } + case PageType::RasterizerCachedMemory: { + u8* const rasterizer_ptr = GetPointerForRasterizerCache(current_vaddr); + on_rasterizer(current_vaddr, copy_amount, rasterizer_ptr); + break; + } + default: + UNREACHABLE(); + } + + page_index++; + page_offset = 0; + increment(copy_amount); + remaining_size -= copy_amount; + } + } + + template + void ReadBlockImpl(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, + const std::size_t size) { + WalkBlock( + process, src_addr, size, + [src_addr, size, &dest_buffer](const std::size_t copy_amount, + const VAddr current_vaddr) { + LOG_ERROR(HW_Memory, + "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", + current_vaddr, src_addr, size); + std::memset(dest_buffer, 0, copy_amount); + }, + [&dest_buffer](const std::size_t copy_amount, const u8* const src_ptr) { + std::memcpy(dest_buffer, src_ptr, copy_amount); + }, + [&dest_buffer](MMIORegionPointer& handler, + const std::size_t copy_amount, + const VAddr current_vaddr) { + handler->ReadBlock(current_vaddr, dest_buffer, copy_amount); + }, + [&dest_buffer](const VAddr current_vaddr, + const std::size_t copy_amount, + const u8* const rasterizer_ptr) { + if constexpr (!UNSAFE) { + RasterizerFlushVirtualRegion(current_vaddr, static_cast(copy_amount), + FlushMode::Flush); + } + std::memcpy(dest_buffer, rasterizer_ptr, copy_amount); + }, + [&dest_buffer](const std::size_t copy_amount) { + dest_buffer = static_cast(dest_buffer) + copy_amount; + }); + } + + template + void WriteBlockImpl(const Kernel::Process& process, const VAddr dest_addr, + const void* src_buffer, const std::size_t size) { + WalkBlock( + process, dest_addr, size, + [dest_addr, size](const std::size_t copy_amount, const VAddr current_vaddr) { + LOG_ERROR(HW_Memory, + "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", + current_vaddr, dest_addr, size); + }, + [&src_buffer](const std::size_t copy_amount, u8* const dest_ptr) { + std::memcpy(dest_ptr, src_buffer, copy_amount); + }, + [&src_buffer](MMIORegionPointer& handler, + const std::size_t copy_amount, + const VAddr current_vaddr) { + handler->WriteBlock(current_vaddr, src_buffer, copy_amount); + }, + [&src_buffer](const VAddr current_vaddr, + const std::size_t copy_amount, u8* const host_ptr) { + if constexpr (!UNSAFE) { + RasterizerFlushVirtualRegion(current_vaddr, static_cast(copy_amount), + FlushMode::Invalidate); + } + std::memcpy(host_ptr, src_buffer, copy_amount); + }, + [&src_buffer](const std::size_t copy_amount) { + src_buffer = static_cast(src_buffer) + copy_amount; + }); + } + + MemoryRef GetPointerForRasterizerCache(VAddr addr) const { + if (addr >= LINEAR_HEAP_VADDR && addr < LINEAR_HEAP_VADDR_END) { + return {fcram_mem, addr - LINEAR_HEAP_VADDR}; + } + if (addr >= NEW_LINEAR_HEAP_VADDR && addr < NEW_LINEAR_HEAP_VADDR_END) { + return {fcram_mem, addr - NEW_LINEAR_HEAP_VADDR}; + } + if (addr >= VRAM_VADDR && addr < VRAM_VADDR_END) { + return {vram_mem, addr - VRAM_VADDR}; + } + + UNREACHABLE(); + return MemoryRef{}; + } + + /** + * This function should only be called for virtual addreses with attribute `PageType::Special`. + */ + MMIORegionPointer GetMMIOHandler(const PageTable& page_table, VAddr vaddr) { + for (const auto& region : page_table.special_regions) { + if (vaddr >= region.base && vaddr < (region.base + region.size)) { + return region.handler; + } + } + + ASSERT_MSG(false, "Mapped IO page without a handler @ {:08X}", vaddr); + return nullptr; // Should never happen + } + private: friend class boost::serialization::access; template @@ -267,16 +403,7 @@ void MemorySystem::UnmapRegion(PageTable& page_table, VAddr base, u32 size) { } MemoryRef MemorySystem::GetPointerForRasterizerCache(VAddr addr) const { - if (addr >= LINEAR_HEAP_VADDR && addr < LINEAR_HEAP_VADDR_END) { - return {impl->fcram_mem, addr - LINEAR_HEAP_VADDR}; - } - if (addr >= NEW_LINEAR_HEAP_VADDR && addr < NEW_LINEAR_HEAP_VADDR_END) { - return {impl->fcram_mem, addr - NEW_LINEAR_HEAP_VADDR}; - } - if (addr >= VRAM_VADDR && addr < VRAM_VADDR_END) { - return {impl->vram_mem, addr - VRAM_VADDR}; - } - UNREACHABLE(); + return impl->GetPointerForRasterizerCache(addr); } void MemorySystem::RegisterPageTable(std::shared_ptr page_table) { @@ -290,19 +417,6 @@ void MemorySystem::UnregisterPageTable(std::shared_ptr page_table) { } } -/** - * This function should only be called for virtual addreses with attribute `PageType::Special`. - */ -static MMIORegionPointer GetMMIOHandler(const PageTable& page_table, VAddr vaddr) { - for (const auto& region : page_table.special_regions) { - if (vaddr >= region.base && vaddr < (region.base + region.size)) { - return region.handler; - } - } - ASSERT_MSG(false, "Mapped IO page without a handler @ {:08X}", vaddr); - return nullptr; // Should never happen -} - template T ReadMMIO(MMIORegionPointer mmio_handler, VAddr addr); @@ -333,10 +447,12 @@ T MemorySystem::Read(const VAddr vaddr) { return value; } case PageType::Special: - return ReadMMIO(GetMMIOHandler(*impl->current_page_table, vaddr), vaddr); + return ReadMMIO(impl->GetMMIOHandler(*impl->current_page_table, vaddr), vaddr); default: UNREACHABLE(); } + + return T{}; } template @@ -366,14 +482,14 @@ void MemorySystem::Write(const VAddr vaddr, const T data) { break; } case PageType::Special: - WriteMMIO(GetMMIOHandler(*impl->current_page_table, vaddr), vaddr, data); + WriteMMIO(impl->GetMMIOHandler(*impl->current_page_table, vaddr), vaddr, data); break; default: UNREACHABLE(); } } -bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) { +bool MemorySystem::IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) { auto& page_table = *process.vm_manager.page_table; auto page_pointer = page_table.pointers[vaddr >> PAGE_BITS]; @@ -386,7 +502,7 @@ bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) { if (page_table.attributes[vaddr >> PAGE_BITS] != PageType::Special) return false; - MMIORegionPointer mmio_region = GetMMIOHandler(page_table, vaddr); + MMIORegionPointer mmio_region = impl->GetMMIOHandler(page_table, vaddr); if (mmio_region) { return mmio_region->IsValidAddress(vaddr); } @@ -395,7 +511,7 @@ bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) { } bool MemorySystem::IsValidPhysicalAddress(const PAddr paddr) const { - return GetPhysicalPointer(paddr) != nullptr; + return GetPhysicalRef(paddr); } PAddr MemorySystem::ClampPhysicalAddress(PAddr base, PAddr address) const { @@ -461,11 +577,14 @@ std::string MemorySystem::ReadCString(VAddr vaddr, std::size_t max_length) { string.reserve(max_length); for (std::size_t i = 0; i < max_length; ++i) { char c = Read8(vaddr); - if (c == '\0') + if (c == '\0') { break; + } + string.push_back(c); ++vaddr; } + string.shrink_to_fit(); return string; } @@ -474,10 +593,6 @@ u8* MemorySystem::GetPhysicalPointer(PAddr address) { return GetPhysicalRef(address); } -const u8* MemorySystem::GetPhysicalPointer(PAddr address) const { - return GetPhysicalRef(address); -} - MemoryRef MemorySystem::GetPhysicalRef(PAddr address) const { struct MemoryArea { PAddr paddr_base; @@ -694,53 +809,12 @@ u64 MemorySystem::Read64(const VAddr addr) { void MemorySystem::ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, const std::size_t size) { - auto& page_table = *process.vm_manager.page_table; + return impl->ReadBlockImpl(process, src_addr, dest_buffer, size); +} - std::size_t remaining_size = size; - std::size_t page_index = src_addr >> PAGE_BITS; - std::size_t page_offset = src_addr & PAGE_MASK; - - while (remaining_size > 0) { - const std::size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size); - const VAddr current_vaddr = static_cast((page_index << PAGE_BITS) + page_offset); - - switch (page_table.attributes[page_index]) { - case PageType::Unmapped: { - LOG_ERROR(HW_Memory, - "unmapped ReadBlock @ 0x{:08X} (start address = 0x{:08X}, size = {}) at PC " - "0x{:08X}", - current_vaddr, src_addr, size, Core::GetRunningCore().GetPC()); - std::memset(dest_buffer, 0, copy_amount); - break; - } - case PageType::Memory: { - DEBUG_ASSERT(page_table.pointers[page_index]); - - const u8* src_ptr = page_table.pointers[page_index] + page_offset; - std::memcpy(dest_buffer, src_ptr, copy_amount); - break; - } - case PageType::Special: { - MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr); - DEBUG_ASSERT(handler); - handler->ReadBlock(current_vaddr, dest_buffer, copy_amount); - break; - } - case PageType::RasterizerCachedMemory: { - RasterizerFlushVirtualRegion(current_vaddr, static_cast(copy_amount), - FlushMode::Flush); - std::memcpy(dest_buffer, GetPointerForRasterizerCache(current_vaddr), copy_amount); - break; - } - default: - UNREACHABLE(); - } - - page_index++; - page_offset = 0; - dest_buffer = static_cast(dest_buffer) + copy_amount; - remaining_size -= copy_amount; - } +void MemorySystem::ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) { + const auto& process = *Core::System::GetInstance().Kernel().GetCurrentProcess(); + return impl->ReadBlockImpl(process, src_addr, dest_buffer, size); } void MemorySystem::Write8(const VAddr addr, const u8 data) { @@ -761,101 +835,40 @@ void MemorySystem::Write64(const VAddr addr, const u64 data) { void MemorySystem::WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer, const std::size_t size) { - auto& page_table = *process.vm_manager.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; + return impl->WriteBlockImpl(process, dest_addr, src_buffer, size); +} - while (remaining_size > 0) { - const std::size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size); - const VAddr current_vaddr = static_cast((page_index << PAGE_BITS) + page_offset); - - switch (page_table.attributes[page_index]) { - case PageType::Unmapped: { - LOG_ERROR(HW_Memory, - "unmapped WriteBlock @ 0x{:08X} (start address = 0x{:08X}, size = {}) at PC " - "0x{:08X}", - current_vaddr, dest_addr, size, Core::GetRunningCore().GetPC()); - break; - } - case PageType::Memory: { - DEBUG_ASSERT(page_table.pointers[page_index]); - - u8* dest_ptr = page_table.pointers[page_index] + page_offset; - std::memcpy(dest_ptr, src_buffer, copy_amount); - break; - } - case PageType::Special: { - MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr); - DEBUG_ASSERT(handler); - handler->WriteBlock(current_vaddr, src_buffer, copy_amount); - break; - } - case PageType::RasterizerCachedMemory: { - RasterizerFlushVirtualRegion(current_vaddr, static_cast(copy_amount), - FlushMode::Invalidate); - std::memcpy(GetPointerForRasterizerCache(current_vaddr), src_buffer, copy_amount); - break; - } - default: - UNREACHABLE(); - } - - page_index++; - page_offset = 0; - src_buffer = static_cast(src_buffer) + copy_amount; - remaining_size -= copy_amount; - } +void MemorySystem::WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { + auto& process = *Core::System::GetInstance().Kernel().GetCurrentProcess(); + return impl->WriteBlockImpl(process, dest_addr, src_buffer, size); } void MemorySystem::ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) { - auto& page_table = *process.vm_manager.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; + static const std::array zeros{0}; - static const std::array zeros = {}; - - while (remaining_size > 0) { - const std::size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size); - const VAddr current_vaddr = static_cast((page_index << PAGE_BITS) + page_offset); - - switch (page_table.attributes[page_index]) { - case PageType::Unmapped: { + impl->WalkBlock( + process, dest_addr, size, + [dest_addr, size](const std::size_t copy_amount, const VAddr current_vaddr) { LOG_ERROR(HW_Memory, - "unmapped ZeroBlock @ 0x{:08X} (start address = 0x{:08X}, size = {}) at PC " - "0x{:08X}", - current_vaddr, dest_addr, size, Core::GetRunningCore().GetPC()); - break; - } - case PageType::Memory: { - DEBUG_ASSERT(page_table.pointers[page_index]); - - u8* dest_ptr = page_table.pointers[page_index] + page_offset; + "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", + current_vaddr, dest_addr, size); + }, + [](const std::size_t copy_amount, u8* const dest_ptr) { std::memset(dest_ptr, 0, copy_amount); - break; - } - case PageType::Special: { - MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr); - DEBUG_ASSERT(handler); + }, + [&zeros = zeros](MMIORegionPointer& handler, + const std::size_t copy_amount, + const VAddr current_vaddr) { handler->WriteBlock(current_vaddr, zeros.data(), copy_amount); - break; - } - case PageType::RasterizerCachedMemory: { + }, + [](const VAddr current_vaddr, const std::size_t copy_amount, + u8* const rasterizer_ptr) { RasterizerFlushVirtualRegion(current_vaddr, static_cast(copy_amount), FlushMode::Invalidate); - std::memset(GetPointerForRasterizerCache(current_vaddr), 0, copy_amount); - break; - } - default: - UNREACHABLE(); - } - - page_index++; - page_offset = 0; - remaining_size -= copy_amount; - } + std::memset(rasterizer_ptr, 0, copy_amount); + }, + [](const std::size_t copy_amount) {}); } void MemorySystem::CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr, @@ -866,55 +879,36 @@ void MemorySystem::CopyBlock(const Kernel::Process& process, VAddr dest_addr, VA void MemorySystem::CopyBlock(const Kernel::Process& dest_process, const Kernel::Process& src_process, VAddr dest_addr, VAddr src_addr, std::size_t size) { - auto& page_table = *src_process.vm_manager.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; + std::array copy_buffer{}; - while (remaining_size > 0) { - const std::size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size); - const VAddr current_vaddr = static_cast((page_index << PAGE_BITS) + page_offset); - - switch (page_table.attributes[page_index]) { - case PageType::Unmapped: { + impl->WalkBlock( + src_process, src_addr, size, + [this, &dest_process, &dest_addr, &src_addr, size](const std::size_t copy_amount, + const VAddr current_vaddr) { LOG_ERROR(HW_Memory, - "unmapped CopyBlock @ 0x{:08X} (start address = 0x{:08X}, size = {}) at PC " - "0x{:08X}", - current_vaddr, src_addr, size, Core::GetRunningCore().GetPC()); + "unmapped CopyBlock @ 0x{:08X} (start address = 0x{:08X}, size = {})", + current_vaddr, src_addr, size); ZeroBlock(dest_process, dest_addr, copy_amount); - break; - } - case PageType::Memory: { - DEBUG_ASSERT(page_table.pointers[page_index]); - const u8* src_ptr = page_table.pointers[page_index] + page_offset; - WriteBlock(dest_process, dest_addr, src_ptr, copy_amount); - break; - } - case PageType::Special: { - MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr); - DEBUG_ASSERT(handler); - std::vector buffer(copy_amount); - handler->ReadBlock(current_vaddr, buffer.data(), buffer.size()); - WriteBlock(dest_process, dest_addr, buffer.data(), buffer.size()); - break; - } - case PageType::RasterizerCachedMemory: { + }, + [this, &dest_process, &dest_addr](const std::size_t copy_amount, const u8* const src_ptr) { + impl->WriteBlockImpl(dest_process, dest_addr, src_ptr, copy_amount); + }, + [this, &dest_process, &dest_addr, ©_buffer](MMIORegionPointer& handler, + const std::size_t copy_amount, + const VAddr current_vaddr) { + handler->ReadBlock(current_vaddr, copy_buffer.data(), copy_amount); + impl->WriteBlockImpl(dest_process, dest_addr, copy_buffer.data(), copy_amount); + }, + [this, &dest_process, &dest_addr]( + const VAddr current_vaddr, const std::size_t copy_amount, u8* const rasterizer_ptr) { RasterizerFlushVirtualRegion(current_vaddr, static_cast(copy_amount), FlushMode::Flush); - WriteBlock(dest_process, dest_addr, GetPointerForRasterizerCache(current_vaddr), - copy_amount); - break; - } - default: - UNREACHABLE(); - } - - page_index++; - page_offset = 0; - dest_addr += static_cast(copy_amount); - src_addr += static_cast(copy_amount); - remaining_size -= copy_amount; - } + impl->WriteBlockImpl(dest_process, dest_addr, rasterizer_ptr, copy_amount); + }, + [&dest_addr, &src_addr](const std::size_t copy_amount) { + dest_addr += static_cast(copy_amount); + src_addr += static_cast(copy_amount); + }); } template <> diff --git a/src/core/memory.h b/src/core/memory.h index c2aef30aa..8ee3c1d60 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -3,15 +3,12 @@ // Refer to the license.txt file included. #pragma once - +#include #include #include -#include #include -#include #include #include -#include "common/common_types.h" #include "common/memory_ref.h" #include "core/mmio.h" @@ -34,10 +31,10 @@ namespace Memory { * Page size used by the ARM architecture. This is the smallest granularity with which memory can * be mapped. */ -const u32 PAGE_SIZE = 0x1000; -const u32 PAGE_MASK = PAGE_SIZE - 1; -const int PAGE_BITS = 12; -const std::size_t PAGE_TABLE_NUM_ENTRIES = 1 << (32 - PAGE_BITS); +constexpr u32 PAGE_SIZE = 0x1000; +constexpr u32 PAGE_MASK = PAGE_SIZE - 1; +constexpr int PAGE_BITS = 12; +constexpr std::size_t PAGE_TABLE_NUM_ENTRIES = 1 << (32 - PAGE_BITS); enum class PageType { /// Page is unmapped and should cause an access error. @@ -106,11 +103,10 @@ struct PageTable { private: std::array raw; - std::array refs; - friend struct PageTable; }; + Pointers pointers; /** @@ -317,38 +313,258 @@ public: void SetCurrentPageTable(std::shared_ptr page_table); std::shared_ptr GetCurrentPageTable() const; + /** + * Gets a pointer to the given address. + * + * @param vaddr Virtual address to retrieve a pointer to. + * + * @returns The pointer to the given address, if the address is valid. + * If the address is not valid, nullptr will be returned. + */ + u8* GetPointer(VAddr vaddr); + + /** + * Gets a pointer to the given address. + * + * @param vaddr Virtual address to retrieve a pointer to. + * + * @returns The pointer to the given address, if the address is valid. + * If the address is not valid, nullptr will be returned. + */ + const u8* GetPointer(VAddr vaddr) const; + + /** + * Reads an 8-bit unsigned value from the current process' address space + * at the given virtual address. + * + * @param addr The virtual address to read the 8-bit value from. + * + * @returns the read 8-bit unsigned value. + */ u8 Read8(VAddr addr); + + /** + * Reads a 16-bit unsigned value from the current process' address space + * at the given virtual address. + * + * @param addr The virtual address to read the 16-bit value from. + * + * @returns the read 16-bit unsigned value. + */ u16 Read16(VAddr addr); + + /** + * Reads a 32-bit unsigned value from the current process' address space + * at the given virtual address. + * + * @param addr The virtual address to read the 32-bit value from. + * + * @returns the read 32-bit unsigned value. + */ u32 Read32(VAddr addr); + + /** + * Reads a 64-bit unsigned value from the current process' address space + * at the given virtual address. + * + * @param addr The virtual address to read the 64-bit value from. + * + * @returns the read 64-bit value. + */ u64 Read64(VAddr addr); + /** + * Writes an 8-bit unsigned integer to the given virtual address in + * the current process' address space. + * + * @param addr The virtual address to write the 8-bit unsigned integer to. + * @param data The 8-bit unsigned integer to write to the given virtual address. + * + * @post The memory at the given virtual address contains the specified data value. + */ void Write8(VAddr addr, u8 data); + + /** + * Writes a 16-bit unsigned integer to the given virtual address in + * the current process' address space. + * + * @param addr The virtual address to write the 16-bit unsigned integer to. + * @param data The 16-bit unsigned integer to write to the given virtual address. + * + * @post The memory range [addr, sizeof(data)) contains the given data value. + */ void Write16(VAddr addr, u16 data); + + /** + * Writes a 32-bit unsigned integer to the given virtual address in + * the current process' address space. + * + * @param addr The virtual address to write the 32-bit unsigned integer to. + * @param data The 32-bit unsigned integer to write to the given virtual address. + * + * @post The memory range [addr, sizeof(data)) contains the given data value. + */ void Write32(VAddr addr, u32 data); + + /** + * Writes a 64-bit unsigned integer to the given virtual address in + * the current process' address space. + * + * @param addr The virtual address to write the 64-bit unsigned integer to. + * @param data The 64-bit unsigned integer to write to the given virtual address. + * + * @post The memory range [addr, sizeof(data)) contains the given data value. + */ void Write64(VAddr addr, u64 data); + /** + * Reads a null-terminated string from the given virtual address. + * This function will continually read characters until either: + * + * - A null character ('\0') is reached. + * - max_length characters have been read. + * + * @note The final null-terminating character (if found) is not included + * in the returned string. + * + * @param vaddr The address to begin reading the string from. + * @param max_length The maximum length of the string to read in characters. + * + * @returns The read string. + */ + std::string ReadCString(VAddr vaddr, std::size_t max_length); + + + /** + * Reads a contiguous block of bytes from a specified process' address space. + * + * @param process The process to read the data from. + * @param src_addr The virtual address to begin reading from. + * @param dest_buffer The buffer to place the read bytes into. + * @param size The amount of data to read, in bytes. + * + * @note If a size of 0 is specified, then this function reads nothing and + * no attempts to access memory are made at all. + * + * @pre dest_buffer must be at least size bytes in length, otherwise a + * buffer overrun will occur. + * + * @post The range [dest_buffer, size) contains the read bytes from the + * process' address space. + */ void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, std::size_t size); + + /** + * Reads a contiguous block of bytes from the current process' address space. + * + * @param src_addr The virtual address to begin reading from. + * @param dest_buffer The buffer to place the read bytes into. + * @param size The amount of data to read, in bytes. + * + * @note If a size of 0 is specified, then this function reads nothing and + * no attempts to access memory are made at all. + * + * @pre dest_buffer must be at least size bytes in length, otherwise a + * buffer overrun will occur. + * + * @post The range [dest_buffer, size) contains the read bytes from the + * current process' address space. + */ + void ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size); + + /** + * Writes a range of bytes into a given process' address space at the specified + * virtual address. + * + * @param process The process to write data into the address space of. + * @param dest_addr The destination virtual address to begin writing the data at. + * @param src_buffer The data to write into the process' address space. + * @param size The size of the data to write, in bytes. + * + * @post The address range [dest_addr, size) in the process' address space + * contains the data that was within src_buffer. + * + * @post If an attempt is made to write into an unmapped region of memory, the writes + * will be ignored and an error will be logged. + * + * @post If a write is performed into a region of memory that is considered cached + * rasterizer memory, will cause the currently active rasterizer to be notified + * and will mark that region as invalidated to caches that the active + * graphics backend may be maintaining over the course of execution. + */ void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer, std::size_t size); + + /** + * Writes a range of bytes into a given process' address space at the specified + * virtual address. + * + * @param dest_addr The destination virtual address to begin writing the data at. + * @param src_buffer The data to write into the process' address space. + * @param size The size of the data to write, in bytes. + * + * @post The address range [dest_addr, size) in the process' address space + * contains the data that was within src_buffer. + * + * @post If an attempt is made to write into an unmapped region of memory, the writes + * will be ignored and an error will be logged. + * + * @post If a write is performed into a region of memory that is considered cached + * rasterizer memory, will cause the currently active rasterizer to be notified + * and will mark that region as invalidated to caches that the active + * graphics backend may be maintaining over the course of execution. + */ + void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size); + + /** + * Zeros a range of bytes within the current process' address space at the specified + * virtual address. + * + * @param process The process that will have data zeroed within its address space. + * @param dest_addr The destination virtual address to zero the data from. + * @param size The size of the range to zero out, in bytes. + * + * @post The range [dest_addr, size) within the process' address space contains the + * value 0. + */ void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, const std::size_t size); + + /** + * Copies data within a process' address space to another location within the + * same address space. + * + * @param process The process that will have data copied within its address space. + * @param dest_addr The destination virtual address to begin copying the data into. + * @param src_addr The source virtual address to begin copying the data from. + * @param size The size of the data to copy, in bytes. + * + * @post The range [dest_addr, size) within the process' address space contains the + * same data within the range [src_addr, size). + */ void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr, std::size_t size); void CopyBlock(const Kernel::Process& dest_process, const Kernel::Process& src_process, VAddr dest_addr, VAddr src_addr, std::size_t size); - std::string ReadCString(VAddr vaddr, std::size_t max_length); + /** + * Marks each page within the specified address range as cached or uncached. + * + * @param vaddr The virtual address indicating the start of the address range. + * @param size The size of the address range in bytes. + * @param cached Whether or not any pages within the address range should be + * marked as cached or uncached. + */ + void RasterizerMarkRegionCached(PAddr start, u32 size, bool cached); /// Gets a pointer to the memory region beginning at the specified physical address. u8* GetPhysicalPointer(PAddr address); - /// Gets a pointer to the memory region beginning at the specified physical address. - const u8* GetPhysicalPointer(PAddr address) const; - + /// Returns a reference to the memory region beginning at the specified physical address MemoryRef GetPhysicalRef(PAddr address) const; - u8* GetPointer(VAddr vaddr); - const u8* GetPointer(VAddr vaddr) const; + /// Determines if the given VAddr is valid for the specified process. + bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr); /// Returns true if the address refers to a valid memory region bool IsValidPhysicalAddress(PAddr paddr) const; @@ -368,11 +584,6 @@ public: /// Gets a serializable ref to FCRAM with the given offset MemoryRef GetFCRAMRef(std::size_t offset) const; - /** - * Mark each page touching the region as cached. - */ - void RasterizerMarkRegionCached(PAddr start, u32 size, bool cached); - /// Registers page table for rasterizer cache marking void RegisterPageTable(std::shared_ptr page_table); @@ -399,7 +610,6 @@ private: void MapPages(PageTable& page_table, u32 base, u32 size, MemoryRef memory, PageType type); class Impl; - std::unique_ptr impl; friend class boost::serialization::access; @@ -411,9 +621,6 @@ public: class BackingMemImpl; }; -/// Determines if the given VAddr is valid for the specified process. -bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr); - } // namespace Memory BOOST_CLASS_EXPORT_KEY(Memory::MemorySystem::BackingMemImpl) diff --git a/src/tests/core/memory/memory.cpp b/src/tests/core/memory/memory.cpp index 22fb69219..ea4b04f90 100644 --- a/src/tests/core/memory/memory.cpp +++ b/src/tests/core/memory/memory.cpp @@ -3,34 +3,31 @@ // Refer to the license.txt file included. #include -#include "core/core.h" #include "core/core_timing.h" -#include "core/hle/kernel/memory.h" #include "core/hle/kernel/process.h" -#include "core/hle/kernel/shared_page.h" #include "core/memory.h" -TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") { +TEST_CASE("memory.IsValidVirtualAddress", "[core][memory]") { Core::Timing timing(1, 100); Memory::MemorySystem memory; Kernel::KernelSystem kernel( memory, timing, [] {}, 0, 1, 0); SECTION("these regions should not be mapped on an empty process") { auto process = kernel.CreateProcess(kernel.CreateCodeSet("", 0)); - CHECK(Memory::IsValidVirtualAddress(*process, Memory::PROCESS_IMAGE_VADDR) == false); - CHECK(Memory::IsValidVirtualAddress(*process, Memory::HEAP_VADDR) == false); - CHECK(Memory::IsValidVirtualAddress(*process, Memory::LINEAR_HEAP_VADDR) == false); - CHECK(Memory::IsValidVirtualAddress(*process, Memory::VRAM_VADDR) == false); - CHECK(Memory::IsValidVirtualAddress(*process, Memory::CONFIG_MEMORY_VADDR) == false); - CHECK(Memory::IsValidVirtualAddress(*process, Memory::SHARED_PAGE_VADDR) == false); - CHECK(Memory::IsValidVirtualAddress(*process, Memory::TLS_AREA_VADDR) == false); + CHECK(memory.IsValidVirtualAddress(*process, Memory::PROCESS_IMAGE_VADDR) == false); + CHECK(memory.IsValidVirtualAddress(*process, Memory::HEAP_VADDR) == false); + CHECK(memory.IsValidVirtualAddress(*process, Memory::LINEAR_HEAP_VADDR) == false); + CHECK(memory.IsValidVirtualAddress(*process, Memory::VRAM_VADDR) == false); + CHECK(memory.IsValidVirtualAddress(*process, Memory::CONFIG_MEMORY_VADDR) == false); + CHECK(memory.IsValidVirtualAddress(*process, Memory::SHARED_PAGE_VADDR) == false); + CHECK(memory.IsValidVirtualAddress(*process, Memory::TLS_AREA_VADDR) == false); } SECTION("CONFIG_MEMORY_VADDR and SHARED_PAGE_VADDR should be valid after mapping them") { auto process = kernel.CreateProcess(kernel.CreateCodeSet("", 0)); kernel.MapSharedPages(process->vm_manager); - CHECK(Memory::IsValidVirtualAddress(*process, Memory::CONFIG_MEMORY_VADDR) == true); - CHECK(Memory::IsValidVirtualAddress(*process, Memory::SHARED_PAGE_VADDR) == true); + CHECK(memory.IsValidVirtualAddress(*process, Memory::CONFIG_MEMORY_VADDR) == true); + CHECK(memory.IsValidVirtualAddress(*process, Memory::SHARED_PAGE_VADDR) == true); } SECTION("special regions should be valid after mapping them") { @@ -38,13 +35,13 @@ TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") { SECTION("VRAM") { kernel.HandleSpecialMapping(process->vm_manager, {Memory::VRAM_VADDR, Memory::VRAM_SIZE, false, false}); - CHECK(Memory::IsValidVirtualAddress(*process, Memory::VRAM_VADDR) == true); + CHECK(memory.IsValidVirtualAddress(*process, Memory::VRAM_VADDR) == true); } SECTION("IO (Not yet implemented)") { kernel.HandleSpecialMapping( process->vm_manager, {Memory::IO_AREA_VADDR, Memory::IO_AREA_SIZE, false, false}); - CHECK_FALSE(Memory::IsValidVirtualAddress(*process, Memory::IO_AREA_VADDR) == true); + CHECK_FALSE(memory.IsValidVirtualAddress(*process, Memory::IO_AREA_VADDR) == true); } } @@ -52,6 +49,6 @@ TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") { auto process = kernel.CreateProcess(kernel.CreateCodeSet("", 0)); kernel.MapSharedPages(process->vm_manager); process->vm_manager.UnmapRange(Memory::CONFIG_MEMORY_VADDR, Memory::CONFIG_MEMORY_SIZE); - CHECK(Memory::IsValidVirtualAddress(*process, Memory::CONFIG_MEMORY_VADDR) == false); + CHECK(memory.IsValidVirtualAddress(*process, Memory::CONFIG_MEMORY_VADDR) == false); } }