From 4c2ed2706e3579ec1304907dad0d45673768e1fc Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 26 Nov 2019 12:33:20 -0500 Subject: [PATCH 01/13] core/memory: Introduce skeleton of Memory class Currently, the main memory management code is one of the remaining places where we have global state. The next series of changes will aim to rectify this. This change simply introduces the main skeleton of the class that will contain all the necessary state. --- src/core/core.cpp | 16 +++++++++++++--- src/core/core.h | 10 ++++++++++ src/core/memory.cpp | 12 ++++++++++++ src/core/memory.h | 21 +++++++++++++++++++++ 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index eba17218a..c45fb960c 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -39,6 +39,7 @@ #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" #include "core/loader/loader.h" +#include "core/memory.h" #include "core/memory/cheat_engine.h" #include "core/perf_stats.h" #include "core/reporter.h" @@ -112,8 +113,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, } struct System::Impl { explicit Impl(System& system) - : kernel{system}, fs_controller{system}, cpu_core_manager{system}, reporter{system}, - applet_manager{system} {} + : kernel{system}, fs_controller{system}, memory{system}, + cpu_core_manager{system}, reporter{system}, applet_manager{system} {} Cpu& CurrentCpuCore() { return cpu_core_manager.GetCurrentCore(); @@ -341,7 +342,8 @@ struct System::Impl { std::unique_ptr renderer; std::unique_ptr gpu_core; std::shared_ptr debug_context; - std::unique_ptr interrupt_manager; + std::unique_ptr interrupt_manager; + Memory::Memory memory; CpuCoreManager cpu_core_manager; bool is_powered_on = false; bool exit_lock = false; @@ -498,6 +500,14 @@ const ExclusiveMonitor& System::Monitor() const { return impl->cpu_core_manager.GetExclusiveMonitor(); } +Memory::Memory& System::Memory() { + return impl->memory; +} + +const Memory::Memory& System::Memory() const { + return impl->memory; +} + Tegra::GPU& System::GPU() { return *impl->gpu_core; } diff --git a/src/core/core.h b/src/core/core.h index f9b1a2866..91184e433 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -86,6 +86,10 @@ namespace Core::Hardware { class InterruptManager; } +namespace Memory { +class Memory; +} + namespace Core { class ARM_Interface; @@ -225,6 +229,12 @@ public: /// Gets a constant reference to the exclusive monitor const ExclusiveMonitor& Monitor() const; + /// Gets a mutable reference to the system memory instance. + Memory::Memory& Memory(); + + /// Gets a constant reference to the system memory instance. + const Memory::Memory& Memory() const; + /// Gets a mutable reference to the GPU interface Tegra::GPU& GPU(); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index fa49f3dd0..2098f13f7 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -24,6 +24,18 @@ 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(system)} {} +Memory::~Memory() = default; + void SetCurrentPageTable(Kernel::Process& process) { current_page_table = &process.VMManager().page_table; diff --git a/src/core/memory.h b/src/core/memory.h index 09008e1dd..c690df3c3 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -8,6 +8,10 @@ #include #include "common/common_types.h" +namespace Core { +class System; +} + namespace Kernel { class Process; } @@ -36,6 +40,23 @@ enum : VAddr { KERNEL_REGION_END = KERNEL_REGION_VADDR + KERNEL_REGION_SIZE, }; +/// Central class that handles all memory operations and state. +class Memory { +public: + explicit Memory(Core::System& system); + ~Memory(); + + Memory(const Memory&) = delete; + Memory& operator=(const Memory&) = delete; + + Memory(Memory&&) = default; + Memory& operator=(Memory&&) = default; + +private: + struct Impl; + std::unique_ptr impl; +}; + /// Changes the currently active page table to that of /// the given process instance. void SetCurrentPageTable(Kernel::Process& process); From 323680e5ad3ca0e27f2dd1de26816741b3243bed Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 26 Nov 2019 13:09:12 -0500 Subject: [PATCH 02/13] core/memory: Migrate over memory mapping functions to the new Memory class Migrates all of the direct mapping facilities over to the new memory class. In the process, this also obsoletes the need for memory_setup.h, so we can remove it entirely from the project. --- src/core/CMakeLists.txt | 1 - src/core/hle/kernel/vm_manager.cpp | 13 +- src/core/memory.cpp | 191 +++++++++++++++---------- src/core/memory.h | 59 ++++++++ src/core/memory_setup.h | 43 ------ src/tests/core/arm/arm_test_common.cpp | 15 +- 6 files changed, 187 insertions(+), 135 deletions(-) delete mode 100644 src/core/memory_setup.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index f2e774a6b..2dfdcb0d7 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -509,7 +509,6 @@ add_library(core STATIC memory/dmnt_cheat_vm.h memory.cpp memory.h - memory_setup.h perf_stats.cpp perf_stats.h reporter.cpp diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index e6eee09d7..a9a20ef76 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -16,7 +16,6 @@ #include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/vm_manager.h" #include "core/memory.h" -#include "core/memory_setup.h" namespace Kernel { namespace { @@ -786,19 +785,21 @@ void VMManager::MergeAdjacentVMA(VirtualMemoryArea& left, const VirtualMemoryAre } void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { + auto& memory = system.Memory(); + switch (vma.type) { case VMAType::Free: - Memory::UnmapRegion(page_table, vma.base, vma.size); + memory.UnmapRegion(page_table, vma.base, vma.size); break; case VMAType::AllocatedMemoryBlock: - Memory::MapMemoryRegion(page_table, vma.base, vma.size, - vma.backing_block->data() + vma.offset); + memory.MapMemoryRegion(page_table, vma.base, vma.size, + vma.backing_block->data() + vma.offset); break; case VMAType::BackingMemory: - Memory::MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory); + memory.MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory); break; case VMAType::MMIO: - Memory::MapIoRegion(page_table, vma.base, vma.size, vma.mmio_handler); + memory.MapIoRegion(page_table, vma.base, vma.size, vma.mmio_handler); break; } } diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 2098f13f7..28b65ca5e 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -17,7 +17,6 @@ #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" namespace Memory { @@ -30,12 +29,125 @@ static Common::PageTable* current_page_table = nullptr; struct Memory::Impl { explicit Impl(Core::System& system_) : system{system_} {} + 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); + } + + 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); + + const auto interval = boost::icl::discrete_interval::closed(base, base + size - 1); + const Common::SpecialRegion region{Common::SpecialRegion::Type::IODevice, + std::move(mmio_handler)}; + page_table.special_regions.add( + std::make_pair(interval, std::set{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); + + const auto interval = boost::icl::discrete_interval::closed(base, base + size - 1); + page_table.special_regions.erase(interval); + } + + void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size, + Common::MemoryHookPointer hook) { + const auto interval = boost::icl::discrete_interval::closed(base, base + size - 1); + const Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)}; + page_table.special_regions.add( + std::make_pair(interval, std::set{region})); + } + + void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size, + Common::MemoryHookPointer hook) { + const auto interval = boost::icl::discrete_interval::closed(base, base + size - 1); + const Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)}; + page_table.special_regions.subtract( + std::make_pair(interval, std::set{region})); + } + + /** + * Maps a region of pages as a specific type. + * + * @param page_table The page table to use to perform the mapping. + * @param base The base address to begin mapping at. + * @param size The total size of the range in bytes. + * @param memory The memory to map. + * @param type The page type to map the memory as. + */ + void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory, + Common::PageType type) { + LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE, + (base + size) * PAGE_SIZE); + + // During boot, current_page_table might not be set yet, in which case we need not flush + if (system.IsPoweredOn()) { + auto& gpu = system.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); + } + } + } + + const 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; + memory += PAGE_SIZE; + } + } + } + Core::System& system; }; Memory::Memory(Core::System& system) : impl{std::make_unique(system)} {} Memory::~Memory() = default; +void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target) { + impl->MapMemoryRegion(page_table, base, size, target); +} + +void Memory::MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size, + Common::MemoryHookPointer mmio_handler) { + impl->MapIoRegion(page_table, base, size, std::move(mmio_handler)); +} + +void Memory::UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) { + impl->UnmapRegion(page_table, base, size); +} + +void Memory::AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size, + Common::MemoryHookPointer hook) { + impl->AddDebugHook(page_table, base, size, std::move(hook)); +} + +void Memory::RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size, + Common::MemoryHookPointer hook) { + impl->RemoveDebugHook(page_table, base, size, std::move(hook)); +} + void SetCurrentPageTable(Kernel::Process& process) { current_page_table = &process.VMManager().page_table; @@ -48,83 +160,6 @@ void SetCurrentPageTable(Kernel::Process& process) { 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) { - LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE, - (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; - memory += PAGE_SIZE; - } - } -} - -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); -} - -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); - - auto interval = boost::icl::discrete_interval::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{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); - - auto interval = boost::icl::discrete_interval::closed(base, base + size - 1); - page_table.special_regions.erase(interval); -} - -void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size, - Common::MemoryHookPointer hook) { - auto interval = boost::icl::discrete_interval::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{region})); -} - -void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size, - Common::MemoryHookPointer hook) { - auto interval = boost::icl::discrete_interval::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{region})); -} - /** * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned) * using a VMA from the current process diff --git a/src/core/memory.h b/src/core/memory.h index c690df3c3..87ed3b696 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -5,8 +5,14 @@ #pragma once #include +#include #include #include "common/common_types.h" +#include "common/memory_hook.h" + +namespace Common { +struct PageTable; +} namespace Core { class System; @@ -52,6 +58,59 @@ public: Memory(Memory&&) = default; Memory& operator=(Memory&&) = default; + /** + * Maps an allocated buffer onto a region of the emulated process address space. + * + * @param page_table The page table of the emulated process. + * @param base The address to start mapping at. Must be page-aligned. + * @param size The amount of bytes to map. Must be page-aligned. + * @param target Buffer with the memory backing the mapping. Must be of length at least + * `size`. + */ + void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target); + + /** + * Maps a region of the emulated process address space as a IO region. + * + * @param page_table The page table of the emulated process. + * @param base The address to start mapping at. Must be page-aligned. + * @param size The amount of bytes to map. Must be page-aligned. + * @param mmio_handler The handler that backs the mapping. + */ + void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size, + Common::MemoryHookPointer mmio_handler); + + /** + * Unmaps a region of the emulated process address space. + * + * @param page_table The page table of the emulated process. + * @param base The address to begin unmapping at. + * @param size The amount of bytes to unmap. + */ + void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size); + + /** + * Adds a memory hook to intercept reads and writes to given region of memory. + * + * @param page_table The page table of the emulated process + * @param base The starting address to apply the hook to. + * @param size The size of the memory region to apply the hook to, in bytes. + * @param hook The hook to apply to the region of memory. + */ + void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size, + Common::MemoryHookPointer hook); + + /** + * Removes a memory hook from a given range of memory. + * + * @param page_table The page table of the emulated process. + * @param base The starting address to remove the hook from. + * @param size The size of the memory region to remove the hook from, in bytes. + * @param hook The hook to remove from the specified region of memory. + */ + void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size, + Common::MemoryHookPointer hook); + private: struct Impl; std::unique_ptr impl; diff --git a/src/core/memory_setup.h b/src/core/memory_setup.h deleted file mode 100644 index 5225ee8e2..000000000 --- a/src/core/memory_setup.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "common/common_types.h" -#include "common/memory_hook.h" - -namespace Common { -struct PageTable; -} - -namespace Memory { - -/** - * Maps an allocated buffer onto a region of the emulated process address space. - * - * @param page_table The page table of the emulated process. - * @param base The address to start mapping at. Must be page-aligned. - * @param size The amount of bytes to map. Must be page-aligned. - * @param target Buffer with the memory backing the mapping. Must be of length at least `size`. - */ -void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target); - -/** - * Maps a region of the emulated process address space as a IO region. - * @param page_table The page table of the emulated process. - * @param base The address to start mapping at. Must be page-aligned. - * @param size The amount of bytes to map. Must be page-aligned. - * @param mmio_handler The handler that backs the mapping. - */ -void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size, - Common::MemoryHookPointer mmio_handler); - -void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size); - -void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size, - Common::MemoryHookPointer hook); -void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size, - Common::MemoryHookPointer hook); - -} // namespace Memory diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp index ac7ae3e52..17043346b 100644 --- a/src/tests/core/arm/arm_test_common.cpp +++ b/src/tests/core/arm/arm_test_common.cpp @@ -8,7 +8,6 @@ #include "core/core.h" #include "core/hle/kernel/process.h" #include "core/memory.h" -#include "core/memory_setup.h" #include "tests/core/arm/arm_test_common.h" namespace ArmTests { @@ -16,8 +15,9 @@ namespace ArmTests { TestEnvironment::TestEnvironment(bool mutable_memory_) : mutable_memory(mutable_memory_), test_memory(std::make_shared(this)), kernel{Core::System::GetInstance()} { - auto process = Kernel::Process::Create(Core::System::GetInstance(), "", - Kernel::Process::ProcessType::Userland); + auto& system = Core::System::GetInstance(); + + auto process = Kernel::Process::Create(system, "", Kernel::Process::ProcessType::Userland); page_table = &process->VMManager().page_table; std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr); @@ -25,15 +25,16 @@ TestEnvironment::TestEnvironment(bool mutable_memory_) std::fill(page_table->attributes.begin(), page_table->attributes.end(), Common::PageType::Unmapped); - Memory::MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory); - Memory::MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory); + system.Memory().MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory); + system.Memory().MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory); kernel.MakeCurrentProcess(process.get()); } TestEnvironment::~TestEnvironment() { - Memory::UnmapRegion(*page_table, 0x80000000, 0x80000000); - Memory::UnmapRegion(*page_table, 0x00000000, 0x80000000); + auto& system = Core::System::GetInstance(); + system.Memory().UnmapRegion(*page_table, 0x80000000, 0x80000000); + system.Memory().UnmapRegion(*page_table, 0x00000000, 0x80000000); } void TestEnvironment::SetMemory64(VAddr vaddr, u64 value) { From e58748fd802dc069e90928d12d4db9ff994a869d Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 26 Nov 2019 13:46:41 -0500 Subject: [PATCH 03/13] core/memory: Migrate over address checking functions to the new Memory class A fairly straightforward migration. These member functions can just be mostly moved verbatim with minor changes. We already have the necessary plumbing in places that they're used. IsKernelVirtualAddress() can remain a non-member function, since it doesn't rely on class state in any form. --- src/core/gdbstub/gdbstub.cpp | 18 +++++---- src/core/hle/kernel/address_arbiter.cpp | 8 ++-- src/core/hle/kernel/svc.cpp | 4 +- src/core/hle/kernel/thread.cpp | 4 +- src/core/memory.cpp | 51 +++++++++++++++---------- src/core/memory.h | 24 ++++++++++-- 6 files changed, 70 insertions(+), 39 deletions(-) diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 54ed680db..78e44f3bd 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -969,7 +969,8 @@ static void ReadMemory() { SendReply("E01"); } - if (!Memory::IsValidVirtualAddress(addr)) { + const auto& memory = Core::System::GetInstance().Memory(); + if (!memory.IsValidVirtualAddress(addr)) { return SendReply("E00"); } @@ -984,22 +985,23 @@ static void ReadMemory() { /// Modify location in memory with data received from the gdb client. static void WriteMemory() { auto start_offset = command_buffer + 1; - auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); - VAddr addr = HexToLong(start_offset, static_cast(addr_pos - start_offset)); + const auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); + const VAddr addr = HexToLong(start_offset, static_cast(addr_pos - start_offset)); start_offset = addr_pos + 1; - auto len_pos = std::find(start_offset, command_buffer + command_length, ':'); - u64 len = HexToLong(start_offset, static_cast(len_pos - start_offset)); + const auto len_pos = std::find(start_offset, command_buffer + command_length, ':'); + const u64 len = HexToLong(start_offset, static_cast(len_pos - start_offset)); - if (!Memory::IsValidVirtualAddress(addr)) { + auto& system = Core::System::GetInstance(); + const auto& memory = system.Memory(); + if (!memory.IsValidVirtualAddress(addr)) { return SendReply("E00"); } std::vector data(len); - GdbHexToMem(data.data(), len_pos + 1, len); Memory::WriteBlock(addr, data.data(), len); - Core::System::GetInstance().InvalidateCpuInstructionCaches(); + system.InvalidateCpuInstructionCaches(); SendReply("OK"); } diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 4859954cb..7f9a559d2 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -68,7 +68,7 @@ ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) { ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) { // Ensure that we can write to the address. - if (!Memory::IsValidVirtualAddress(address)) { + if (!system.Memory().IsValidVirtualAddress(address)) { return ERR_INVALID_ADDRESS_STATE; } @@ -83,7 +83,7 @@ ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) { // Ensure that we can write to the address. - if (!Memory::IsValidVirtualAddress(address)) { + if (!system.Memory().IsValidVirtualAddress(address)) { return ERR_INVALID_ADDRESS_STATE; } @@ -135,7 +135,7 @@ ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement) { // Ensure that we can read the address. - if (!Memory::IsValidVirtualAddress(address)) { + if (!system.Memory().IsValidVirtualAddress(address)) { return ERR_INVALID_ADDRESS_STATE; } @@ -158,7 +158,7 @@ ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s6 ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { // Ensure that we can read the address. - if (!Memory::IsValidVirtualAddress(address)) { + if (!system.Memory().IsValidVirtualAddress(address)) { return ERR_INVALID_ADDRESS_STATE; } // Only wait for the address if equal. diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 9928b3a26..eddafaf60 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -332,7 +332,7 @@ static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_ad /// Connect to an OS service given the port name, returns the handle to the port to out static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle, VAddr port_name_address) { - if (!Memory::IsValidVirtualAddress(port_name_address)) { + if (!system.Memory().IsValidVirtualAddress(port_name_address)) { LOG_ERROR(Kernel_SVC, "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}", port_name_address); @@ -452,7 +452,7 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}", handles_address, handle_count, nano_seconds); - if (!Memory::IsValidVirtualAddress(handles_address)) { + if (!system.Memory().IsValidVirtualAddress(handles_address)) { LOG_ERROR(Kernel_SVC, "Handle address is not a valid virtual address, handle_address=0x{:016X}", handles_address); diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 735019d96..e84e5ce0d 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -162,13 +162,13 @@ ResultVal> Thread::Create(KernelCore& kernel, std::strin return ERR_INVALID_PROCESSOR_ID; } - if (!Memory::IsValidVirtualAddress(owner_process, entry_point)) { + auto& system = Core::System::GetInstance(); + if (!system.Memory().IsValidVirtualAddress(owner_process, entry_point)) { LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); // TODO (bunnei): Find the correct error code to use here return RESULT_UNKNOWN; } - auto& system = Core::System::GetInstance(); std::shared_ptr thread = std::make_shared(kernel); thread->thread_id = kernel.CreateNewThreadID(); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 28b65ca5e..4c13ea1e7 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -75,6 +75,29 @@ struct Memory::Impl { std::make_pair(interval, std::set{region})); } + bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const { + const auto& page_table = process.VMManager().page_table; + + const u8* const page_pointer = page_table.pointers[vaddr >> PAGE_BITS]; + if (page_pointer != nullptr) { + 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(VAddr vaddr) const { + return IsValidVirtualAddress(*system.CurrentProcess(), vaddr); + } + /** * Maps a region of pages as a specific type. * @@ -148,6 +171,14 @@ void Memory::RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size impl->RemoveDebugHook(page_table, base, size, std::move(hook)); } +bool Memory::IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const { + return impl->IsValidVirtualAddress(process, vaddr); +} + +bool Memory::IsValidVirtualAddress(const VAddr vaddr) const { + return impl->IsValidVirtualAddress(vaddr); +} + void SetCurrentPageTable(Kernel::Process& process) { current_page_table = &process.VMManager().page_table; @@ -256,26 +287,6 @@ void Write(const VAddr vaddr, const T data) { } } -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; } diff --git a/src/core/memory.h b/src/core/memory.h index 87ed3b696..cacf4fb1a 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -111,6 +111,27 @@ public: void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size, Common::MemoryHookPointer hook); + /** + * Checks whether or not the supplied address is a valid virtual + * address for the given process. + * + * @param process The emulated process to check the address against. + * @param vaddr The virtual address to check the validity of. + * + * @returns True if the given virtual address is valid, false otherwise. + */ + bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr) const; + + /** + * Checks whether or not the supplied address is a valid virtual + * address for the current process. + * + * @param vaddr The virtual address to check the validity of. + * + * @returns True if the given virtual address is valid, false otherwise. + */ + bool IsValidVirtualAddress(VAddr vaddr) const; + private: struct Impl; std::unique_ptr impl; @@ -120,9 +141,6 @@ private: /// the given process instance. void SetCurrentPageTable(Kernel::Process& process); -/// Determines if the given VAddr is valid for the specified process. -bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr); -bool IsValidVirtualAddress(VAddr vaddr); /// Determines if the given VAddr is a kernel address bool IsKernelVirtualAddress(VAddr vaddr); From fc7d0a17b6ec7dfc44a56f3e4a8bd97108f1c596 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 26 Nov 2019 13:58:03 -0500 Subject: [PATCH 04/13] core/memory: Move memory read/write implementation functions into an anonymous namespace These will eventually be migrated into the main Memory class, but for now, we put them in an anonymous namespace, so that the other functions that use them, can be migrated over separately. --- src/core/memory.cpp | 195 ++++++++++++++++++++++---------------------- 1 file changed, 98 insertions(+), 97 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 4c13ea1e7..017033613 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -20,8 +20,105 @@ #include "video_core/gpu.h" namespace Memory { +namespace { +Common::PageTable* current_page_table = nullptr; -static Common::PageTable* current_page_table = nullptr; +/** + * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned) + * using a VMA from the current process + */ +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. + */ +u8* GetPointerFromVMA(VAddr vaddr) { + return ::Memory::GetPointerFromVMA(*Core::System::GetInstance().CurrentProcess(), vaddr); +} + +template +T Read(const VAddr vaddr) { + const u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; + if (page_pointer != nullptr) { + // 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; + } + + const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; + switch (type) { + case Common::PageType::Unmapped: + 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: { + const u8* const host_ptr{GetPointerFromVMA(vaddr)}; + Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), sizeof(T)); + T value; + std::memcpy(&value, host_ptr, sizeof(T)); + return value; + } + default: + UNREACHABLE(); + } + return {}; +} + +template +void Write(const VAddr vaddr, const T data) { + u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; + if (page_pointer != nullptr) { + // 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: + LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, + static_cast(data), vaddr); + return; + case Common::PageType::Memory: + ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); + break; + case Common::PageType::RasterizerCachedMemory: { + u8* const host_ptr{GetPointerFromVMA(vaddr)}; + Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T)); + std::memcpy(host_ptr, &data, sizeof(T)); + break; + } + default: + UNREACHABLE(); + } +} +} // Anonymous namespace // 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 @@ -191,102 +288,6 @@ void SetCurrentPageTable(Kernel::Process& process) { system.ArmInterface(3).PageTableChanged(*current_page_table, address_space_width); } -/** - * 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); -} - -template -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: - 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)); - T value; - std::memcpy(&value, host_ptr, sizeof(T)); - return value; - } - default: - UNREACHABLE(); - } - return {}; -} - -template -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: - LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, - static_cast(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; - } - default: - UNREACHABLE(); - } -} - bool IsKernelVirtualAddress(const VAddr vaddr) { return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END; } From 536fc7f0ea77e08d68c760f387c307d258804e3b Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 26 Nov 2019 14:10:49 -0500 Subject: [PATCH 05/13] core: Prepare various classes for memory read/write migration Amends a few interfaces to be able to handle the migration over to the new Memory class by passing the class by reference as a function parameter where necessary. Notably, within the filesystem services, this eliminates two ReadBlock() calls by using the helper functions of HLERequestContext to do that for us. --- src/audio_core/audio_renderer.cpp | 24 ++++++++++--------- src/audio_core/audio_renderer.h | 10 ++++++-- src/core/arm/arm_interface.cpp | 3 +-- src/core/arm/arm_interface.h | 8 ++++++- src/core/arm/dynarmic/arm_dynarmic.cpp | 8 ++++--- src/core/arm/dynarmic/arm_dynarmic.h | 1 - src/core/arm/unicorn/arm_unicorn.cpp | 2 +- src/core/arm/unicorn/arm_unicorn.h | 1 - src/core/hle/kernel/client_session.cpp | 4 ++-- src/core/hle/kernel/client_session.h | 6 ++++- src/core/hle/kernel/server_session.cpp | 3 ++- src/core/hle/kernel/server_session.h | 9 ++++++- src/core/hle/kernel/svc.cpp | 2 +- src/core/hle/service/audio/audren_u.cpp | 5 ++-- src/core/hle/service/filesystem/fsp_srv.cpp | 7 ++---- src/core/hle/service/lm/lm.cpp | 13 ++++++---- src/core/reporter.cpp | 14 +++++------ src/core/tools/freezer.cpp | 14 +++++------ src/core/tools/freezer.h | 7 +++++- src/video_core/rasterizer_accelerated.cpp | 3 ++- src/video_core/rasterizer_accelerated.h | 9 +++++-- .../renderer_opengl/gl_rasterizer.cpp | 5 ++-- .../renderer_vulkan/vk_buffer_cache.cpp | 4 +++- .../renderer_vulkan/vk_buffer_cache.h | 7 +++++- 24 files changed, 108 insertions(+), 61 deletions(-) diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index 6b0167acd..12e2b7901 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp @@ -36,9 +36,9 @@ public: } void SetWaveIndex(std::size_t index); - std::vector DequeueSamples(std::size_t sample_count); + std::vector DequeueSamples(std::size_t sample_count, Memory::Memory& memory); void UpdateState(); - void RefreshBuffer(); + void RefreshBuffer(Memory::Memory& memory); private: bool is_in_use{}; @@ -66,17 +66,18 @@ public: return info; } - void UpdateState(); + void UpdateState(Memory::Memory& memory); private: EffectOutStatus out_status{}; EffectInStatus info{}; }; -AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, AudioRendererParameter params, +AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Memory::Memory& memory_, + AudioRendererParameter params, std::shared_ptr buffer_event, std::size_t instance_number) : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count), - effects(params.effect_count) { + effects(params.effect_count), memory{memory_} { audio_out = std::make_unique(); stream = audio_out->OpenStream(core_timing, STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, @@ -162,7 +163,7 @@ std::vector AudioRenderer::UpdateAudioRenderer(const std::vector& input_ } for (auto& effect : effects) { - effect.UpdateState(); + effect.UpdateState(memory); } // Release previous buffers and queue next ones for playback @@ -206,13 +207,14 @@ void AudioRenderer::VoiceState::SetWaveIndex(std::size_t index) { is_refresh_pending = true; } -std::vector AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_count) { +std::vector AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_count, + Memory::Memory& memory) { if (!IsPlaying()) { return {}; } if (is_refresh_pending) { - RefreshBuffer(); + RefreshBuffer(memory); } const std::size_t max_size{samples.size() - offset}; @@ -256,7 +258,7 @@ void AudioRenderer::VoiceState::UpdateState() { is_in_use = info.is_in_use; } -void AudioRenderer::VoiceState::RefreshBuffer() { +void AudioRenderer::VoiceState::RefreshBuffer(Memory::Memory& memory) { std::vector new_samples(info.wave_buffer[wave_index].buffer_sz / sizeof(s16)); Memory::ReadBlock(info.wave_buffer[wave_index].buffer_addr, new_samples.data(), info.wave_buffer[wave_index].buffer_sz); @@ -307,7 +309,7 @@ void AudioRenderer::VoiceState::RefreshBuffer() { is_refresh_pending = false; } -void AudioRenderer::EffectState::UpdateState() { +void AudioRenderer::EffectState::UpdateState(Memory::Memory& memory) { if (info.is_new) { out_status.state = EffectStatus::New; } else { @@ -340,7 +342,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { std::size_t offset{}; s64 samples_remaining{BUFFER_SIZE}; while (samples_remaining > 0) { - const std::vector samples{voice.DequeueSamples(samples_remaining)}; + const std::vector samples{voice.DequeueSamples(samples_remaining, memory)}; if (samples.empty()) { break; diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h index abed224bb..be1b019f1 100644 --- a/src/audio_core/audio_renderer.h +++ b/src/audio_core/audio_renderer.h @@ -22,6 +22,10 @@ namespace Kernel { class WritableEvent; } +namespace Memory { +class Memory; +} + namespace AudioCore { class AudioOut; @@ -217,7 +221,8 @@ static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size class AudioRenderer { public: - AudioRenderer(Core::Timing::CoreTiming& core_timing, AudioRendererParameter params, + AudioRenderer(Core::Timing::CoreTiming& core_timing, Memory::Memory& memory_, + AudioRendererParameter params, std::shared_ptr buffer_event, std::size_t instance_number); ~AudioRenderer(); @@ -238,7 +243,8 @@ private: std::vector voices; std::vector effects; std::unique_ptr audio_out; - AudioCore::StreamPtr stream; + StreamPtr stream; + Memory::Memory& memory; }; } // namespace AudioCore diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index 372612c9b..dea192869 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp @@ -13,7 +13,6 @@ #include "core/memory.h" namespace Core { - namespace { constexpr u64 ELF_DYNAMIC_TAG_NULL = 0; @@ -156,7 +155,7 @@ std::vector ARM_Interface::GetBacktrace() const { } std::map modules; - auto& loader{System::GetInstance().GetAppLoader()}; + auto& loader{system.GetAppLoader()}; if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) { return {}; } diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 45e94e625..47b964eb7 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -17,11 +17,13 @@ enum class VMAPermission : u8; } namespace Core { +class System; /// Generic ARMv8 CPU interface class ARM_Interface : NonCopyable { public: - virtual ~ARM_Interface() {} + explicit ARM_Interface(System& system_) : system{system_} {} + virtual ~ARM_Interface() = default; struct ThreadContext { std::array cpu_registers; @@ -163,6 +165,10 @@ public: /// fp+0 : pointer to previous frame record /// fp+8 : value of lr for frame void LogBacktrace() const; + +protected: + /// System context that this ARM interface is running under. + System& system; }; } // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index a0705b2b8..2b396f1d6 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -28,6 +28,7 @@ public: explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {} u8 MemoryRead8(u64 vaddr) override { + auto& s = parent.system; return Memory::Read8(vaddr); } u16 MemoryRead16(u64 vaddr) override { @@ -171,9 +172,10 @@ void ARM_Dynarmic::Step() { ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index) - : cb(std::make_unique(*this)), inner_unicorn{system}, - core_index{core_index}, system{system}, - exclusive_monitor{dynamic_cast(exclusive_monitor)} {} + : ARM_Interface{system}, + cb(std::make_unique(*this)), inner_unicorn{system}, + core_index{core_index}, exclusive_monitor{ + dynamic_cast(exclusive_monitor)} {} ARM_Dynarmic::~ARM_Dynarmic() = default; diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h index 504d46c68..d08de475f 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ b/src/core/arm/dynarmic/arm_dynarmic.h @@ -58,7 +58,6 @@ private: ARM_Unicorn inner_unicorn; std::size_t core_index; - System& system; DynarmicExclusiveMonitor& exclusive_monitor; }; diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index 9698172db..48182c99a 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -60,7 +60,7 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si return false; } -ARM_Unicorn::ARM_Unicorn(System& system) : system{system} { +ARM_Unicorn::ARM_Unicorn(System& system) : ARM_Interface{system} { CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc)); auto fpv = 3 << 20; diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index b39426ea0..3c5b155f9 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h @@ -45,7 +45,6 @@ private: static void InterruptHook(uc_engine* uc, u32 int_no, void* user_data); uc_engine* uc{}; - System& system; GDBStub::BreakpointAddress last_bkpt{}; bool last_bkpt_hit = false; }; diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp index 5995a6556..9849dbe91 100644 --- a/src/core/hle/kernel/client_session.cpp +++ b/src/core/hle/kernel/client_session.cpp @@ -21,10 +21,10 @@ ClientSession::~ClientSession() { } } -ResultCode ClientSession::SendSyncRequest(Thread* thread) { +ResultCode ClientSession::SendSyncRequest(Thread* thread, Memory::Memory& memory) { // Signal the server session that new data is available if (auto server = parent->server.lock()) { - return server->HandleSyncRequest(SharedFrom(thread)); + return server->HandleSyncRequest(SharedFrom(thread), memory); } return ERR_SESSION_CLOSED_BY_REMOTE; diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h index 5ae41db29..484dd7bc9 100644 --- a/src/core/hle/kernel/client_session.h +++ b/src/core/hle/kernel/client_session.h @@ -10,6 +10,10 @@ union ResultCode; +namespace Memory { +class Memory; +} + namespace Kernel { class KernelCore; @@ -37,7 +41,7 @@ public: return HANDLE_TYPE; } - ResultCode SendSyncRequest(Thread* thread); + ResultCode SendSyncRequest(Thread* thread, Memory::Memory& memory); private: /// The parent session, which links to the server endpoint. diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index c7db21eb2..57878514d 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -127,7 +127,8 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con return RESULT_SUCCESS; } -ResultCode ServerSession::HandleSyncRequest(std::shared_ptr thread) { +ResultCode ServerSession::HandleSyncRequest(std::shared_ptr thread, + Memory::Memory& memory) { // The ServerSession received a sync request, this means that there's new data available // from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or // similar. diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index 8a65647b6..641709a45 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -13,6 +13,10 @@ #include "core/hle/kernel/wait_object.h" #include "core/hle/result.h" +namespace Memory { +class Memory; +} + namespace Kernel { class ClientPort; @@ -85,10 +89,13 @@ public: /** * Handle a sync request from the emulated application. + * * @param thread Thread that initiated the request. + * @param memory Memory context to handle the sync request under. + * * @returns ResultCode from the operation. */ - ResultCode HandleSyncRequest(std::shared_ptr thread); + ResultCode HandleSyncRequest(std::shared_ptr thread, Memory::Memory& memory); bool ShouldWait(const Thread* thread) const override; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index eddafaf60..68bff11ec 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -383,7 +383,7 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) { // TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server // responds and cause a reschedule. - return session->SendSyncRequest(system.CurrentScheduler().GetCurrentThread()); + return session->SendSyncRequest(system.CurrentScheduler().GetCurrentThread(), system.Memory()); } /// Get the ID for the specified thread. diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 4ea7ade6e..82a5dbf14 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -49,8 +49,9 @@ public: system_event = Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioRenderer:SystemEvent"); - renderer = std::make_unique( - system.CoreTiming(), audren_params, system_event.writable, instance_number); + renderer = std::make_unique(system.CoreTiming(), system.Memory(), + audren_params, system_event.writable, + instance_number); } private: diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index 5874ed6bd..89e1957f9 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -391,13 +391,10 @@ public: } void RenameFile(Kernel::HLERequestContext& ctx) { - std::vector buffer; - buffer.resize(ctx.BufferDescriptorX()[0].Size()); - Memory::ReadBlock(ctx.BufferDescriptorX()[0].Address(), buffer.data(), buffer.size()); + std::vector buffer = ctx.ReadBuffer(0); const std::string src_name = Common::StringFromBuffer(buffer); - buffer.resize(ctx.BufferDescriptorX()[1].Size()); - Memory::ReadBlock(ctx.BufferDescriptorX()[1].Address(), buffer.data(), buffer.size()); + buffer = ctx.ReadBuffer(1); const std::string dst_name = Common::StringFromBuffer(buffer); LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name); diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp index 435f2d286..74ecaef1b 100644 --- a/src/core/hle/service/lm/lm.cpp +++ b/src/core/hle/service/lm/lm.cpp @@ -17,7 +17,8 @@ namespace Service::LM { class ILogger final : public ServiceFramework { public: - ILogger(Manager& manager) : ServiceFramework("ILogger"), manager(manager) { + explicit ILogger(Manager& manager_, Memory::Memory& memory_) + : ServiceFramework("ILogger"), manager{manager_}, memory{memory_} { static const FunctionInfo functions[] = { {0, &ILogger::Log, "Log"}, {1, &ILogger::SetDestination, "SetDestination"}, @@ -74,11 +75,13 @@ private: } Manager& manager; + Memory::Memory& memory; }; class LM final : public ServiceFramework { public: - explicit LM(Manager& manager) : ServiceFramework{"lm"}, manager(manager) { + explicit LM(Manager& manager_, Memory::Memory& memory_) + : ServiceFramework{"lm"}, manager{manager_}, memory{memory_} { // clang-format off static const FunctionInfo functions[] = { {0, &LM::OpenLogger, "OpenLogger"}, @@ -94,14 +97,16 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface(manager); + rb.PushIpcInterface(manager, memory); } Manager& manager; + Memory::Memory& memory; }; void InstallInterfaces(Core::System& system) { - std::make_shared(system.GetLogManager())->InstallAsService(system.ServiceManager()); + std::make_shared(system.GetLogManager(), system.Memory()) + ->InstallAsService(system.ServiceManager()); } } // namespace Service::LM diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index 6f4af77fd..af0988d62 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp @@ -147,7 +147,7 @@ json GetFullDataAuto(const std::string& timestamp, u64 title_id, Core::System& s } template -json GetHLEBufferDescriptorData(const std::vector& buffer) { +json GetHLEBufferDescriptorData(const std::vector& buffer, Memory::Memory& memory) { auto buffer_out = json::array(); for (const auto& desc : buffer) { auto entry = json{ @@ -167,7 +167,7 @@ json GetHLEBufferDescriptorData(const std::vector& buffer) { return buffer_out; } -json GetHLERequestContextData(Kernel::HLERequestContext& ctx) { +json GetHLERequestContextData(Kernel::HLERequestContext& ctx, Memory::Memory& memory) { json out; auto cmd_buf = json::array(); @@ -177,10 +177,10 @@ json GetHLERequestContextData(Kernel::HLERequestContext& ctx) { out["command_buffer"] = std::move(cmd_buf); - out["buffer_descriptor_a"] = GetHLEBufferDescriptorData(ctx.BufferDescriptorA()); - out["buffer_descriptor_b"] = GetHLEBufferDescriptorData(ctx.BufferDescriptorB()); - out["buffer_descriptor_c"] = GetHLEBufferDescriptorData(ctx.BufferDescriptorC()); - out["buffer_descriptor_x"] = GetHLEBufferDescriptorData(ctx.BufferDescriptorX()); + out["buffer_descriptor_a"] = GetHLEBufferDescriptorData(ctx.BufferDescriptorA(), memory); + out["buffer_descriptor_b"] = GetHLEBufferDescriptorData(ctx.BufferDescriptorB(), memory); + out["buffer_descriptor_c"] = GetHLEBufferDescriptorData(ctx.BufferDescriptorC(), memory); + out["buffer_descriptor_x"] = GetHLEBufferDescriptorData(ctx.BufferDescriptorX(), memory); return out; } @@ -259,7 +259,7 @@ void Reporter::SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u const auto title_id = system.CurrentProcess()->GetTitleID(); auto out = GetFullDataAuto(timestamp, title_id, system); - auto function_out = GetHLERequestContextData(ctx); + auto function_out = GetHLERequestContextData(ctx, system.Memory()); function_out["command_id"] = command_id; function_out["function_name"] = name; function_out["service_name"] = service_name; diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp index 19b531ecb..c7f42388f 100644 --- a/src/core/tools/freezer.cpp +++ b/src/core/tools/freezer.cpp @@ -11,12 +11,11 @@ #include "core/tools/freezer.h" namespace Tools { - namespace { constexpr s64 MEMORY_FREEZER_TICKS = static_cast(Core::Timing::BASE_CLOCK_RATE / 60); -u64 MemoryReadWidth(u32 width, VAddr addr) { +u64 MemoryReadWidth(Memory::Memory& memory, u32 width, VAddr addr) { switch (width) { case 1: return Memory::Read8(addr); @@ -32,7 +31,7 @@ u64 MemoryReadWidth(u32 width, VAddr addr) { } } -void MemoryWriteWidth(u32 width, VAddr addr, u64 value) { +void MemoryWriteWidth(Memory::Memory& memory, u32 width, VAddr addr, u64 value) { switch (width) { case 1: Memory::Write8(addr, static_cast(value)); @@ -53,7 +52,8 @@ void MemoryWriteWidth(u32 width, VAddr addr, u64 value) { } // Anonymous namespace -Freezer::Freezer(Core::Timing::CoreTiming& core_timing) : core_timing(core_timing) { +Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Memory::Memory& memory_) + : core_timing{core_timing_}, memory{memory_} { event = Core::Timing::CreateEvent( "MemoryFreezer::FrameCallback", [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); @@ -89,7 +89,7 @@ void Freezer::Clear() { u64 Freezer::Freeze(VAddr address, u32 width) { std::lock_guard lock{entries_mutex}; - const auto current_value = MemoryReadWidth(width, address); + const auto current_value = MemoryReadWidth(memory, width, address); entries.push_back({address, width, current_value}); LOG_DEBUG(Common_Memory, @@ -169,7 +169,7 @@ void Freezer::FrameCallback(u64 userdata, s64 cycles_late) { LOG_DEBUG(Common_Memory, "Enforcing memory freeze at address={:016X}, value={:016X}, width={:02X}", entry.address, entry.value, entry.width); - MemoryWriteWidth(entry.width, entry.address, entry.value); + MemoryWriteWidth(memory, entry.width, entry.address, entry.value); } core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS - cycles_late, event); @@ -181,7 +181,7 @@ void Freezer::FillEntryReads() { LOG_DEBUG(Common_Memory, "Updating memory freeze entries to current values."); for (auto& entry : entries) { - entry.value = MemoryReadWidth(entry.width, entry.address); + entry.value = MemoryReadWidth(memory, entry.width, entry.address); } } diff --git a/src/core/tools/freezer.h b/src/core/tools/freezer.h index 90b1a885c..916339c6c 100644 --- a/src/core/tools/freezer.h +++ b/src/core/tools/freezer.h @@ -16,6 +16,10 @@ class CoreTiming; struct EventType; } // namespace Core::Timing +namespace Memory { +class Memory; +} + namespace Tools { /** @@ -34,7 +38,7 @@ public: u64 value; }; - explicit Freezer(Core::Timing::CoreTiming& core_timing); + explicit Freezer(Core::Timing::CoreTiming& core_timing_, Memory::Memory& memory_); ~Freezer(); // Enables or disables the entire memory freezer. @@ -78,6 +82,7 @@ private: std::shared_ptr event; Core::Timing::CoreTiming& core_timing; + Memory::Memory& memory; }; } // namespace Tools diff --git a/src/video_core/rasterizer_accelerated.cpp b/src/video_core/rasterizer_accelerated.cpp index b230dcc18..f3dbadebc 100644 --- a/src/video_core/rasterizer_accelerated.cpp +++ b/src/video_core/rasterizer_accelerated.cpp @@ -22,7 +22,8 @@ constexpr auto RangeFromInterval(Map& map, const Interval& interval) { } // Anonymous namespace -RasterizerAccelerated::RasterizerAccelerated() = default; +RasterizerAccelerated::RasterizerAccelerated(Memory::Memory& cpu_memory_) + : cpu_memory{cpu_memory_} {} RasterizerAccelerated::~RasterizerAccelerated() = default; diff --git a/src/video_core/rasterizer_accelerated.h b/src/video_core/rasterizer_accelerated.h index 8f7e3547e..315798e7c 100644 --- a/src/video_core/rasterizer_accelerated.h +++ b/src/video_core/rasterizer_accelerated.h @@ -11,12 +11,16 @@ #include "common/common_types.h" #include "video_core/rasterizer_interface.h" +namespace Memory { +class Memory; +} + namespace VideoCore { /// Implements the shared part in GPU accelerated rasterizers in RasterizerInterface. class RasterizerAccelerated : public RasterizerInterface { public: - explicit RasterizerAccelerated(); + explicit RasterizerAccelerated(Memory::Memory& cpu_memory_); ~RasterizerAccelerated() override; void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) override; @@ -24,8 +28,9 @@ public: private: using CachedPageMap = boost::icl::interval_map; CachedPageMap cached_pages; - std::mutex pages_mutex; + + Memory::Memory& cpu_memory; }; } // namespace VideoCore diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index f97ec06f0..85f05544c 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -86,8 +86,9 @@ std::size_t GetConstBufferSize(const Tegra::Engines::ConstBufferInfo& buffer, RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window, ScreenInfo& info) - : texture_cache{system, *this, device}, shader_cache{*this, system, emu_window, device}, - system{system}, screen_info{info}, buffer_cache{*this, system, device, STREAM_BUFFER_SIZE} { + : RasterizerAccelerated{system.Memory()}, texture_cache{system, *this, device}, + shader_cache{*this, system, emu_window, device}, system{system}, screen_info{info}, + buffer_cache{*this, system, device, STREAM_BUFFER_SIZE} { shader_program_manager = std::make_unique(); state.draw.shader_program = 0; state.Apply(); diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index d2e9f4031..959638747 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -24,9 +24,11 @@ CachedBufferEntry::CachedBufferEntry(VAddr cpu_addr, std::size_t size, u64 offse alignment{alignment} {} VKBufferCache::VKBufferCache(Tegra::MemoryManager& tegra_memory_manager, + Memory::Memory& cpu_memory_, VideoCore::RasterizerInterface& rasterizer, const VKDevice& device, VKMemoryManager& memory_manager, VKScheduler& scheduler, u64 size) - : RasterizerCache{rasterizer}, tegra_memory_manager{tegra_memory_manager} { + : RasterizerCache{rasterizer}, tegra_memory_manager{tegra_memory_manager}, cpu_memory{ + cpu_memory_} { const auto usage = vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eUniformBuffer; diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index 49f13bcdc..daa8ccf66 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -13,6 +13,10 @@ #include "video_core/renderer_vulkan/declarations.h" #include "video_core/renderer_vulkan/vk_scheduler.h" +namespace Memory { +class Memory; +} + namespace Tegra { class MemoryManager; } @@ -58,7 +62,7 @@ private: class VKBufferCache final : public RasterizerCache> { public: - explicit VKBufferCache(Tegra::MemoryManager& tegra_memory_manager, + explicit VKBufferCache(Tegra::MemoryManager& tegra_memory_manager, Memory::Memory& cpu_memory_, VideoCore::RasterizerInterface& rasterizer, const VKDevice& device, VKMemoryManager& memory_manager, VKScheduler& scheduler, u64 size); ~VKBufferCache(); @@ -92,6 +96,7 @@ private: void AlignBuffer(std::size_t alignment); Tegra::MemoryManager& tegra_memory_manager; + Memory::Memory& cpu_memory; std::unique_ptr stream_buffer; vk::Buffer buffer_handle; From 3f08e8d8d4ef16cf2468620fbfbdac46e43dcaef Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 26 Nov 2019 15:19:15 -0500 Subject: [PATCH 06/13] core/memory: Migrate over GetPointer() With all of the interfaces ready for migration, it's trivial to migrate over GetPointer(). --- src/core/hle/kernel/server_session.cpp | 3 +- src/core/memory.cpp | 38 +++++++++++-------- src/core/memory.h | 22 ++++++++++- src/video_core/memory_manager.cpp | 4 +- .../renderer_opengl/gl_rasterizer.cpp | 3 +- .../renderer_opengl/renderer_opengl.cpp | 2 +- .../renderer_vulkan/vk_buffer_cache.cpp | 6 +-- 7 files changed, 53 insertions(+), 25 deletions(-) diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 57878514d..1198c7a97 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -19,6 +19,7 @@ #include "core/hle/kernel/server_session.h" #include "core/hle/kernel/session.h" #include "core/hle/kernel/thread.h" +#include "core/memory.h" namespace Kernel { @@ -133,7 +134,7 @@ ResultCode ServerSession::HandleSyncRequest(std::shared_ptr thread, // from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or // similar. Kernel::HLERequestContext context(SharedFrom(this), thread); - u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress()); + u32* cmd_buf = (u32*)memory.GetPointer(thread->GetTLSAddress()); context.PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); ResultCode result = RESULT_SUCCESS; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 017033613..93cd67e39 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -195,6 +195,21 @@ struct Memory::Impl { return IsValidVirtualAddress(*system.CurrentProcess(), vaddr); } + u8* GetPointer(const VAddr vaddr) { + u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; + if (page_pointer != nullptr) { + return page_pointer + (vaddr & PAGE_MASK); + } + + if (current_page_table->attributes[vaddr >> PAGE_BITS] == + Common::PageType::RasterizerCachedMemory) { + return GetPointerFromVMA(vaddr); + } + + LOG_ERROR(HW_Memory, "Unknown GetPointer @ 0x{:016X}", vaddr); + return nullptr; + } + /** * Maps a region of pages as a specific type. * @@ -276,6 +291,14 @@ bool Memory::IsValidVirtualAddress(const VAddr vaddr) const { return impl->IsValidVirtualAddress(vaddr); } +u8* Memory::GetPointer(VAddr vaddr) { + return impl->GetPointer(vaddr); +} + +const u8* Memory::GetPointer(VAddr vaddr) const { + return impl->GetPointer(vaddr); +} + void SetCurrentPageTable(Kernel::Process& process) { current_page_table = &process.VMManager().page_table; @@ -292,21 +315,6 @@ 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); - } - - LOG_ERROR(HW_Memory, "Unknown GetPointer @ 0x{:016X}", vaddr); - return nullptr; -} - std::string ReadCString(VAddr vaddr, std::size_t max_length) { std::string string; string.reserve(max_length); diff --git a/src/core/memory.h b/src/core/memory.h index cacf4fb1a..59b9ce2bb 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -132,6 +132,26 @@ public: */ bool IsValidVirtualAddress(VAddr vaddr) 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; + private: struct Impl; std::unique_ptr impl; @@ -162,8 +182,6 @@ void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size); void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size); void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size); -u8* GetPointer(VAddr vaddr); - std::string ReadCString(VAddr vaddr, std::size_t max_length); /** diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index bffae940c..11848fbce 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -52,7 +52,7 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) { const u64 aligned_size{Common::AlignUp(size, page_size)}; const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)}; - MapBackingMemory(gpu_addr, Memory::GetPointer(cpu_addr), aligned_size, cpu_addr); + MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr); ASSERT(system.CurrentProcess() ->VMManager() .SetMemoryAttribute(cpu_addr, size, Kernel::MemoryAttribute::DeviceMapped, @@ -67,7 +67,7 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size) const u64 aligned_size{Common::AlignUp(size, page_size)}; - MapBackingMemory(gpu_addr, Memory::GetPointer(cpu_addr), aligned_size, cpu_addr); + MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr); ASSERT(system.CurrentProcess() ->VMManager() .SetMemoryAttribute(cpu_addr, size, Kernel::MemoryAttribute::DeviceMapped, diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 85f05544c..a568a4343 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -19,6 +19,7 @@ #include "common/scope_exit.h" #include "core/core.h" #include "core/hle/kernel/process.h" +#include "core/memory.h" #include "core/settings.h" #include "video_core/engines/kepler_compute.h" #include "video_core/engines/maxwell_3d.h" @@ -838,7 +839,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config, MICROPROFILE_SCOPE(OpenGL_CacheManagement); const auto surface{ - texture_cache.TryFindFramebufferSurface(Memory::GetPointer(framebuffer_addr))}; + texture_cache.TryFindFramebufferSurface(system.Memory().GetPointer(framebuffer_addr))}; if (!surface) { return {}; } diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 7646cbb0e..a57a564f7 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -158,7 +158,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)}; const u32 bytes_per_pixel{VideoCore::Surface::GetBytesPerPixel(pixel_format)}; const u64 size_in_bytes{framebuffer.stride * framebuffer.height * bytes_per_pixel}; - const auto host_ptr{Memory::GetPointer(framebuffer_addr)}; + u8* const host_ptr{system.Memory().GetPointer(framebuffer_addr)}; rasterizer->FlushRegion(ToCacheAddr(host_ptr), size_in_bytes); // TODO(Rodrigo): Read this from HLE diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 959638747..46da81aaa 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -50,9 +50,9 @@ u64 VKBufferCache::UploadMemory(GPUVAddr gpu_addr, std::size_t size, u64 alignme // TODO: Figure out which size is the best for given games. cache &= size >= 2048; - const auto& host_ptr{Memory::GetPointer(*cpu_addr)}; + u8* const host_ptr{cpu_memory.GetPointer(*cpu_addr)}; if (cache) { - auto entry = TryGet(host_ptr); + const auto entry = TryGet(host_ptr); if (entry) { if (entry->GetSize() >= size && entry->GetAlignment() == alignment) { return entry->GetOffset(); @@ -64,7 +64,7 @@ u64 VKBufferCache::UploadMemory(GPUVAddr gpu_addr, std::size_t size, u64 alignme AlignBuffer(alignment); const u64 uploaded_offset = buffer_offset; - if (!host_ptr) { + if (host_ptr == nullptr) { return uploaded_offset; } From b2165c6b353be5e8117d1f9bc677bb198fa9d8cd Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 26 Nov 2019 15:48:19 -0500 Subject: [PATCH 07/13] core/memory: Migrate over ReadCString() to the Memory class This only had one usage spot, so this is fairly straightforward to convert over. --- src/core/hle/kernel/svc.cpp | 6 ++++-- src/core/memory.cpp | 33 +++++++++++++++++++-------------- src/core/memory.h | 19 +++++++++++++++++-- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 68bff11ec..738db528d 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -332,7 +332,9 @@ static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_ad /// Connect to an OS service given the port name, returns the handle to the port to out static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle, VAddr port_name_address) { - if (!system.Memory().IsValidVirtualAddress(port_name_address)) { + auto& memory = system.Memory(); + + if (!memory.IsValidVirtualAddress(port_name_address)) { LOG_ERROR(Kernel_SVC, "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}", port_name_address); @@ -341,7 +343,7 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle, static constexpr std::size_t PortNameMaxLength = 11; // Read 1 char beyond the max allowed port name to detect names that are too long. - std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1); + const std::string port_name = memory.ReadCString(port_name_address, PortNameMaxLength + 1); if (port_name.size() > PortNameMaxLength) { LOG_ERROR(Kernel_SVC, "Port name is too long, expected {} but got {}", PortNameMaxLength, port_name.size()); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 93cd67e39..fb824d710 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -210,6 +210,21 @@ struct Memory::Impl { return nullptr; } + 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) { + const char c = Read8(vaddr); + if (c == '\0') { + break; + } + string.push_back(c); + ++vaddr; + } + string.shrink_to_fit(); + return string; + } + /** * Maps a region of pages as a specific type. * @@ -299,6 +314,10 @@ const u8* Memory::GetPointer(VAddr vaddr) const { return impl->GetPointer(vaddr); } +std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) { + return impl->ReadCString(vaddr, max_length); +} + void SetCurrentPageTable(Kernel::Process& process) { current_page_table = &process.VMManager().page_table; @@ -315,20 +334,6 @@ bool IsKernelVirtualAddress(const VAddr vaddr) { return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END; } -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; diff --git a/src/core/memory.h b/src/core/memory.h index 59b9ce2bb..47765c8a0 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -152,6 +152,23 @@ public: */ const u8* GetPointer(VAddr vaddr) const; + /** + * 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); + private: struct Impl; std::unique_ptr impl; @@ -182,8 +199,6 @@ void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size); void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size); void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size); -std::string ReadCString(VAddr vaddr, std::size_t max_length); - /** * Mark each page touching the region as cached. */ From 849581075a230ad0f5419bb5d5e1f9e48e6cfd8a Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 26 Nov 2019 15:56:13 -0500 Subject: [PATCH 08/13] core/memory: Migrate over RasterizerMarkRegionCached() to the Memory class This is only used within the accelerated rasterizer in two places, so this is also a very trivial migration. --- src/core/memory.cpp | 130 +++++++++++----------- src/core/memory.h | 15 ++- src/video_core/rasterizer_accelerated.cpp | 4 +- 3 files changed, 79 insertions(+), 70 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index fb824d710..8c3489ed3 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -225,6 +225,69 @@ struct Memory::Impl { 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(); + } + } + } + } + /** * Maps a region of pages as a specific type. * @@ -318,6 +381,10 @@ std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) { return impl->ReadCString(vaddr, max_length); } +void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { + impl->RasterizerMarkRegionCached(vaddr, size, cached); +} + void SetCurrentPageTable(Kernel::Process& process) { current_page_table = &process.VMManager().page_table; @@ -334,69 +401,6 @@ bool IsKernelVirtualAddress(const VAddr vaddr) { return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END; } -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(addr); } diff --git a/src/core/memory.h b/src/core/memory.h index 47765c8a0..7cd348b49 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -169,6 +169,16 @@ public: */ 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(VAddr vaddr, u64 size, bool cached); + private: struct Impl; std::unique_ptr impl; @@ -199,9 +209,4 @@ void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size); void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size); void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size); -/** - * Mark each page touching the region as cached. - */ -void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached); - } // namespace Memory diff --git a/src/video_core/rasterizer_accelerated.cpp b/src/video_core/rasterizer_accelerated.cpp index f3dbadebc..fc6ecb899 100644 --- a/src/video_core/rasterizer_accelerated.cpp +++ b/src/video_core/rasterizer_accelerated.cpp @@ -48,9 +48,9 @@ void RasterizerAccelerated::UpdatePagesCachedCount(VAddr addr, u64 size, int del const u64 interval_size = interval_end_addr - interval_start_addr; if (delta > 0 && count == delta) { - Memory::RasterizerMarkRegionCached(interval_start_addr, interval_size, true); + cpu_memory.RasterizerMarkRegionCached(interval_start_addr, interval_size, true); } else if (delta < 0 && count == -delta) { - Memory::RasterizerMarkRegionCached(interval_start_addr, interval_size, false); + cpu_memory.RasterizerMarkRegionCached(interval_start_addr, interval_size, false); } else { ASSERT(count >= 0); } From 89ef3ef5752e42d0eb0bdfa23cc72d391db74216 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 26 Nov 2019 16:06:49 -0500 Subject: [PATCH 09/13] core/memory: Migrate over ZeroBlock() and CopyBlock() to the Memory class These currently aren't used anywhere in the codebase, so these are very trivial to move over to the Memory class. --- src/core/memory.cpp | 199 ++++++++++++++++++++++++-------------------- src/core/memory.h | 53 +++++++++++- 2 files changed, 161 insertions(+), 91 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 8c3489ed3..c939e980d 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -225,6 +225,99 @@ struct Memory::Impl { return string; } + 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; + + while (remaining_size > 0) { + const std::size_t copy_amount = + std::min(static_cast(PAGE_SIZE) - page_offset, remaining_size); + const auto current_vaddr = static_cast((page_index << PAGE_BITS) + page_offset); + + switch (page_table.attributes[page_index]) { + case Common::PageType::Unmapped: { + LOG_ERROR(HW_Memory, + "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", + current_vaddr, dest_addr, size); + break; + } + case Common::PageType::Memory: { + DEBUG_ASSERT(page_table.pointers[page_index]); + + u8* dest_ptr = page_table.pointers[page_index] + page_offset; + std::memset(dest_ptr, 0, copy_amount); + break; + } + case Common::PageType::RasterizerCachedMemory: { + u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); + system.GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount); + std::memset(host_ptr, 0, copy_amount); + break; + } + default: + UNREACHABLE(); + } + + page_index++; + page_offset = 0; + remaining_size -= copy_amount; + } + } + + void ZeroBlock(const VAddr dest_addr, const std::size_t size) { + ZeroBlock(*system.CurrentProcess(), dest_addr, size); + } + + 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; + + while (remaining_size > 0) { + const std::size_t copy_amount = + std::min(static_cast(PAGE_SIZE) - page_offset, remaining_size); + const auto current_vaddr = static_cast((page_index << PAGE_BITS) + page_offset); + + switch (page_table.attributes[page_index]) { + case Common::PageType::Unmapped: { + LOG_ERROR(HW_Memory, + "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", + current_vaddr, src_addr, size); + ZeroBlock(process, dest_addr, copy_amount); + 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 u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); + system.GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount); + WriteBlock(process, dest_addr, host_ptr, 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; + } + } + + void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) { + return CopyBlock(*system.CurrentProcess(), dest_addr, src_addr, size); + } + void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { if (vaddr == 0) { return; @@ -381,6 +474,23 @@ std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) { return impl->ReadCString(vaddr, max_length); } +void Memory::ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size) { + impl->ZeroBlock(process, dest_addr, size); +} + +void Memory::ZeroBlock(VAddr dest_addr, std::size_t size) { + impl->ZeroBlock(dest_addr, size); +} + +void Memory::CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr, + const std::size_t size) { + impl->CopyBlock(process, dest_addr, src_addr, size); +} + +void Memory::CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) { + impl->CopyBlock(dest_addr, src_addr, size); +} + void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { impl->RasterizerMarkRegionCached(vaddr, size, cached); } @@ -529,93 +639,4 @@ void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t 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; - - while (remaining_size > 0) { - const std::size_t copy_amount = - std::min(static_cast(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 Common::PageType::Unmapped: { - LOG_ERROR(HW_Memory, - "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", - current_vaddr, dest_addr, size); - break; - } - case Common::PageType::Memory: { - DEBUG_ASSERT(page_table.pointers[page_index]); - - u8* dest_ptr = page_table.pointers[page_index] + page_offset; - 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; - } - 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; - - while (remaining_size > 0) { - const std::size_t copy_amount = - std::min(static_cast(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 Common::PageType::Unmapped: { - LOG_ERROR(HW_Memory, - "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", - current_vaddr, src_addr, size); - ZeroBlock(process, dest_addr, copy_amount); - 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); - break; - } - default: - UNREACHABLE(); - } - - page_index++; - page_offset = 0; - dest_addr += static_cast(copy_amount); - src_addr += static_cast(copy_amount); - 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 diff --git a/src/core/memory.h b/src/core/memory.h index 7cd348b49..fc0013a96 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -169,6 +169,57 @@ public: */ std::string ReadCString(VAddr vaddr, std::size_t max_length); + /** + * Fills the specified address range within a process' address space with zeroes. + * + * @param process The process that will have a portion of its memory zeroed out. + * @param dest_addr The starting virtual address of the range to zero out. + * @param size The size of the address range to zero out, in bytes. + * + * @post The range [dest_addr, size) within the process' address space is + * filled with zeroes. + */ + void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size); + + /** + * Fills the specified address range within the current process' address space with zeroes. + * + * @param dest_addr The starting virtual address of the range to zero out. + * @param size The size of the address range to zero out, in bytes. + * + * @post The range [dest_addr, size) within the current process' address space is + * filled with zeroes. + */ + void ZeroBlock(VAddr dest_addr, 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); + + /** + * Copies data within the current process' address space to another location within the + * same 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 current process' address space + * contains the same data within the range [src_addr, size). + */ + void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size); + /** * Marks each page within the specified address range as cached or uncached. * @@ -206,7 +257,5 @@ void ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size); void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer, std::size_t size); void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size); -void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size); -void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size); } // namespace Memory From b05bfc603689419dc515a656b9fc711d79994f13 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 26 Nov 2019 16:29:34 -0500 Subject: [PATCH 10/13] core/memory: Migrate over Read{8, 16, 32, 64, Block} to the Memory class With all of the trivial parts of the memory interface moved over, we can get right into moving over the bits that are used. Note that this does require the use of GetInstance from the global system instance to be used within hle_ipc.cpp and the gdbstub. This is fine for the time being, as they both already rely on the global system instance in other functions. These will be removed in a change directed at both of these respectively. For now, it's sufficient, as it still accomplishes the goal of de-globalizing the memory code. --- src/audio_core/audio_renderer.cpp | 17 +- src/core/arm/arm_interface.cpp | 24 +- src/core/arm/dynarmic/arm_dynarmic.cpp | 12 +- src/core/gdbstub/gdbstub.cpp | 8 +- src/core/hle/kernel/address_arbiter.cpp | 28 ++- src/core/hle/kernel/hle_ipc.cpp | 12 +- src/core/hle/kernel/mutex.cpp | 2 +- src/core/hle/kernel/svc.cpp | 16 +- src/core/hle/service/audio/audout_u.cpp | 6 +- src/core/hle/service/ldr/ldr.cpp | 5 +- src/core/hle/service/lm/lm.cpp | 10 +- .../hle/service/nvdrv/devices/nvhost_gpu.cpp | 4 +- src/core/memory.cpp | 228 ++++++++++-------- src/core/memory.h | 85 ++++++- src/core/memory/cheat_engine.cpp | 5 +- src/core/memory/cheat_engine.h | 4 +- src/core/reporter.cpp | 2 +- src/core/tools/freezer.cpp | 8 +- src/yuzu/debugger/wait_tree.cpp | 7 +- 19 files changed, 305 insertions(+), 178 deletions(-) diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index 12e2b7901..c187d8ac5 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp @@ -259,9 +259,10 @@ void AudioRenderer::VoiceState::UpdateState() { } void AudioRenderer::VoiceState::RefreshBuffer(Memory::Memory& memory) { - std::vector new_samples(info.wave_buffer[wave_index].buffer_sz / sizeof(s16)); - Memory::ReadBlock(info.wave_buffer[wave_index].buffer_addr, new_samples.data(), - info.wave_buffer[wave_index].buffer_sz); + const auto wave_buffer_address = info.wave_buffer[wave_index].buffer_addr; + const auto wave_buffer_size = info.wave_buffer[wave_index].buffer_sz; + std::vector new_samples(wave_buffer_size / sizeof(s16)); + memory.ReadBlock(wave_buffer_address, new_samples.data(), wave_buffer_size); switch (static_cast(info.sample_format)) { case Codec::PcmFormat::Int16: { @@ -271,7 +272,7 @@ void AudioRenderer::VoiceState::RefreshBuffer(Memory::Memory& memory) { case Codec::PcmFormat::Adpcm: { // Decode ADPCM to PCM16 Codec::ADPCM_Coeff coeffs; - Memory::ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff)); + memory.ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff)); new_samples = Codec::DecodeADPCM(reinterpret_cast(new_samples.data()), new_samples.size() * sizeof(s16), coeffs, adpcm_state); break; @@ -314,13 +315,13 @@ void AudioRenderer::EffectState::UpdateState(Memory::Memory& memory) { out_status.state = EffectStatus::New; } else { if (info.type == Effect::Aux) { - ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_info) == 0, + ASSERT_MSG(memory.Read32(info.aux_info.return_buffer_info) == 0, "Aux buffers tried to update"); - ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_info) == 0, + ASSERT_MSG(memory.Read32(info.aux_info.send_buffer_info) == 0, "Aux buffers tried to update"); - ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_base) == 0, + ASSERT_MSG(memory.Read32(info.aux_info.return_buffer_base) == 0, "Aux buffers tried to update"); - ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_base) == 0, + ASSERT_MSG(memory.Read32(info.aux_info.send_buffer_base) == 0, "Aux buffers tried to update"); } } diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index dea192869..7e846ddd5 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp @@ -60,15 +60,15 @@ static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size."); using Symbols = std::vector>; -Symbols GetSymbols(VAddr text_offset) { - const auto mod_offset = text_offset + Memory::Read32(text_offset + 4); +Symbols GetSymbols(VAddr text_offset, Memory::Memory& memory) { + const auto mod_offset = text_offset + memory.Read32(text_offset + 4); if (mod_offset < text_offset || (mod_offset & 0b11) != 0 || - Memory::Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) { + memory.Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) { return {}; } - const auto dynamic_offset = Memory::Read32(mod_offset + 0x4) + mod_offset; + const auto dynamic_offset = memory.Read32(mod_offset + 0x4) + mod_offset; VAddr string_table_offset{}; VAddr symbol_table_offset{}; @@ -76,8 +76,8 @@ Symbols GetSymbols(VAddr text_offset) { VAddr dynamic_index = dynamic_offset; while (true) { - const auto tag = Memory::Read64(dynamic_index); - const auto value = Memory::Read64(dynamic_index + 0x8); + const u64 tag = memory.Read64(dynamic_index); + const u64 value = memory.Read64(dynamic_index + 0x8); dynamic_index += 0x10; if (tag == ELF_DYNAMIC_TAG_NULL) { @@ -105,11 +105,11 @@ Symbols GetSymbols(VAddr text_offset) { VAddr symbol_index = symbol_table_address; while (symbol_index < string_table_address) { ELFSymbol symbol{}; - Memory::ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol)); + memory.ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol)); VAddr string_offset = string_table_address + symbol.name_index; std::string name; - for (u8 c = Memory::Read8(string_offset); c != 0; c = Memory::Read8(++string_offset)) { + for (u8 c = memory.Read8(string_offset); c != 0; c = memory.Read8(++string_offset)) { name += static_cast(c); } @@ -141,17 +141,17 @@ constexpr u64 SEGMENT_BASE = 0x7100000000ull; std::vector ARM_Interface::GetBacktrace() const { std::vector out; + auto& memory = system.Memory(); auto fp = GetReg(29); auto lr = GetReg(30); - while (true) { out.push_back({"", 0, lr, 0}); if (!fp) { break; } - lr = Memory::Read64(fp + 8) - 4; - fp = Memory::Read64(fp); + lr = memory.Read64(fp + 8) - 4; + fp = memory.Read64(fp); } std::map modules; @@ -162,7 +162,7 @@ std::vector ARM_Interface::GetBacktrace() const { std::map symbols; for (const auto& module : modules) { - symbols.insert_or_assign(module.second, GetSymbols(module.first)); + symbols.insert_or_assign(module.second, GetSymbols(module.first, memory)); } for (auto& entry : out) { diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 2b396f1d6..585fb55a9 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -28,20 +28,20 @@ public: explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {} u8 MemoryRead8(u64 vaddr) override { - auto& s = parent.system; - return Memory::Read8(vaddr); + return parent.system.Memory().Read8(vaddr); } u16 MemoryRead16(u64 vaddr) override { - return Memory::Read16(vaddr); + return parent.system.Memory().Read16(vaddr); } u32 MemoryRead32(u64 vaddr) override { - return Memory::Read32(vaddr); + return parent.system.Memory().Read32(vaddr); } u64 MemoryRead64(u64 vaddr) override { - return Memory::Read64(vaddr); + return parent.system.Memory().Read64(vaddr); } Vector MemoryRead128(u64 vaddr) override { - return {Memory::Read64(vaddr), Memory::Read64(vaddr + 8)}; + auto& memory = parent.system.Memory(); + return {memory.Read64(vaddr), memory.Read64(vaddr + 8)}; } void MemoryWrite8(u64 vaddr, u8 value) override { diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 78e44f3bd..1c74a44d8 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -969,13 +969,13 @@ static void ReadMemory() { SendReply("E01"); } - const auto& memory = Core::System::GetInstance().Memory(); + auto& memory = Core::System::GetInstance().Memory(); if (!memory.IsValidVirtualAddress(addr)) { return SendReply("E00"); } std::vector data(len); - Memory::ReadBlock(addr, data.data(), len); + memory.ReadBlock(addr, data.data(), len); MemToGdbHex(reply, data.data(), len); reply[len * 2] = '\0'; @@ -1057,7 +1057,9 @@ static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) { breakpoint.active = true; breakpoint.addr = addr; breakpoint.len = len; - Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size()); + + auto& memory = Core::System::GetInstance().Memory(); + memory.ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size()); static constexpr std::array btrap{0x00, 0x7d, 0x20, 0xd4}; if (type == BreakpointType::Execute) { diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 7f9a559d2..07f0dac67 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -67,12 +67,14 @@ ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) { ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) { + auto& memory = system.Memory(); + // Ensure that we can write to the address. - if (!system.Memory().IsValidVirtualAddress(address)) { + if (!memory.IsValidVirtualAddress(address)) { return ERR_INVALID_ADDRESS_STATE; } - if (static_cast(Memory::Read32(address)) != value) { + if (static_cast(memory.Read32(address)) != value) { return ERR_INVALID_STATE; } @@ -82,8 +84,10 @@ ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) { + auto& memory = system.Memory(); + // Ensure that we can write to the address. - if (!system.Memory().IsValidVirtualAddress(address)) { + if (!memory.IsValidVirtualAddress(address)) { return ERR_INVALID_ADDRESS_STATE; } @@ -109,7 +113,7 @@ ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr a } } - if (static_cast(Memory::Read32(address)) != value) { + if (static_cast(memory.Read32(address)) != value) { return ERR_INVALID_STATE; } @@ -134,12 +138,14 @@ ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement) { + auto& memory = system.Memory(); + // Ensure that we can read the address. - if (!system.Memory().IsValidVirtualAddress(address)) { + if (!memory.IsValidVirtualAddress(address)) { return ERR_INVALID_ADDRESS_STATE; } - const s32 cur_value = static_cast(Memory::Read32(address)); + const s32 cur_value = static_cast(memory.Read32(address)); if (cur_value >= value) { return ERR_INVALID_STATE; } @@ -157,15 +163,19 @@ ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s6 } ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { + auto& memory = system.Memory(); + // Ensure that we can read the address. - if (!system.Memory().IsValidVirtualAddress(address)) { + if (!memory.IsValidVirtualAddress(address)) { return ERR_INVALID_ADDRESS_STATE; } + // Only wait for the address if equal. - if (static_cast(Memory::Read32(address)) != value) { + if (static_cast(memory.Read32(address)) != value) { return ERR_INVALID_STATE; } - // Short-circuit without rescheduling, if timeout is zero. + + // Short-circuit without rescheduling if timeout is zero. if (timeout == 0) { return RESULT_TIMEOUT; } diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index be24cef06..03745c449 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -214,10 +214,11 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTabl ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) { auto& owner_process = *thread.GetOwnerProcess(); auto& handle_table = owner_process.GetHandleTable(); + auto& memory = Core::System::GetInstance().Memory(); std::array dst_cmdbuf; - Memory::ReadBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), - dst_cmdbuf.size() * sizeof(u32)); + memory.ReadBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), + dst_cmdbuf.size() * sizeof(u32)); // The header was already built in the internal command buffer. Attempt to parse it to verify // the integrity and then copy it over to the target command buffer. @@ -282,15 +283,14 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) { std::vector HLERequestContext::ReadBuffer(int buffer_index) const { std::vector buffer; const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()}; + auto& memory = Core::System::GetInstance().Memory(); if (is_buffer_a) { buffer.resize(BufferDescriptorA()[buffer_index].Size()); - Memory::ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), - buffer.size()); + memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); } else { buffer.resize(BufferDescriptorX()[buffer_index].Size()); - Memory::ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), - buffer.size()); + memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); } return buffer; diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 8493d0f78..88eede436 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -79,7 +79,7 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle, // thread. ASSERT(requesting_thread == current_thread); - const u32 addr_value = Memory::Read32(address); + const u32 addr_value = system.Memory().Read32(address); // If the mutex isn't being held, just return success. if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) { diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 738db528d..a6c377cfc 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -454,7 +454,8 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}", handles_address, handle_count, nano_seconds); - if (!system.Memory().IsValidVirtualAddress(handles_address)) { + auto& memory = system.Memory(); + if (!memory.IsValidVirtualAddress(handles_address)) { LOG_ERROR(Kernel_SVC, "Handle address is not a valid virtual address, handle_address=0x{:016X}", handles_address); @@ -476,7 +477,7 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); for (u64 i = 0; i < handle_count; ++i) { - const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle)); + const Handle handle = memory.Read32(handles_address + i * sizeof(Handle)); const auto object = handle_table.Get(handle); if (object == nullptr) { @@ -618,13 +619,15 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { return; } + auto& memory = system.Memory(); + // This typically is an error code so we're going to assume this is the case if (sz == sizeof(u32)) { - LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", Memory::Read32(addr)); + LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", memory.Read32(addr)); } else { // We don't know what's in here so we'll hexdump it debug_buffer.resize(sz); - Memory::ReadBlock(addr, debug_buffer.data(), sz); + memory.ReadBlock(addr, debug_buffer.data(), sz); std::string hexdump; for (std::size_t i = 0; i < debug_buffer.size(); i++) { hexdump += fmt::format("{:02X} ", debug_buffer[i]); @@ -714,7 +717,7 @@ static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr addre } std::string str(len, '\0'); - Memory::ReadBlock(address, str.data(), str.size()); + system.Memory().ReadBlock(address, str.data(), str.size()); LOG_DEBUG(Debug_Emulated, "{}", str); } @@ -1674,6 +1677,7 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var const std::size_t current_core = system.CurrentCoreIndex(); auto& monitor = system.Monitor(); + auto& memory = system.Memory(); // Atomically read the value of the mutex. u32 mutex_val = 0; @@ -1683,7 +1687,7 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var monitor.SetExclusive(current_core, mutex_address); // If the mutex is not yet acquired, acquire it. - mutex_val = Memory::Read32(mutex_address); + mutex_val = memory.Read32(mutex_address); if (mutex_val != 0) { update_val = mutex_val | Mutex::MutexHasWaitersFlag; diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 6a29377e3..4fb2cbc4b 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -43,7 +43,8 @@ public: IAudioOut(Core::System& system, AudoutParams audio_params, AudioCore::AudioOut& audio_core, std::string&& device_name, std::string&& unique_name) : ServiceFramework("IAudioOut"), audio_core(audio_core), - device_name(std::move(device_name)), audio_params(audio_params) { + device_name(std::move(device_name)), + audio_params(audio_params), main_memory{system.Memory()} { // clang-format off static const FunctionInfo functions[] = { {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, @@ -137,7 +138,7 @@ private: const u64 tag{rp.Pop()}; std::vector samples(audio_buffer.buffer_size / sizeof(s16)); - Memory::ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size); + main_memory.ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size); if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) { IPC::ResponseBuilder rb{ctx, 2}; @@ -209,6 +210,7 @@ private: /// This is the event handle used to check if the audio buffer was released Kernel::EventPair buffer_event; + Memory::Memory& main_memory; }; AudOutU::AudOutU(Core::System& system_) : ServiceFramework("audout:u"), system{system_} { diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 88f903bfd..157aeec88 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -140,9 +140,10 @@ public: rb.Push(ERROR_INVALID_SIZE); return; } + // Read NRR data from memory std::vector nrr_data(nrr_size); - Memory::ReadBlock(nrr_address, nrr_data.data(), nrr_size); + system.Memory().ReadBlock(nrr_address, nrr_data.data(), nrr_size); NRRHeader header; std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader)); @@ -291,7 +292,7 @@ public: // Read NRO data from memory std::vector nro_data(nro_size); - Memory::ReadBlock(nro_address, nro_data.data(), nro_size); + system.Memory().ReadBlock(nro_address, nro_data.data(), nro_size); SHA256Hash hash{}; mbedtls_sha256_ret(nro_data.data(), nro_data.size(), hash.data(), 0); diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp index 74ecaef1b..346c8f899 100644 --- a/src/core/hle/service/lm/lm.cpp +++ b/src/core/hle/service/lm/lm.cpp @@ -36,15 +36,15 @@ private: MessageHeader header{}; VAddr addr{ctx.BufferDescriptorX()[0].Address()}; const VAddr end_addr{addr + ctx.BufferDescriptorX()[0].size}; - Memory::ReadBlock(addr, &header, sizeof(MessageHeader)); + memory.ReadBlock(addr, &header, sizeof(MessageHeader)); addr += sizeof(MessageHeader); FieldMap fields; while (addr < end_addr) { - const auto field = static_cast(Memory::Read8(addr++)); - const auto length = Memory::Read8(addr++); + const auto field = static_cast(memory.Read8(addr++)); + const auto length = memory.Read8(addr++); - if (static_cast(Memory::Read8(addr)) == Field::Skip) { + if (static_cast(memory.Read8(addr)) == Field::Skip) { ++addr; } @@ -55,7 +55,7 @@ private: } std::vector data(length); - Memory::ReadBlock(addr, data.data(), length); + memory.ReadBlock(addr, data.data(), length); fields.emplace(field, std::move(data)); } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 9de0ace22..6d8bca8bb 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -191,8 +191,8 @@ u32 nvhost_gpu::KickoffPB(const std::vector& input, std::vector& output, std::memcpy(entries.data(), input2.data(), params.num_entries * sizeof(Tegra::CommandListHeader)); } else { - Memory::ReadBlock(params.address, entries.data(), - params.num_entries * sizeof(Tegra::CommandListHeader)); + system.Memory().ReadBlock(params.address, entries.data(), + params.num_entries * sizeof(Tegra::CommandListHeader)); } UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0); UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index c939e980d..699c48107 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -59,37 +59,6 @@ u8* GetPointerFromVMA(VAddr vaddr) { return ::Memory::GetPointerFromVMA(*Core::System::GetInstance().CurrentProcess(), vaddr); } -template -T Read(const VAddr vaddr) { - const u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; - if (page_pointer != nullptr) { - // 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; - } - - const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; - switch (type) { - case Common::PageType::Unmapped: - 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: { - const u8* const host_ptr{GetPointerFromVMA(vaddr)}; - Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), sizeof(T)); - T value; - std::memcpy(&value, host_ptr, sizeof(T)); - return value; - } - default: - UNREACHABLE(); - } - return {}; -} - template void Write(const VAddr vaddr, const T data) { u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; @@ -210,6 +179,22 @@ struct Memory::Impl { return nullptr; } + u8 Read8(const VAddr addr) { + return Read(addr); + } + + u16 Read16(const VAddr addr) { + return Read(addr); + } + + u32 Read32(const VAddr addr) { + return Read(addr); + } + + u64 Read64(const VAddr addr) { + return Read(addr); + } + std::string ReadCString(VAddr vaddr, std::size_t max_length) { std::string string; string.reserve(max_length); @@ -225,6 +210,55 @@ struct Memory::Impl { return string; } + 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; + + while (remaining_size > 0) { + const std::size_t copy_amount = + std::min(static_cast(PAGE_SIZE) - page_offset, remaining_size); + const auto current_vaddr = static_cast((page_index << PAGE_BITS) + page_offset); + + switch (page_table.attributes[page_index]) { + case Common::PageType::Unmapped: { + 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); + break; + } + case Common::PageType::Memory: { + DEBUG_ASSERT(page_table.pointers[page_index]); + + const u8* const src_ptr = page_table.pointers[page_index] + page_offset; + std::memcpy(dest_buffer, src_ptr, copy_amount); + break; + } + case Common::PageType::RasterizerCachedMemory: { + const u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); + system.GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount); + std::memcpy(dest_buffer, host_ptr, copy_amount); + break; + } + default: + UNREACHABLE(); + } + + page_index++; + page_offset = 0; + dest_buffer = static_cast(dest_buffer) + copy_amount; + remaining_size -= copy_amount; + } + } + + void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) { + ReadBlock(*system.CurrentProcess(), src_addr, dest_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; @@ -425,6 +459,48 @@ struct Memory::Impl { } } + /** + * Reads a particular data type out of memory at the given virtual address. + * + * @param vaddr The virtual address to read the data type from. + * + * @tparam T The data type to read out of memory. This type *must* be + * trivially copyable, otherwise the behavior of this function + * is undefined. + * + * @returns The instance of T read from the specified virtual address. + */ + template + T Read(const VAddr vaddr) { + const u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; + if (page_pointer != nullptr) { + // 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; + } + + const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; + switch (type) { + case Common::PageType::Unmapped: + 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: { + const u8* const host_ptr = GetPointerFromVMA(vaddr); + system.GPU().FlushRegion(ToCacheAddr(host_ptr), sizeof(T)); + T value; + std::memcpy(&value, host_ptr, sizeof(T)); + return value; + } + default: + UNREACHABLE(); + } + return {}; + } + Core::System& system; }; @@ -470,10 +546,35 @@ const u8* Memory::GetPointer(VAddr vaddr) const { return impl->GetPointer(vaddr); } +u8 Memory::Read8(const VAddr addr) { + return impl->Read8(addr); +} + +u16 Memory::Read16(const VAddr addr) { + return impl->Read16(addr); +} + +u32 Memory::Read32(const VAddr addr) { + return impl->Read32(addr); +} + +u64 Memory::Read64(const VAddr addr) { + return impl->Read64(addr); +} + std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) { return impl->ReadCString(vaddr, max_length); } +void Memory::ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, + const std::size_t size) { + impl->ReadBlock(process, src_addr, dest_buffer, size); +} + +void Memory::ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) { + impl->ReadBlock(src_addr, dest_buffer, size); +} + void Memory::ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size) { impl->ZeroBlock(process, dest_addr, size); } @@ -511,71 +612,6 @@ bool IsKernelVirtualAddress(const VAddr vaddr) { return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END; } -u8 Read8(const VAddr addr) { - return Read(addr); -} - -u16 Read16(const VAddr addr) { - return Read(addr); -} - -u32 Read32(const VAddr addr) { - return Read(addr); -} - -u64 Read64(const VAddr addr) { - return Read(addr); -} - -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; - - while (remaining_size > 0) { - const std::size_t copy_amount = - std::min(static_cast(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 Common::PageType::Unmapped: { - 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); - break; - } - case Common::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 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; - } - default: - UNREACHABLE(); - } - - page_index++; - page_offset = 0; - dest_buffer = static_cast(dest_buffer) + copy_amount; - 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(addr, data); } diff --git a/src/core/memory.h b/src/core/memory.h index fc0013a96..cc6ab920e 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -152,6 +152,46 @@ public: */ 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); + /** * Reads a null-terminated string from the given virtual address. * This function will continually read characters until either: @@ -169,6 +209,44 @@ public: */ 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); + /** * Fills the specified address range within a process' address space with zeroes. * @@ -242,18 +320,11 @@ void SetCurrentPageTable(Kernel::Process& process); /// Determines if the given VAddr is a kernel address bool IsKernelVirtualAddress(VAddr vaddr); -u8 Read8(VAddr addr); -u16 Read16(VAddr addr); -u32 Read32(VAddr addr); -u64 Read64(VAddr addr); - void Write8(VAddr addr, u8 data); void Write16(VAddr addr, u16 data); void Write32(VAddr addr, u32 data); void Write64(VAddr addr, u64 data); -void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, std::size_t size); -void ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size); void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer, std::size_t size); void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size); diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index b73cc9fbd..d6745af8b 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp @@ -20,14 +20,13 @@ namespace Memory { constexpr s64 CHEAT_ENGINE_TICKS = static_cast(Core::Timing::BASE_CLOCK_RATE / 12); constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; -StandardVmCallbacks::StandardVmCallbacks(const Core::System& system, - const CheatProcessMetadata& metadata) +StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata) : metadata(metadata), system(system) {} StandardVmCallbacks::~StandardVmCallbacks() = default; void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) { - ReadBlock(SanitizeAddress(address), data, size); + system.Memory().ReadBlock(SanitizeAddress(address), data, size); } void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) { diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h index e3db90dac..3d6b2298a 100644 --- a/src/core/memory/cheat_engine.h +++ b/src/core/memory/cheat_engine.h @@ -24,7 +24,7 @@ namespace Memory { class StandardVmCallbacks : public DmntCheatVm::Callbacks { public: - StandardVmCallbacks(const Core::System& system, const CheatProcessMetadata& metadata); + StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata); ~StandardVmCallbacks() override; void MemoryRead(VAddr address, void* data, u64 size) override; @@ -37,7 +37,7 @@ private: VAddr SanitizeAddress(VAddr address) const; const CheatProcessMetadata& metadata; - const Core::System& system; + Core::System& system; }; // Intermediary class that parses a text file or other disk format for storing cheats into a diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index af0988d62..f95eee3b1 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp @@ -157,7 +157,7 @@ json GetHLEBufferDescriptorData(const std::vector& buffer, Memor if constexpr (read_value) { std::vector data(desc.Size()); - Memory::ReadBlock(desc.Address(), data.data(), desc.Size()); + memory.ReadBlock(desc.Address(), data.data(), desc.Size()); entry["data"] = Common::HexToString(data); } diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp index c7f42388f..ab66f35f9 100644 --- a/src/core/tools/freezer.cpp +++ b/src/core/tools/freezer.cpp @@ -18,13 +18,13 @@ constexpr s64 MEMORY_FREEZER_TICKS = static_cast(Core::Timing::BASE_CLOCK_R u64 MemoryReadWidth(Memory::Memory& memory, u32 width, VAddr addr) { switch (width) { case 1: - return Memory::Read8(addr); + return memory.Read8(addr); case 2: - return Memory::Read16(addr); + return memory.Read16(addr); case 4: - return Memory::Read32(addr); + return memory.Read32(addr); case 8: - return Memory::Read64(addr); + return memory.Read64(addr); default: UNREACHABLE(); return 0; diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 535b3ce90..727bd8a94 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -80,7 +80,7 @@ QString WaitTreeText::GetText() const { WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTable& handle_table) : mutex_address(mutex_address) { - mutex_value = Memory::Read32(mutex_address); + mutex_value = Core::System::GetInstance().Memory().Read32(mutex_address); owner_handle = static_cast(mutex_value & Kernel::Mutex::MutexOwnerMask); owner = handle_table.Get(owner_handle); } @@ -115,10 +115,11 @@ std::vector> WaitTreeCallstack::GetChildren() cons std::vector> list; constexpr std::size_t BaseRegister = 29; + auto& memory = Core::System::GetInstance().Memory(); u64 base_pointer = thread.GetContext().cpu_registers[BaseRegister]; while (base_pointer != 0) { - const u64 lr = Memory::Read64(base_pointer + sizeof(u64)); + const u64 lr = memory.Read64(base_pointer + sizeof(u64)); if (lr == 0) { break; } @@ -126,7 +127,7 @@ std::vector> WaitTreeCallstack::GetChildren() cons list.push_back(std::make_unique( tr("0x%1").arg(lr - sizeof(u32), 16, 16, QLatin1Char{'0'}))); - base_pointer = Memory::Read64(base_pointer); + base_pointer = memory.Read64(base_pointer); } return list; From e4c381b8850db96f162cfcf2cbe28b0e7c1f76f1 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 26 Nov 2019 17:39:57 -0500 Subject: [PATCH 11/13] core/memory: Migrate over Write{8, 16, 32, 64, Block} to the Memory class The Write functions are used slightly less than the Read functions, which make these a bit nicer to move over. The only adjustments we really need to make here are to Dynarmic's exclusive monitor instance. We need to keep a reference to the currently active memory instance to perform exclusive read/write operations. --- src/core/arm/dynarmic/arm_dynarmic.cpp | 30 ++-- src/core/arm/dynarmic/arm_dynarmic.h | 7 +- src/core/core_cpu.cpp | 5 +- src/core/core_cpu.h | 18 +- src/core/cpu_core_manager.cpp | 2 +- src/core/gdbstub/gdbstub.cpp | 16 +- src/core/hle/kernel/address_arbiter.cpp | 6 +- src/core/hle/kernel/hle_ipc.cpp | 9 +- src/core/hle/kernel/mutex.cpp | 4 +- src/core/hle/kernel/svc.cpp | 27 +-- src/core/memory.cpp | 220 ++++++++++++++---------- src/core/memory.h | 97 ++++++++++- src/core/memory/cheat_engine.cpp | 2 +- src/core/tools/freezer.cpp | 8 +- 14 files changed, 298 insertions(+), 153 deletions(-) diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 585fb55a9..f8c7f0efd 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -45,20 +45,21 @@ public: } void MemoryWrite8(u64 vaddr, u8 value) override { - Memory::Write8(vaddr, value); + parent.system.Memory().Write8(vaddr, value); } void MemoryWrite16(u64 vaddr, u16 value) override { - Memory::Write16(vaddr, value); + parent.system.Memory().Write16(vaddr, value); } void MemoryWrite32(u64 vaddr, u32 value) override { - Memory::Write32(vaddr, value); + parent.system.Memory().Write32(vaddr, value); } void MemoryWrite64(u64 vaddr, u64 value) override { - Memory::Write64(vaddr, value); + parent.system.Memory().Write64(vaddr, value); } void MemoryWrite128(u64 vaddr, Vector value) override { - Memory::Write64(vaddr, value[0]); - Memory::Write64(vaddr + 8, value[1]); + auto& memory = parent.system.Memory(); + memory.Write64(vaddr, value[0]); + memory.Write64(vaddr + 8, value[1]); } void InterpreterFallback(u64 pc, std::size_t num_instructions) override { @@ -266,7 +267,9 @@ void ARM_Dynarmic::PageTableChanged(Common::PageTable& page_table, jit = MakeJit(page_table, new_address_space_size_in_bits); } -DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(std::size_t core_count) : monitor(core_count) {} +DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory_, std::size_t core_count) + : monitor(core_count), memory{memory_} {} + DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default; void DynarmicExclusiveMonitor::SetExclusive(std::size_t core_index, VAddr addr) { @@ -279,29 +282,28 @@ void DynarmicExclusiveMonitor::ClearExclusive() { } bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) { - return monitor.DoExclusiveOperation(core_index, vaddr, 1, - [&] { Memory::Write8(vaddr, value); }); + return monitor.DoExclusiveOperation(core_index, vaddr, 1, [&] { memory.Write8(vaddr, value); }); } bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) { return monitor.DoExclusiveOperation(core_index, vaddr, 2, - [&] { Memory::Write16(vaddr, value); }); + [&] { memory.Write16(vaddr, value); }); } bool DynarmicExclusiveMonitor::ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) { return monitor.DoExclusiveOperation(core_index, vaddr, 4, - [&] { Memory::Write32(vaddr, value); }); + [&] { memory.Write32(vaddr, value); }); } bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) { return monitor.DoExclusiveOperation(core_index, vaddr, 8, - [&] { Memory::Write64(vaddr, value); }); + [&] { memory.Write64(vaddr, value); }); } bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) { return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] { - Memory::Write64(vaddr + 0, value[0]); - Memory::Write64(vaddr + 8, value[1]); + memory.Write64(vaddr + 0, value[0]); + memory.Write64(vaddr + 8, value[1]); }); } diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h index d08de475f..9cd475cfb 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ b/src/core/arm/dynarmic/arm_dynarmic.h @@ -12,6 +12,10 @@ #include "core/arm/exclusive_monitor.h" #include "core/arm/unicorn/arm_unicorn.h" +namespace Memory { +class Memory; +} + namespace Core { class ARM_Dynarmic_Callbacks; @@ -63,7 +67,7 @@ private: class DynarmicExclusiveMonitor final : public ExclusiveMonitor { public: - explicit DynarmicExclusiveMonitor(std::size_t core_count); + explicit DynarmicExclusiveMonitor(Memory::Memory& memory_, std::size_t core_count); ~DynarmicExclusiveMonitor() override; void SetExclusive(std::size_t core_index, VAddr addr) override; @@ -78,6 +82,7 @@ public: private: friend class ARM_Dynarmic; Dynarmic::A64::ExclusiveMonitor monitor; + Memory::Memory& memory; }; } // namespace Core diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp index 233ea572c..cf3fe0b0b 100644 --- a/src/core/core_cpu.cpp +++ b/src/core/core_cpu.cpp @@ -66,9 +66,10 @@ Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_ba Cpu::~Cpu() = default; -std::unique_ptr Cpu::MakeExclusiveMonitor(std::size_t num_cores) { +std::unique_ptr Cpu::MakeExclusiveMonitor( + [[maybe_unused]] Memory::Memory& memory, [[maybe_unused]] std::size_t num_cores) { #ifdef ARCHITECTURE_x86_64 - return std::make_unique(num_cores); + return std::make_unique(memory, num_cores); #else // TODO(merry): Passthrough exclusive monitor return nullptr; diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h index cafca8df7..78f5021a2 100644 --- a/src/core/core_cpu.h +++ b/src/core/core_cpu.h @@ -24,6 +24,10 @@ namespace Core::Timing { class CoreTiming; } +namespace Memory { +class Memory; +} + namespace Core { class ARM_Interface; @@ -86,7 +90,19 @@ public: void Shutdown(); - static std::unique_ptr MakeExclusiveMonitor(std::size_t num_cores); + /** + * Creates an exclusive monitor to handle exclusive reads/writes. + * + * @param memory The current memory subsystem that the monitor may wish + * to keep track of. + * + * @param num_cores The number of cores to assume about the CPU. + * + * @returns The constructed exclusive monitor instance, or nullptr if the current + * CPU backend is unable to use an exclusive monitor. + */ + static std::unique_ptr MakeExclusiveMonitor(Memory::Memory& memory, + std::size_t num_cores); private: void Reschedule(); diff --git a/src/core/cpu_core_manager.cpp b/src/core/cpu_core_manager.cpp index 8efd410bb..f04a34133 100644 --- a/src/core/cpu_core_manager.cpp +++ b/src/core/cpu_core_manager.cpp @@ -25,7 +25,7 @@ CpuCoreManager::~CpuCoreManager() = default; void CpuCoreManager::Initialize() { barrier = std::make_unique(); - exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size()); + exclusive_monitor = Cpu::MakeExclusiveMonitor(system.Memory(), cores.size()); for (std::size_t index = 0; index < cores.size(); ++index) { cores[index] = std::make_unique(system, *exclusive_monitor, *barrier, index); diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 1c74a44d8..37cb28848 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -508,8 +508,9 @@ static void RemoveBreakpoint(BreakpointType type, VAddr addr) { bp->second.len, bp->second.addr, static_cast(type)); if (type == BreakpointType::Execute) { - Memory::WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size()); - Core::System::GetInstance().InvalidateCpuInstructionCaches(); + auto& system = Core::System::GetInstance(); + system.Memory().WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size()); + system.InvalidateCpuInstructionCaches(); } p.erase(addr); } @@ -993,14 +994,14 @@ static void WriteMemory() { const u64 len = HexToLong(start_offset, static_cast(len_pos - start_offset)); auto& system = Core::System::GetInstance(); - const auto& memory = system.Memory(); + auto& memory = system.Memory(); if (!memory.IsValidVirtualAddress(addr)) { return SendReply("E00"); } std::vector data(len); GdbHexToMem(data.data(), len_pos + 1, len); - Memory::WriteBlock(addr, data.data(), len); + memory.WriteBlock(addr, data.data(), len); system.InvalidateCpuInstructionCaches(); SendReply("OK"); } @@ -1058,13 +1059,14 @@ static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) { breakpoint.addr = addr; breakpoint.len = len; - auto& memory = Core::System::GetInstance().Memory(); + auto& system = Core::System::GetInstance(); + auto& memory = system.Memory(); memory.ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size()); static constexpr std::array btrap{0x00, 0x7d, 0x20, 0xd4}; if (type == BreakpointType::Execute) { - Memory::WriteBlock(addr, btrap.data(), btrap.size()); - Core::System::GetInstance().InvalidateCpuInstructionCaches(); + memory.WriteBlock(addr, btrap.data(), btrap.size()); + system.InvalidateCpuInstructionCaches(); } p.insert({addr, breakpoint}); diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 07f0dac67..98d07fa5b 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -78,7 +78,7 @@ ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 return ERR_INVALID_STATE; } - Memory::Write32(address, static_cast(value + 1)); + memory.Write32(address, static_cast(value + 1)); return SignalToAddressOnly(address, num_to_wake); } @@ -117,7 +117,7 @@ ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr a return ERR_INVALID_STATE; } - Memory::Write32(address, static_cast(updated_value)); + memory.Write32(address, static_cast(updated_value)); WakeThreads(waiting_threads, num_to_wake); return RESULT_SUCCESS; } @@ -151,7 +151,7 @@ ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s6 } if (should_decrement) { - Memory::Write32(address, static_cast(cur_value - 1)); + memory.Write32(address, static_cast(cur_value - 1)); } // Short-circuit without rescheduling, if timeout is zero. diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 03745c449..8b01567a8 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -274,8 +274,8 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) { } // Copy the translated command buffer back into the thread's command buffer area. - Memory::WriteBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), - dst_cmdbuf.size() * sizeof(u32)); + memory.WriteBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), + dst_cmdbuf.size() * sizeof(u32)); return RESULT_SUCCESS; } @@ -311,10 +311,11 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, size = buffer_size; // TODO(bunnei): This needs to be HW tested } + auto& memory = Core::System::GetInstance().Memory(); if (is_buffer_b) { - Memory::WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); + memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); } else { - Memory::WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); + memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); } return size; diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 88eede436..061e9bcb0 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -117,7 +117,7 @@ ResultCode Mutex::Release(VAddr address) { // There are no more threads waiting for the mutex, release it completely. if (thread == nullptr) { - Memory::Write32(address, 0); + system.Memory().Write32(address, 0); return RESULT_SUCCESS; } @@ -132,7 +132,7 @@ ResultCode Mutex::Release(VAddr address) { } // Grant the mutex to the next waiting thread and resume it. - Memory::Write32(address, mutex_value); + system.Memory().Write32(address, mutex_value); ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); thread->ResumeFromWait(); diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index a6c377cfc..db3ae3eb8 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1120,7 +1120,7 @@ static ResultCode GetThreadContext(Core::System& system, VAddr thread_context, H std::fill(ctx.vector_registers.begin() + 16, ctx.vector_registers.end(), u128{}); } - Memory::WriteBlock(thread_context, &ctx, sizeof(ctx)); + system.Memory().WriteBlock(thread_context, &ctx, sizeof(ctx)); return RESULT_SUCCESS; } @@ -1280,20 +1280,21 @@ static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_add return ERR_INVALID_HANDLE; } + auto& memory = system.Memory(); const auto& vm_manager = process->VMManager(); const MemoryInfo memory_info = vm_manager.QueryMemory(address); - Memory::Write64(memory_info_address, memory_info.base_address); - Memory::Write64(memory_info_address + 8, memory_info.size); - Memory::Write32(memory_info_address + 16, memory_info.state); - Memory::Write32(memory_info_address + 20, memory_info.attributes); - Memory::Write32(memory_info_address + 24, memory_info.permission); - Memory::Write32(memory_info_address + 32, memory_info.ipc_ref_count); - Memory::Write32(memory_info_address + 28, memory_info.device_ref_count); - Memory::Write32(memory_info_address + 36, 0); + memory.Write64(memory_info_address, memory_info.base_address); + memory.Write64(memory_info_address + 8, memory_info.size); + memory.Write32(memory_info_address + 16, memory_info.state); + memory.Write32(memory_info_address + 20, memory_info.attributes); + memory.Write32(memory_info_address + 24, memory_info.permission); + memory.Write32(memory_info_address + 32, memory_info.ipc_ref_count); + memory.Write32(memory_info_address + 28, memory_info.device_ref_count); + memory.Write32(memory_info_address + 36, 0); // Page info appears to be currently unused by the kernel and is always set to zero. - Memory::Write32(page_info_address, 0); + memory.Write32(page_info_address, 0); return RESULT_SUCCESS; } @@ -2290,12 +2291,13 @@ static ResultCode GetProcessList(Core::System& system, u32* out_num_processes, return ERR_INVALID_ADDRESS_STATE; } + auto& memory = system.Memory(); const auto& process_list = kernel.GetProcessList(); const auto num_processes = process_list.size(); const auto copy_amount = std::min(std::size_t{out_process_ids_size}, num_processes); for (std::size_t i = 0; i < copy_amount; ++i) { - Memory::Write64(out_process_ids, process_list[i]->GetProcessID()); + memory.Write64(out_process_ids, process_list[i]->GetProcessID()); out_process_ids += sizeof(u64); } @@ -2329,13 +2331,14 @@ static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAdd return ERR_INVALID_ADDRESS_STATE; } + auto& memory = system.Memory(); const auto& thread_list = current_process->GetThreadList(); const auto num_threads = thread_list.size(); const auto copy_amount = std::min(std::size_t{out_thread_ids_size}, num_threads); auto list_iter = thread_list.cbegin(); for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) { - Memory::Write64(out_thread_ids, (*list_iter)->GetThreadID()); + memory.Write64(out_thread_ids, (*list_iter)->GetThreadID()); out_thread_ids += sizeof(u64); } diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 699c48107..5c940a82e 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -58,35 +58,6 @@ u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) { u8* GetPointerFromVMA(VAddr vaddr) { return ::Memory::GetPointerFromVMA(*Core::System::GetInstance().CurrentProcess(), vaddr); } - -template -void Write(const VAddr vaddr, const T data) { - u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; - if (page_pointer != nullptr) { - // 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: - LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, - static_cast(data), vaddr); - return; - case Common::PageType::Memory: - ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); - break; - case Common::PageType::RasterizerCachedMemory: { - u8* const host_ptr{GetPointerFromVMA(vaddr)}; - Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T)); - std::memcpy(host_ptr, &data, sizeof(T)); - break; - } - default: - UNREACHABLE(); - } -} } // Anonymous namespace // Implementation class used to keep the specifics of the memory subsystem hidden @@ -195,6 +166,22 @@ struct Memory::Impl { return Read(addr); } + void Write8(const VAddr addr, const u8 data) { + Write(addr, data); + } + + void Write16(const VAddr addr, const u16 data) { + Write(addr, data); + } + + void Write32(const VAddr addr, const u32 data) { + Write(addr, data); + } + + void Write64(const VAddr addr, const u64 data) { + Write(addr, data); + } + std::string ReadCString(VAddr vaddr, std::size_t max_length) { std::string string; string.reserve(max_length); @@ -259,6 +246,53 @@ struct Memory::Impl { ReadBlock(*system.CurrentProcess(), src_addr, dest_buffer, size); } + 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; + + while (remaining_size > 0) { + const std::size_t copy_amount = + std::min(static_cast(PAGE_SIZE) - page_offset, remaining_size); + const auto current_vaddr = static_cast((page_index << PAGE_BITS) + page_offset); + + switch (page_table.attributes[page_index]) { + case Common::PageType::Unmapped: { + LOG_ERROR(HW_Memory, + "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", + current_vaddr, dest_addr, size); + break; + } + case Common::PageType::Memory: { + DEBUG_ASSERT(page_table.pointers[page_index]); + + u8* const dest_ptr = page_table.pointers[page_index] + page_offset; + std::memcpy(dest_ptr, src_buffer, copy_amount); + break; + } + case Common::PageType::RasterizerCachedMemory: { + u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); + system.GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount); + std::memcpy(host_ptr, 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 WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { + WriteBlock(*system.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; @@ -501,6 +535,46 @@ struct Memory::Impl { return {}; } + /** + * Writes a particular data type to memory at the given virtual address. + * + * @param vaddr The virtual address to write the data type to. + * + * @tparam T The data type to write to memory. This type *must* be + * trivially copyable, otherwise the behavior of this function + * is undefined. + * + * @returns The instance of T write to the specified virtual address. + */ + template + void Write(const VAddr vaddr, const T data) { + u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; + if (page_pointer != nullptr) { + // NOTE: Avoid adding any extra logic to this fast-path block + std::memcpy(&page_pointer[vaddr & PAGE_MASK], &data, sizeof(T)); + return; + } + + const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; + switch (type) { + case Common::PageType::Unmapped: + LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, + static_cast(data), vaddr); + return; + case Common::PageType::Memory: + ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); + break; + case Common::PageType::RasterizerCachedMemory: { + u8* const host_ptr{GetPointerFromVMA(vaddr)}; + system.GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T)); + std::memcpy(host_ptr, &data, sizeof(T)); + break; + } + default: + UNREACHABLE(); + } + } + Core::System& system; }; @@ -562,6 +636,22 @@ u64 Memory::Read64(const VAddr addr) { return impl->Read64(addr); } +void Memory::Write8(VAddr addr, u8 data) { + impl->Write8(addr, data); +} + +void Memory::Write16(VAddr addr, u16 data) { + impl->Write16(addr, data); +} + +void Memory::Write32(VAddr addr, u32 data) { + impl->Write32(addr, data); +} + +void Memory::Write64(VAddr addr, u64 data) { + impl->Write64(addr, data); +} + std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) { return impl->ReadCString(vaddr, max_length); } @@ -575,6 +665,15 @@ void Memory::ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_ impl->ReadBlock(src_addr, dest_buffer, size); } +void Memory::WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer, + std::size_t size) { + impl->WriteBlock(process, dest_addr, src_buffer, size); +} + +void Memory::WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { + impl->WriteBlock(dest_addr, src_buffer, size); +} + void Memory::ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size) { impl->ZeroBlock(process, dest_addr, size); } @@ -612,67 +711,4 @@ bool IsKernelVirtualAddress(const VAddr vaddr) { return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END; } -void Write8(const VAddr addr, const u8 data) { - Write(addr, data); -} - -void Write16(const VAddr addr, const u16 data) { - Write(addr, data); -} - -void Write32(const VAddr addr, const u32 data) { - Write(addr, data); -} - -void Write64(const VAddr addr, const u64 data) { - Write(addr, data); -} - -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; - - while (remaining_size > 0) { - const std::size_t copy_amount = - std::min(static_cast(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 Common::PageType::Unmapped: { - LOG_ERROR(HW_Memory, - "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", - current_vaddr, dest_addr, size); - break; - } - case Common::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 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; - } - default: - UNREACHABLE(); - } - - page_index++; - page_offset = 0; - src_buffer = static_cast(src_buffer) + copy_amount; - remaining_size -= copy_amount; - } -} - -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); -} - } // namespace Memory diff --git a/src/core/memory.h b/src/core/memory.h index cc6ab920e..7878f3fb1 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -192,6 +192,50 @@ public: */ 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: @@ -247,6 +291,50 @@ public: */ 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 the current 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 current process' address space. + * @param size The size of the data to write, in bytes. + * + * @post The address range [dest_addr, size) in the current 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); + /** * Fills the specified address range within a process' address space with zeroes. * @@ -320,13 +408,4 @@ void SetCurrentPageTable(Kernel::Process& process); /// Determines if the given VAddr is a kernel address bool IsKernelVirtualAddress(VAddr vaddr); -void Write8(VAddr addr, u8 data); -void Write16(VAddr addr, u16 data); -void Write32(VAddr addr, u32 data); -void Write64(VAddr addr, u64 data); - -void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer, - std::size_t size); -void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size); - } // namespace Memory diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index d6745af8b..d1e6bed93 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp @@ -30,7 +30,7 @@ void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) { } void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) { - WriteBlock(SanitizeAddress(address), data, size); + system.Memory().WriteBlock(SanitizeAddress(address), data, size); } u64 StandardVmCallbacks::HidKeysDown() { diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp index ab66f35f9..55e0dbc49 100644 --- a/src/core/tools/freezer.cpp +++ b/src/core/tools/freezer.cpp @@ -34,16 +34,16 @@ u64 MemoryReadWidth(Memory::Memory& memory, u32 width, VAddr addr) { void MemoryWriteWidth(Memory::Memory& memory, u32 width, VAddr addr, u64 value) { switch (width) { case 1: - Memory::Write8(addr, static_cast(value)); + memory.Write8(addr, static_cast(value)); break; case 2: - Memory::Write16(addr, static_cast(value)); + memory.Write16(addr, static_cast(value)); break; case 4: - Memory::Write32(addr, static_cast(value)); + memory.Write32(addr, static_cast(value)); break; case 8: - Memory::Write64(addr, value); + memory.Write64(addr, value); break; default: UNREACHABLE(); From 50a518be69ef871a674afd91caebdf31cbda4485 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 26 Nov 2019 18:28:44 -0500 Subject: [PATCH 12/13] core/memory: Migrate over GetPointerFromVMA() to the Memory class Now that everything else is migrated over, this is essentially just code relocation and conversion of a global accessor to the class member variable. All that remains is to migrate over the page table. --- src/core/memory.cpp | 72 ++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 5c940a82e..a49e971aa 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -22,42 +22,6 @@ namespace Memory { namespace { Common::PageTable* current_page_table = nullptr; - -/** - * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned) - * using a VMA from the current process - */ -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. - */ -u8* GetPointerFromVMA(VAddr vaddr) { - return ::Memory::GetPointerFromVMA(*Core::System::GetInstance().CurrentProcess(), vaddr); -} } // Anonymous namespace // Implementation class used to keep the specifics of the memory subsystem hidden @@ -135,6 +99,42 @@ struct Memory::Impl { return IsValidVirtualAddress(*system.CurrentProcess(), vaddr); } + /** + * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned) + * using a VMA from the current process + */ + 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. + */ + u8* GetPointerFromVMA(VAddr vaddr) { + return GetPointerFromVMA(*system.CurrentProcess(), vaddr); + } + u8* GetPointer(const VAddr vaddr) { u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; if (page_pointer != nullptr) { From e7e939104bb167babec7b5f7d5d8390c85f3cbf4 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 26 Nov 2019 18:34:30 -0500 Subject: [PATCH 13/13] core/memory; Migrate over SetCurrentPageTable() to the Memory class Now that literally every other API function is converted over to the Memory class, we can just move the file-local page table into the Memory implementation class, finally getting rid of global state within the memory code. --- src/core/hle/kernel/kernel.cpp | 18 +++++++++++------- src/core/memory.cpp | 31 ++++++++++++++++--------------- src/core/memory.h | 11 +++++++---- 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index a9851113a..1c90546a4 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -154,6 +154,16 @@ struct KernelCore::Impl { system.CoreTiming().ScheduleEvent(time_interval, preemption_event); } + void MakeCurrentProcess(Process* process) { + current_process = process; + + if (process == nullptr) { + return; + } + + system.Memory().SetCurrentPageTable(*process); + } + std::atomic next_object_id{0}; std::atomic next_kernel_process_id{Process::InitialKIPIDMin}; std::atomic next_user_process_id{Process::ProcessIDMin}; @@ -208,13 +218,7 @@ void KernelCore::AppendNewProcess(std::shared_ptr process) { } void KernelCore::MakeCurrentProcess(Process* process) { - impl->current_process = process; - - if (process == nullptr) { - return; - } - - Memory::SetCurrentPageTable(*process); + impl->MakeCurrentProcess(process); } Process* KernelCore::CurrentProcess() { diff --git a/src/core/memory.cpp b/src/core/memory.cpp index a49e971aa..91bf07a92 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -20,9 +20,6 @@ #include "video_core/gpu.h" namespace Memory { -namespace { -Common::PageTable* current_page_table = nullptr; -} // Anonymous namespace // 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 @@ -30,6 +27,17 @@ Common::PageTable* current_page_table = nullptr; struct Memory::Impl { explicit Impl(Core::System& system_) : system{system_} {} + void SetCurrentPageTable(Kernel::Process& process) { + current_page_table = &process.VMManager().page_table; + + const std::size_t address_space_width = process.VMManager().GetAddressSpaceWidth(); + + 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); + } + 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); @@ -575,12 +583,17 @@ struct Memory::Impl { } } + Common::PageTable* current_page_table = nullptr; Core::System& system; }; Memory::Memory(Core::System& system) : impl{std::make_unique(system)} {} Memory::~Memory() = default; +void Memory::SetCurrentPageTable(Kernel::Process& process) { + impl->SetCurrentPageTable(process); +} + void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target) { impl->MapMemoryRegion(page_table, base, size, target); } @@ -695,18 +708,6 @@ void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { impl->RasterizerMarkRegionCached(vaddr, size, cached); } -void SetCurrentPageTable(Kernel::Process& process) { - current_page_table = &process.VMManager().page_table; - - const std::size_t address_space_width = process.VMManager().GetAddressSpaceWidth(); - - auto& system = Core::System::GetInstance(); - 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); -} - bool IsKernelVirtualAddress(const VAddr vaddr) { return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END; } diff --git a/src/core/memory.h b/src/core/memory.h index 7878f3fb1..1428a6d60 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -58,6 +58,13 @@ public: Memory(Memory&&) = default; Memory& operator=(Memory&&) = default; + /** + * Changes the currently active page table to that of the given process instance. + * + * @param process The process to use the page table of. + */ + void SetCurrentPageTable(Kernel::Process& process); + /** * Maps an allocated buffer onto a region of the emulated process address space. * @@ -401,10 +408,6 @@ private: std::unique_ptr impl; }; -/// Changes the currently active page table to that of -/// the given process instance. -void SetCurrentPageTable(Kernel::Process& process); - /// Determines if the given VAddr is a kernel address bool IsKernelVirtualAddress(VAddr vaddr);