diff options
Diffstat (limited to 'src/core')
38 files changed, 1241 insertions, 674 deletions
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/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index 372612c9b..7e846ddd5 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; @@ -61,15 +60,15 @@ static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size."); using Symbols = std::vector<std::pair<ELFSymbol, std::string>>; -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{}; @@ -77,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) { @@ -106,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<char>(c); } @@ -142,28 +141,28 @@ constexpr u64 SEGMENT_BASE = 0x7100000000ull; std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const { std::vector<BacktraceEntry> 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<VAddr, std::string> modules; - auto& loader{System::GetInstance().GetAppLoader()}; + auto& loader{system.GetAppLoader()}; if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) { return {}; } std::map<std::string, Symbols> 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/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<u64, 31> 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..f8c7f0efd 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -28,36 +28,38 @@ public: explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {} u8 MemoryRead8(u64 vaddr) override { - 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 { - 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 { @@ -171,9 +173,10 @@ void ARM_Dynarmic::Step() { ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index) - : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system}, - core_index{core_index}, system{system}, - exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {} + : ARM_Interface{system}, + cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system}, + core_index{core_index}, exclusive_monitor{ + dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {} ARM_Dynarmic::~ARM_Dynarmic() = default; @@ -264,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) { @@ -277,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 504d46c68..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; @@ -58,13 +62,12 @@ private: ARM_Unicorn inner_unicorn; std::size_t core_index; - System& system; DynarmicExclusiveMonitor& exclusive_monitor; }; 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; @@ -79,6 +82,7 @@ public: private: friend class ARM_Dynarmic; Dynarmic::A64::ExclusiveMonitor monitor; + Memory::Memory& memory; }; } // namespace Core 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/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<VideoCore::RendererBase> renderer; std::unique_ptr<Tegra::GPU> gpu_core; std::shared_ptr<Tegra::DebugContext> debug_context; - std::unique_ptr<Core::Hardware::InterruptManager> interrupt_manager; + std::unique_ptr<Hardware::InterruptManager> 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/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<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) { +std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor( + [[maybe_unused]] Memory::Memory& memory, [[maybe_unused]] std::size_t num_cores) { #ifdef ARCHITECTURE_x86_64 - return std::make_unique<DynarmicExclusiveMonitor>(num_cores); + return std::make_unique<DynarmicExclusiveMonitor>(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<ExclusiveMonitor> 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<ExclusiveMonitor> 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<CpuBarrier>(); - 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<Cpu>(system, *exclusive_monitor, *barrier, index); diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 54ed680db..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<int>(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); } @@ -969,12 +970,13 @@ static void ReadMemory() { SendReply("E01"); } - if (!Memory::IsValidVirtualAddress(addr)) { + auto& memory = Core::System::GetInstance().Memory(); + if (!memory.IsValidVirtualAddress(addr)) { return SendReply("E00"); } std::vector<u8> data(len); - Memory::ReadBlock(addr, data.data(), len); + memory.ReadBlock(addr, data.data(), len); MemToGdbHex(reply, data.data(), len); reply[len * 2] = '\0'; @@ -984,22 +986,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<u64>(addr_pos - start_offset)); + const auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); + const VAddr addr = HexToLong(start_offset, static_cast<u64>(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<u64>(len_pos - start_offset)); + const auto len_pos = std::find(start_offset, command_buffer + command_length, ':'); + const u64 len = HexToLong(start_offset, static_cast<u64>(len_pos - start_offset)); - if (!Memory::IsValidVirtualAddress(addr)) { + auto& system = Core::System::GetInstance(); + auto& memory = system.Memory(); + if (!memory.IsValidVirtualAddress(addr)) { return SendReply("E00"); } std::vector<u8> data(len); - GdbHexToMem(data.data(), len_pos + 1, len); - Memory::WriteBlock(addr, data.data(), len); - Core::System::GetInstance().InvalidateCpuInstructionCaches(); + memory.WriteBlock(addr, data.data(), len); + system.InvalidateCpuInstructionCaches(); SendReply("OK"); } @@ -1055,12 +1058,15 @@ 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& system = Core::System::GetInstance(); + auto& memory = system.Memory(); + memory.ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size()); static constexpr std::array<u8, 4> 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 4859954cb..98d07fa5b 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -67,23 +67,27 @@ 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 (!Memory::IsValidVirtualAddress(address)) { + if (!memory.IsValidVirtualAddress(address)) { return ERR_INVALID_ADDRESS_STATE; } - if (static_cast<s32>(Memory::Read32(address)) != value) { + if (static_cast<s32>(memory.Read32(address)) != value) { return ERR_INVALID_STATE; } - Memory::Write32(address, static_cast<u32>(value + 1)); + memory.Write32(address, static_cast<u32>(value + 1)); return SignalToAddressOnly(address, num_to_wake); } ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) { + auto& memory = system.Memory(); + // Ensure that we can write to the address. - if (!Memory::IsValidVirtualAddress(address)) { + if (!memory.IsValidVirtualAddress(address)) { return ERR_INVALID_ADDRESS_STATE; } @@ -109,11 +113,11 @@ ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr a } } - if (static_cast<s32>(Memory::Read32(address)) != value) { + if (static_cast<s32>(memory.Read32(address)) != value) { return ERR_INVALID_STATE; } - Memory::Write32(address, static_cast<u32>(updated_value)); + memory.Write32(address, static_cast<u32>(updated_value)); WakeThreads(waiting_threads, num_to_wake); return RESULT_SUCCESS; } @@ -134,18 +138,20 @@ 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 (!Memory::IsValidVirtualAddress(address)) { + if (!memory.IsValidVirtualAddress(address)) { return ERR_INVALID_ADDRESS_STATE; } - const s32 cur_value = static_cast<s32>(Memory::Read32(address)); + const s32 cur_value = static_cast<s32>(memory.Read32(address)); if (cur_value >= value) { return ERR_INVALID_STATE; } if (should_decrement) { - Memory::Write32(address, static_cast<u32>(cur_value - 1)); + memory.Write32(address, static_cast<u32>(cur_value - 1)); } // Short-circuit without rescheduling, if timeout is zero. @@ -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 (!Memory::IsValidVirtualAddress(address)) { + if (!memory.IsValidVirtualAddress(address)) { return ERR_INVALID_ADDRESS_STATE; } + // Only wait for the address if equal. - if (static_cast<s32>(Memory::Read32(address)) != value) { + if (static_cast<s32>(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/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/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index be24cef06..8b01567a8 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<u32, IPC::COMMAND_BUFFER_LENGTH> 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. @@ -273,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; } @@ -282,15 +283,14 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) { std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const { std::vector<u8> 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; @@ -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/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<u32> next_object_id{0}; std::atomic<u64> next_kernel_process_id{Process::InitialKIPIDMin}; std::atomic<u64> next_user_process_id{Process::ProcessIDMin}; @@ -208,13 +218,7 @@ void KernelCore::AppendNewProcess(std::shared_ptr<Process> 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/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 8493d0f78..061e9bcb0 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)) { @@ -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/server_session.cpp b/src/core/hle/kernel/server_session.cpp index c7db21eb2..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 { @@ -127,12 +128,13 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con return RESULT_SUCCESS; } -ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread) { +ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> 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. 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/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> thread); + ResultCode HandleSyncRequest(std::shared_ptr<Thread> 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 9928b3a26..db3ae3eb8 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 (!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()); @@ -383,7 +385,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. @@ -452,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 (!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); @@ -474,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<WaitObject>(handle); if (object == nullptr) { @@ -616,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]); @@ -712,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); } @@ -1115,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; } @@ -1275,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; } @@ -1672,6 +1678,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; @@ -1681,7 +1688,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; @@ -2284,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); } @@ -2323,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/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<std::shared_ptr<Thread>> 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> thread = std::make_shared<Thread>(kernel); thread->thread_id = kernel.CreateNewThreadID(); 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/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<u64>()}; std::vector<s16> 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/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<AudioCore::AudioRenderer>( - system.CoreTiming(), audren_params, system_event.writable, instance_number); + renderer = std::make_unique<AudioCore::AudioRenderer>(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 ea9cc901e..55d62fc5e 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<u8> buffer; - buffer.resize(ctx.BufferDescriptorX()[0].Size()); - Memory::ReadBlock(ctx.BufferDescriptorX()[0].Address(), buffer.data(), buffer.size()); + std::vector<u8> 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/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<u8> 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<u8> 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 435f2d286..346c8f899 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<ILogger> { 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"}, @@ -35,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<Field>(Memory::Read8(addr++)); - const auto length = Memory::Read8(addr++); + const auto field = static_cast<Field>(memory.Read8(addr++)); + const auto length = memory.Read8(addr++); - if (static_cast<Field>(Memory::Read8(addr)) == Field::Skip) { + if (static_cast<Field>(memory.Read8(addr)) == Field::Skip) { ++addr; } @@ -54,7 +55,7 @@ private: } std::vector<u8> data(length); - Memory::ReadBlock(addr, data.data(), length); + memory.ReadBlock(addr, data.data(), length); fields.emplace(field, std::move(data)); } @@ -74,11 +75,13 @@ private: } Manager& manager; + Memory::Memory& memory; }; class LM final : public ServiceFramework<LM> { 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<ILogger>(manager); + rb.PushIpcInterface<ILogger>(manager, memory); } Manager& manager; + Memory::Memory& memory; }; void InstallInterfaces(Core::System& system) { - std::make_shared<LM>(system.GetLogManager())->InstallAsService(system.ServiceManager()); + std::make_shared<LM>(system.GetLogManager(), system.Memory()) + ->InstallAsService(system.ServiceManager()); } } // namespace Service::LM 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<u8>& input, std::vector<u8>& 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 fa49f3dd0..91bf07a92 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -17,529 +17,699 @@ #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 { -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_} {} -void SetCurrentPageTable(Kernel::Process& process) { - current_page_table = &process.VMManager().page_table; + void SetCurrentPageTable(Kernel::Process& process) { + current_page_table = &process.VMManager().page_table; - const std::size_t address_space_width = process.VMManager().GetAddressSpaceWidth(); + 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); -} + system.ArmInterface(0).PageTableChanged(*current_page_table, address_space_width); + system.ArmInterface(1).PageTableChanged(*current_page_table, address_space_width); + system.ArmInterface(2).PageTableChanged(*current_page_table, address_space_width); + system.ArmInterface(3).PageTableChanged(*current_page_table, address_space_width); + } -static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory, - Common::PageType type) { - 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); - } - } + 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); } - VAddr end = base + size; - ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}", - base + page_table.pointers.size()); + 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<VAddr>::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<Common::SpecialRegion>{region})); + } - std::fill(page_table.attributes.begin() + base, page_table.attributes.begin() + end, type); + 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); - 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; + const auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); + page_table.special_regions.erase(interval); + } - base += 1; - memory += PAGE_SIZE; - } + void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size, + Common::MemoryHookPointer hook) { + const auto interval = boost::icl::discrete_interval<VAddr>::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<Common::SpecialRegion>{region})); } -} -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 RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size, + Common::MemoryHookPointer hook) { + const auto interval = boost::icl::discrete_interval<VAddr>::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<Common::SpecialRegion>{region})); + } -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); + bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const { + const auto& page_table = process.VMManager().page_table; - auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); - Common::SpecialRegion region{Common::SpecialRegion::Type::IODevice, std::move(mmio_handler)}; - page_table.special_regions.add( - std::make_pair(interval, std::set<Common::SpecialRegion>{region})); -} + const u8* const page_pointer = page_table.pointers[vaddr >> PAGE_BITS]; + if (page_pointer != nullptr) { + return true; + } -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); + if (page_table.attributes[vaddr >> PAGE_BITS] == Common::PageType::RasterizerCachedMemory) { + return true; + } - auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); - page_table.special_regions.erase(interval); -} + if (page_table.attributes[vaddr >> PAGE_BITS] != Common::PageType::Special) { + return false; + } -void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size, - Common::MemoryHookPointer hook) { - auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); - Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)}; - page_table.special_regions.add( - std::make_pair(interval, std::set<Common::SpecialRegion>{region})); -} + return false; + } -void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size, - Common::MemoryHookPointer hook) { - auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); - Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)}; - page_table.special_regions.subtract( - std::make_pair(interval, std::set<Common::SpecialRegion>{region})); -} + bool IsValidVirtualAddress(VAddr vaddr) const { + 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 - */ -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(); + /** + * 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); } - 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); + } -/** - * 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); -} + 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); + } -template <typename T> -T Read(const VAddr vaddr) { - const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; - if (page_pointer) { - // NOTE: Avoid adding any extra logic to this fast-path block - T value; - std::memcpy(&value, &page_pointer[vaddr & PAGE_MASK], sizeof(T)); - return value; - } - - Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; - switch (type) { - case Common::PageType::Unmapped: - 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 {}; -} + if (current_page_table->attributes[vaddr >> PAGE_BITS] == + Common::PageType::RasterizerCachedMemory) { + return GetPointerFromVMA(vaddr); + } -template <typename T> -void Write(const VAddr vaddr, const T data) { - u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; - if (page_pointer) { - // NOTE: Avoid adding any extra logic to this fast-path block - std::memcpy(&page_pointer[vaddr & PAGE_MASK], &data, sizeof(T)); - return; - } - - Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; - switch (type) { - case Common::PageType::Unmapped: - LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, - static_cast<u32>(data), vaddr); - return; - case Common::PageType::Memory: - ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); - break; - case Common::PageType::RasterizerCachedMemory: { - auto host_ptr{GetPointerFromVMA(vaddr)}; - Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T)); - std::memcpy(host_ptr, &data, sizeof(T)); - break; - } - default: - UNREACHABLE(); + LOG_ERROR(HW_Memory, "Unknown GetPointer @ 0x{:016X}", vaddr); + return nullptr; } -} -bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) { - const auto& page_table = process.VMManager().page_table; + u8 Read8(const VAddr addr) { + return Read<u8>(addr); + } - const u8* page_pointer = page_table.pointers[vaddr >> PAGE_BITS]; - if (page_pointer) - return true; + u16 Read16(const VAddr addr) { + return Read<u16_le>(addr); + } - if (page_table.attributes[vaddr >> PAGE_BITS] == Common::PageType::RasterizerCachedMemory) - return true; + u32 Read32(const VAddr addr) { + return Read<u32_le>(addr); + } - if (page_table.attributes[vaddr >> PAGE_BITS] != Common::PageType::Special) - return false; + u64 Read64(const VAddr addr) { + return Read<u64_le>(addr); + } - return false; -} + void Write8(const VAddr addr, const u8 data) { + Write<u8>(addr, data); + } -bool IsValidVirtualAddress(const VAddr vaddr) { - return IsValidVirtualAddress(*Core::System::GetInstance().CurrentProcess(), vaddr); -} + void Write16(const VAddr addr, const u16 data) { + Write<u16_le>(addr, data); + } -bool IsKernelVirtualAddress(const VAddr vaddr) { - return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END; -} + void Write32(const VAddr addr, const u32 data) { + Write<u32_le>(addr, data); + } -u8* GetPointer(const VAddr vaddr) { - u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; - if (page_pointer) { - return page_pointer + (vaddr & PAGE_MASK); + void Write64(const VAddr addr, const u64 data) { + Write<u64_le>(addr, data); } - if (current_page_table->attributes[vaddr >> PAGE_BITS] == - Common::PageType::RasterizerCachedMemory) { - return GetPointerFromVMA(vaddr); + 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; } - LOG_ERROR(HW_Memory, "Unknown GetPointer @ 0x{:016X}", vaddr); - return nullptr; -} + 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<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); + const auto current_vaddr = static_cast<VAddr>((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]); -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; + 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<u8*>(dest_buffer) + copy_amount; + remaining_size -= copy_amount; + } } - string.shrink_to_fit(); - return string; -} -void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { - if (vaddr == 0) { - return; + void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) { + ReadBlock(*system.CurrentProcess(), src_addr, dest_buffer, size); } - // 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. + 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<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); + const auto current_vaddr = static_cast<VAddr>((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<const u8*>(src_buffer) + copy_amount; + remaining_size -= copy_amount; + } + } - 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]; + void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { + WriteBlock(*system.CurrentProcess(), dest_addr, src_buffer, size); + } - 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. + 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<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); + const auto current_vaddr = static_cast<VAddr>((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: - page_type = Common::PageType::RasterizerCachedMemory; - current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr; + } + 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: - // There can be more than one GPU region mapped per CPU region, so it's common that - // this area is already marked as cached. + } + 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(); } - } 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. + + 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<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); + const auto current_vaddr = static_cast<VAddr>((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: - // There can be more than one GPU region mapped per CPU region, so it's common that - // this area is already unmarked as cached. + } + 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: { - 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; - } + 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<VAddr>(copy_amount); + src_addr += static_cast<VAddr>(copy_amount); + remaining_size -= copy_amount; } } -} -u8 Read8(const VAddr addr) { - return Read<u8>(addr); -} + void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) { + return CopyBlock(*system.CurrentProcess(), dest_addr, src_addr, size); + } -u16 Read16(const VAddr addr) { - return Read<u16_le>(addr); -} + void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { + if (vaddr == 0) { + return; + } -u32 Read32(const VAddr addr) { - return Read<u32_le>(addr); -} + // 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(); + } + } + } + } -u64 Read64(const VAddr addr) { - return Read<u64_le>(addr); -} + /** + * 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); + } + } + } -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<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); - const VAddr current_vaddr = static_cast<VAddr>((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; + 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; + } + } + } + + /** + * 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 <typename T> + 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; } - 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); + 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 {}; + } + + /** + * 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 <typename T> + 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<u32>(data), vaddr); + return; + case Common::PageType::Memory: + ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); + 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); + u8* const host_ptr{GetPointerFromVMA(vaddr)}; + system.GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T)); + std::memcpy(host_ptr, &data, sizeof(T)); break; } default: UNREACHABLE(); } - - page_index++; - page_offset = 0; - dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; - remaining_size -= copy_amount; } + + Common::PageTable* current_page_table = nullptr; + Core::System& system; +}; + +Memory::Memory(Core::System& system) : impl{std::make_unique<Impl>(system)} {} +Memory::~Memory() = default; + +void Memory::SetCurrentPageTable(Kernel::Process& process) { + impl->SetCurrentPageTable(process); } -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 Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target) { + impl->MapMemoryRegion(page_table, base, size, target); } -void Write8(const VAddr addr, const u8 data) { - Write<u8>(addr, data); +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 Write16(const VAddr addr, const u16 data) { - Write<u16_le>(addr, data); +void Memory::UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) { + impl->UnmapRegion(page_table, base, size); } -void Write32(const VAddr addr, const u32 data) { - Write<u32_le>(addr, data); +void Memory::AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size, + Common::MemoryHookPointer hook) { + impl->AddDebugHook(page_table, base, size, std::move(hook)); } -void Write64(const VAddr addr, const u64 data) { - Write<u64_le>(addr, data); +void Memory::RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size, + Common::MemoryHookPointer hook) { + impl->RemoveDebugHook(page_table, base, size, std::move(hook)); } -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<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); - const VAddr current_vaddr = static_cast<VAddr>((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]); +bool Memory::IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const { + return impl->IsValidVirtualAddress(process, vaddr); +} - 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(); - } +bool Memory::IsValidVirtualAddress(const VAddr vaddr) const { + return impl->IsValidVirtualAddress(vaddr); +} - page_index++; - page_offset = 0; - src_buffer = static_cast<const u8*>(src_buffer) + copy_amount; - remaining_size -= copy_amount; - } +u8* Memory::GetPointer(VAddr vaddr) { + return impl->GetPointer(vaddr); } -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); +const u8* Memory::GetPointer(VAddr vaddr) const { + return impl->GetPointer(vaddr); } -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<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); - const VAddr current_vaddr = static_cast<VAddr>((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 Memory::Read8(const VAddr addr) { + return impl->Read8(addr); +} - 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(); - } +u16 Memory::Read16(const VAddr addr) { + return impl->Read16(addr); +} - page_index++; - page_offset = 0; - remaining_size -= copy_amount; - } +u32 Memory::Read32(const VAddr addr) { + return impl->Read32(addr); } -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<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); - const VAddr current_vaddr = static_cast<VAddr>((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(); - } +u64 Memory::Read64(const VAddr addr) { + return impl->Read64(addr); +} - page_index++; - page_offset = 0; - dest_addr += static_cast<VAddr>(copy_amount); - src_addr += static_cast<VAddr>(copy_amount); - remaining_size -= copy_amount; - } +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); +} + +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::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 CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) { - CopyBlock(*Core::System::GetInstance().CurrentProcess(), dest_addr, src_addr, size); +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); +} + +bool IsKernelVirtualAddress(const VAddr vaddr) { + return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END; } } // namespace Memory diff --git a/src/core/memory.h b/src/core/memory.h index 09008e1dd..1428a6d60 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -5,8 +5,18 @@ #pragma once #include <cstddef> +#include <memory> #include <string> #include "common/common_types.h" +#include "common/memory_hook.h" + +namespace Common { +struct PageTable; +} + +namespace Core { +class System; +} namespace Kernel { class Process; @@ -36,41 +46,369 @@ enum : VAddr { KERNEL_REGION_END = KERNEL_REGION_VADDR + KERNEL_REGION_SIZE, }; -/// Changes the currently active page table to that of -/// the given process instance. -void SetCurrentPageTable(Kernel::Process& process); +/// Central class that handles all memory operations and state. +class Memory { +public: + explicit Memory(Core::System& system); + ~Memory(); -/// 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); + Memory(const Memory&) = delete; + Memory& operator=(const Memory&) = delete; -u8 Read8(VAddr addr); -u16 Read16(VAddr addr); -u32 Read32(VAddr addr); -u64 Read64(VAddr addr); + Memory(Memory&&) = default; + Memory& operator=(Memory&&) = default; -void Write8(VAddr addr, u8 data); -void Write16(VAddr addr, u16 data); -void Write32(VAddr addr, u32 data); -void Write64(VAddr addr, u64 data); + /** + * 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); -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); -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); + /** + * 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); -u8* GetPointer(VAddr vaddr); + /** + * 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); -std::string ReadCString(VAddr vaddr, std::size_t max_length); + /** + * 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); -/** - * Mark each page touching the region as cached. - */ -void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached); + /** + * 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); + + /** + * 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; + + /** + * Gets a pointer to the given address. + * + * @param vaddr Virtual address to retrieve a pointer to. + * + * @returns The pointer to the given address, if the address is valid. + * If the address is not valid, nullptr will be returned. + */ + u8* GetPointer(VAddr vaddr); + + /** + * Gets a pointer to the given address. + * + * @param vaddr Virtual address to retrieve a pointer to. + * + * @returns The pointer to the given address, if the address is valid. + * If the address is not valid, nullptr will be returned. + */ + const u8* GetPointer(VAddr vaddr) const; + + /** + * Reads an 8-bit unsigned value from the current process' address space + * at the given virtual address. + * + * @param addr The virtual address to read the 8-bit value from. + * + * @returns the read 8-bit unsigned value. + */ + u8 Read8(VAddr addr); + + /** + * Reads a 16-bit unsigned value from the current process' address space + * at the given virtual address. + * + * @param addr The virtual address to read the 16-bit value from. + * + * @returns the read 16-bit unsigned value. + */ + u16 Read16(VAddr addr); + + /** + * Reads a 32-bit unsigned value from the current process' address space + * at the given virtual address. + * + * @param addr The virtual address to read the 32-bit value from. + * + * @returns the read 32-bit unsigned value. + */ + u32 Read32(VAddr addr); + + /** + * Reads a 64-bit unsigned value from the current process' address space + * at the given virtual address. + * + * @param addr The virtual address to read the 64-bit value from. + * + * @returns the read 64-bit value. + */ + u64 Read64(VAddr addr); + + /** + * Writes an 8-bit unsigned integer to the given virtual address in + * the current process' address space. + * + * @param addr The virtual address to write the 8-bit unsigned integer to. + * @param data The 8-bit unsigned integer to write to the given virtual address. + * + * @post The memory at the given virtual address contains the specified data value. + */ + void Write8(VAddr addr, u8 data); + + /** + * Writes a 16-bit unsigned integer to the given virtual address in + * the current process' address space. + * + * @param addr The virtual address to write the 16-bit unsigned integer to. + * @param data The 16-bit unsigned integer to write to the given virtual address. + * + * @post The memory range [addr, sizeof(data)) contains the given data value. + */ + void Write16(VAddr addr, u16 data); + + /** + * Writes a 32-bit unsigned integer to the given virtual address in + * the current process' address space. + * + * @param addr The virtual address to write the 32-bit unsigned integer to. + * @param data The 32-bit unsigned integer to write to the given virtual address. + * + * @post The memory range [addr, sizeof(data)) contains the given data value. + */ + void Write32(VAddr addr, u32 data); + + /** + * Writes a 64-bit unsigned integer to the given virtual address in + * the current process' address space. + * + * @param addr The virtual address to write the 64-bit unsigned integer to. + * @param data The 64-bit unsigned integer to write to the given virtual address. + * + * @post The memory range [addr, sizeof(data)) contains the given data value. + */ + void Write64(VAddr addr, u64 data); + + /** + * Reads a null-terminated string from the given virtual address. + * This function will continually read characters until either: + * + * - A null character ('\0') is reached. + * - max_length characters have been read. + * + * @note The final null-terminating character (if found) is not included + * in the returned string. + * + * @param vaddr The address to begin reading the string from. + * @param max_length The maximum length of the string to read in characters. + * + * @returns The read string. + */ + std::string ReadCString(VAddr vaddr, std::size_t max_length); + + /** + * Reads a contiguous block of bytes from a specified process' address space. + * + * @param process The process to read the data from. + * @param src_addr The virtual address to begin reading from. + * @param dest_buffer The buffer to place the read bytes into. + * @param size The amount of data to read, in bytes. + * + * @note If a size of 0 is specified, then this function reads nothing and + * no attempts to access memory are made at all. + * + * @pre dest_buffer must be at least size bytes in length, otherwise a + * buffer overrun will occur. + * + * @post The range [dest_buffer, size) contains the read bytes from the + * process' address space. + */ + void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, + std::size_t size); + + /** + * Reads a contiguous block of bytes from the current process' address space. + * + * @param src_addr The virtual address to begin reading from. + * @param dest_buffer The buffer to place the read bytes into. + * @param size The amount of data to read, in bytes. + * + * @note If a size of 0 is specified, then this function reads nothing and + * no attempts to access memory are made at all. + * + * @pre dest_buffer must be at least size bytes in length, otherwise a + * buffer overrun will occur. + * + * @post The range [dest_buffer, size) contains the read bytes from the + * current process' address space. + */ + void ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size); + + /** + * Writes a range of bytes into a given process' address space at the specified + * virtual address. + * + * @param process The process to write data into the address space of. + * @param dest_addr The destination virtual address to begin writing the data at. + * @param src_buffer The data to write into the process' address space. + * @param size The size of the data to write, in bytes. + * + * @post The address range [dest_addr, size) in the process' address space + * contains the data that was within src_buffer. + * + * @post If an attempt is made to write into an unmapped region of memory, the writes + * will be ignored and an error will be logged. + * + * @post If a write is performed into a region of memory that is considered cached + * rasterizer memory, will cause the currently active rasterizer to be notified + * and will mark that region as invalidated to caches that the active + * graphics backend may be maintaining over the course of execution. + */ + void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer, + std::size_t size); + + /** + * Writes a range of bytes into 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. + * + * @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. + * + * @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> impl; +}; + +/// Determines if the given VAddr is a kernel address +bool IsKernelVirtualAddress(VAddr vaddr); } // namespace Memory diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index b73cc9fbd..d1e6bed93 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp @@ -20,18 +20,17 @@ namespace Memory { constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(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) { - WriteBlock(SanitizeAddress(address), data, size); + system.Memory().WriteBlock(SanitizeAddress(address), data, size); } u64 StandardVmCallbacks::HidKeysDown() { 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/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/core/reporter.cpp b/src/core/reporter.cpp index 6f4af77fd..f95eee3b1 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 <bool read_value, typename DescriptorType> -json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer) { +json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer, Memory::Memory& memory) { auto buffer_out = json::array(); for (const auto& desc : buffer) { auto entry = json{ @@ -157,7 +157,7 @@ json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer) { if constexpr (read_value) { std::vector<u8> data(desc.Size()); - Memory::ReadBlock(desc.Address(), data.data(), desc.Size()); + memory.ReadBlock(desc.Address(), data.data(), desc.Size()); entry["data"] = Common::HexToString(data); } @@ -167,7 +167,7 @@ json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& 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<true>(ctx.BufferDescriptorA()); - out["buffer_descriptor_b"] = GetHLEBufferDescriptorData<false>(ctx.BufferDescriptorB()); - out["buffer_descriptor_c"] = GetHLEBufferDescriptorData<false>(ctx.BufferDescriptorC()); - out["buffer_descriptor_x"] = GetHLEBufferDescriptorData<true>(ctx.BufferDescriptorX()); + out["buffer_descriptor_a"] = GetHLEBufferDescriptorData<true>(ctx.BufferDescriptorA(), memory); + out["buffer_descriptor_b"] = GetHLEBufferDescriptorData<false>(ctx.BufferDescriptorB(), memory); + out["buffer_descriptor_c"] = GetHLEBufferDescriptorData<false>(ctx.BufferDescriptorC(), memory); + out["buffer_descriptor_x"] = GetHLEBufferDescriptorData<true>(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..55e0dbc49 100644 --- a/src/core/tools/freezer.cpp +++ b/src/core/tools/freezer.cpp @@ -11,40 +11,39 @@ #include "core/tools/freezer.h" namespace Tools { - namespace { constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(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); + 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; } } -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<u8>(value)); + memory.Write8(addr, static_cast<u8>(value)); break; case 2: - Memory::Write16(addr, static_cast<u16>(value)); + memory.Write16(addr, static_cast<u16>(value)); break; case 4: - Memory::Write32(addr, static_cast<u32>(value)); + memory.Write32(addr, static_cast<u32>(value)); break; case 8: - Memory::Write64(addr, value); + memory.Write64(addr, value); break; default: UNREACHABLE(); @@ -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<Core::Timing::EventType> event; Core::Timing::CoreTiming& core_timing; + Memory::Memory& memory; }; } // namespace Tools |