diff options
| -rw-r--r-- | src/audio_core/audio_renderer.cpp | 17 | ||||
| -rw-r--r-- | src/core/arm/arm_interface.cpp | 24 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic.cpp | 12 | ||||
| -rw-r--r-- | src/core/gdbstub/gdbstub.cpp | 8 | ||||
| -rw-r--r-- | src/core/hle/kernel/address_arbiter.cpp | 28 | ||||
| -rw-r--r-- | src/core/hle/kernel/hle_ipc.cpp | 12 | ||||
| -rw-r--r-- | src/core/hle/kernel/mutex.cpp | 2 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc.cpp | 16 | ||||
| -rw-r--r-- | src/core/hle/service/audio/audout_u.cpp | 6 | ||||
| -rw-r--r-- | src/core/hle/service/ldr/ldr.cpp | 5 | ||||
| -rw-r--r-- | src/core/hle/service/lm/lm.cpp | 10 | ||||
| -rw-r--r-- | src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp | 4 | ||||
| -rw-r--r-- | src/core/memory.cpp | 228 | ||||
| -rw-r--r-- | src/core/memory.h | 85 | ||||
| -rw-r--r-- | src/core/memory/cheat_engine.cpp | 5 | ||||
| -rw-r--r-- | src/core/memory/cheat_engine.h | 4 | ||||
| -rw-r--r-- | src/core/reporter.cpp | 2 | ||||
| -rw-r--r-- | src/core/tools/freezer.cpp | 8 | ||||
| -rw-r--r-- | src/yuzu/debugger/wait_tree.cpp | 7 |
19 files changed, 305 insertions, 178 deletions
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index 12e2b7901..c187d8ac5 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp | |||
| @@ -259,9 +259,10 @@ void AudioRenderer::VoiceState::UpdateState() { | |||
| 259 | } | 259 | } |
| 260 | 260 | ||
| 261 | void AudioRenderer::VoiceState::RefreshBuffer(Memory::Memory& memory) { | 261 | void AudioRenderer::VoiceState::RefreshBuffer(Memory::Memory& memory) { |
| 262 | std::vector<s16> new_samples(info.wave_buffer[wave_index].buffer_sz / sizeof(s16)); | 262 | const auto wave_buffer_address = info.wave_buffer[wave_index].buffer_addr; |
| 263 | Memory::ReadBlock(info.wave_buffer[wave_index].buffer_addr, new_samples.data(), | 263 | const auto wave_buffer_size = info.wave_buffer[wave_index].buffer_sz; |
| 264 | info.wave_buffer[wave_index].buffer_sz); | 264 | std::vector<s16> new_samples(wave_buffer_size / sizeof(s16)); |
| 265 | memory.ReadBlock(wave_buffer_address, new_samples.data(), wave_buffer_size); | ||
| 265 | 266 | ||
| 266 | switch (static_cast<Codec::PcmFormat>(info.sample_format)) { | 267 | switch (static_cast<Codec::PcmFormat>(info.sample_format)) { |
| 267 | case Codec::PcmFormat::Int16: { | 268 | case Codec::PcmFormat::Int16: { |
| @@ -271,7 +272,7 @@ void AudioRenderer::VoiceState::RefreshBuffer(Memory::Memory& memory) { | |||
| 271 | case Codec::PcmFormat::Adpcm: { | 272 | case Codec::PcmFormat::Adpcm: { |
| 272 | // Decode ADPCM to PCM16 | 273 | // Decode ADPCM to PCM16 |
| 273 | Codec::ADPCM_Coeff coeffs; | 274 | Codec::ADPCM_Coeff coeffs; |
| 274 | Memory::ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff)); | 275 | memory.ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff)); |
| 275 | new_samples = Codec::DecodeADPCM(reinterpret_cast<u8*>(new_samples.data()), | 276 | new_samples = Codec::DecodeADPCM(reinterpret_cast<u8*>(new_samples.data()), |
| 276 | new_samples.size() * sizeof(s16), coeffs, adpcm_state); | 277 | new_samples.size() * sizeof(s16), coeffs, adpcm_state); |
| 277 | break; | 278 | break; |
| @@ -314,13 +315,13 @@ void AudioRenderer::EffectState::UpdateState(Memory::Memory& memory) { | |||
| 314 | out_status.state = EffectStatus::New; | 315 | out_status.state = EffectStatus::New; |
| 315 | } else { | 316 | } else { |
| 316 | if (info.type == Effect::Aux) { | 317 | if (info.type == Effect::Aux) { |
| 317 | ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_info) == 0, | 318 | ASSERT_MSG(memory.Read32(info.aux_info.return_buffer_info) == 0, |
| 318 | "Aux buffers tried to update"); | 319 | "Aux buffers tried to update"); |
| 319 | ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_info) == 0, | 320 | ASSERT_MSG(memory.Read32(info.aux_info.send_buffer_info) == 0, |
| 320 | "Aux buffers tried to update"); | 321 | "Aux buffers tried to update"); |
| 321 | ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_base) == 0, | 322 | ASSERT_MSG(memory.Read32(info.aux_info.return_buffer_base) == 0, |
| 322 | "Aux buffers tried to update"); | 323 | "Aux buffers tried to update"); |
| 323 | ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_base) == 0, | 324 | ASSERT_MSG(memory.Read32(info.aux_info.send_buffer_base) == 0, |
| 324 | "Aux buffers tried to update"); | 325 | "Aux buffers tried to update"); |
| 325 | } | 326 | } |
| 326 | } | 327 | } |
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index dea192869..7e846ddd5 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp | |||
| @@ -60,15 +60,15 @@ static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size."); | |||
| 60 | 60 | ||
| 61 | using Symbols = std::vector<std::pair<ELFSymbol, std::string>>; | 61 | using Symbols = std::vector<std::pair<ELFSymbol, std::string>>; |
| 62 | 62 | ||
| 63 | Symbols GetSymbols(VAddr text_offset) { | 63 | Symbols GetSymbols(VAddr text_offset, Memory::Memory& memory) { |
| 64 | const auto mod_offset = text_offset + Memory::Read32(text_offset + 4); | 64 | const auto mod_offset = text_offset + memory.Read32(text_offset + 4); |
| 65 | 65 | ||
| 66 | if (mod_offset < text_offset || (mod_offset & 0b11) != 0 || | 66 | if (mod_offset < text_offset || (mod_offset & 0b11) != 0 || |
| 67 | Memory::Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) { | 67 | memory.Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) { |
| 68 | return {}; | 68 | return {}; |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | const auto dynamic_offset = Memory::Read32(mod_offset + 0x4) + mod_offset; | 71 | const auto dynamic_offset = memory.Read32(mod_offset + 0x4) + mod_offset; |
| 72 | 72 | ||
| 73 | VAddr string_table_offset{}; | 73 | VAddr string_table_offset{}; |
| 74 | VAddr symbol_table_offset{}; | 74 | VAddr symbol_table_offset{}; |
| @@ -76,8 +76,8 @@ Symbols GetSymbols(VAddr text_offset) { | |||
| 76 | 76 | ||
| 77 | VAddr dynamic_index = dynamic_offset; | 77 | VAddr dynamic_index = dynamic_offset; |
| 78 | while (true) { | 78 | while (true) { |
| 79 | const auto tag = Memory::Read64(dynamic_index); | 79 | const u64 tag = memory.Read64(dynamic_index); |
| 80 | const auto value = Memory::Read64(dynamic_index + 0x8); | 80 | const u64 value = memory.Read64(dynamic_index + 0x8); |
| 81 | dynamic_index += 0x10; | 81 | dynamic_index += 0x10; |
| 82 | 82 | ||
| 83 | if (tag == ELF_DYNAMIC_TAG_NULL) { | 83 | if (tag == ELF_DYNAMIC_TAG_NULL) { |
| @@ -105,11 +105,11 @@ Symbols GetSymbols(VAddr text_offset) { | |||
| 105 | VAddr symbol_index = symbol_table_address; | 105 | VAddr symbol_index = symbol_table_address; |
| 106 | while (symbol_index < string_table_address) { | 106 | while (symbol_index < string_table_address) { |
| 107 | ELFSymbol symbol{}; | 107 | ELFSymbol symbol{}; |
| 108 | Memory::ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol)); | 108 | memory.ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol)); |
| 109 | 109 | ||
| 110 | VAddr string_offset = string_table_address + symbol.name_index; | 110 | VAddr string_offset = string_table_address + symbol.name_index; |
| 111 | std::string name; | 111 | std::string name; |
| 112 | for (u8 c = Memory::Read8(string_offset); c != 0; c = Memory::Read8(++string_offset)) { | 112 | for (u8 c = memory.Read8(string_offset); c != 0; c = memory.Read8(++string_offset)) { |
| 113 | name += static_cast<char>(c); | 113 | name += static_cast<char>(c); |
| 114 | } | 114 | } |
| 115 | 115 | ||
| @@ -141,17 +141,17 @@ constexpr u64 SEGMENT_BASE = 0x7100000000ull; | |||
| 141 | 141 | ||
| 142 | std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const { | 142 | std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const { |
| 143 | std::vector<BacktraceEntry> out; | 143 | std::vector<BacktraceEntry> out; |
| 144 | auto& memory = system.Memory(); | ||
| 144 | 145 | ||
| 145 | auto fp = GetReg(29); | 146 | auto fp = GetReg(29); |
| 146 | auto lr = GetReg(30); | 147 | auto lr = GetReg(30); |
| 147 | |||
| 148 | while (true) { | 148 | while (true) { |
| 149 | out.push_back({"", 0, lr, 0}); | 149 | out.push_back({"", 0, lr, 0}); |
| 150 | if (!fp) { | 150 | if (!fp) { |
| 151 | break; | 151 | break; |
| 152 | } | 152 | } |
| 153 | lr = Memory::Read64(fp + 8) - 4; | 153 | lr = memory.Read64(fp + 8) - 4; |
| 154 | fp = Memory::Read64(fp); | 154 | fp = memory.Read64(fp); |
| 155 | } | 155 | } |
| 156 | 156 | ||
| 157 | std::map<VAddr, std::string> modules; | 157 | std::map<VAddr, std::string> modules; |
| @@ -162,7 +162,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const { | |||
| 162 | 162 | ||
| 163 | std::map<std::string, Symbols> symbols; | 163 | std::map<std::string, Symbols> symbols; |
| 164 | for (const auto& module : modules) { | 164 | for (const auto& module : modules) { |
| 165 | symbols.insert_or_assign(module.second, GetSymbols(module.first)); | 165 | symbols.insert_or_assign(module.second, GetSymbols(module.first, memory)); |
| 166 | } | 166 | } |
| 167 | 167 | ||
| 168 | for (auto& entry : out) { | 168 | for (auto& entry : out) { |
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 2b396f1d6..585fb55a9 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp | |||
| @@ -28,20 +28,20 @@ public: | |||
| 28 | explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {} | 28 | explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {} |
| 29 | 29 | ||
| 30 | u8 MemoryRead8(u64 vaddr) override { | 30 | u8 MemoryRead8(u64 vaddr) override { |
| 31 | auto& s = parent.system; | 31 | return parent.system.Memory().Read8(vaddr); |
| 32 | return Memory::Read8(vaddr); | ||
| 33 | } | 32 | } |
| 34 | u16 MemoryRead16(u64 vaddr) override { | 33 | u16 MemoryRead16(u64 vaddr) override { |
| 35 | return Memory::Read16(vaddr); | 34 | return parent.system.Memory().Read16(vaddr); |
| 36 | } | 35 | } |
| 37 | u32 MemoryRead32(u64 vaddr) override { | 36 | u32 MemoryRead32(u64 vaddr) override { |
| 38 | return Memory::Read32(vaddr); | 37 | return parent.system.Memory().Read32(vaddr); |
| 39 | } | 38 | } |
| 40 | u64 MemoryRead64(u64 vaddr) override { | 39 | u64 MemoryRead64(u64 vaddr) override { |
| 41 | return Memory::Read64(vaddr); | 40 | return parent.system.Memory().Read64(vaddr); |
| 42 | } | 41 | } |
| 43 | Vector MemoryRead128(u64 vaddr) override { | 42 | Vector MemoryRead128(u64 vaddr) override { |
| 44 | return {Memory::Read64(vaddr), Memory::Read64(vaddr + 8)}; | 43 | auto& memory = parent.system.Memory(); |
| 44 | return {memory.Read64(vaddr), memory.Read64(vaddr + 8)}; | ||
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | void MemoryWrite8(u64 vaddr, u8 value) override { | 47 | void MemoryWrite8(u64 vaddr, u8 value) override { |
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 78e44f3bd..1c74a44d8 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp | |||
| @@ -969,13 +969,13 @@ static void ReadMemory() { | |||
| 969 | SendReply("E01"); | 969 | SendReply("E01"); |
| 970 | } | 970 | } |
| 971 | 971 | ||
| 972 | const auto& memory = Core::System::GetInstance().Memory(); | 972 | auto& memory = Core::System::GetInstance().Memory(); |
| 973 | if (!memory.IsValidVirtualAddress(addr)) { | 973 | if (!memory.IsValidVirtualAddress(addr)) { |
| 974 | return SendReply("E00"); | 974 | return SendReply("E00"); |
| 975 | } | 975 | } |
| 976 | 976 | ||
| 977 | std::vector<u8> data(len); | 977 | std::vector<u8> data(len); |
| 978 | Memory::ReadBlock(addr, data.data(), len); | 978 | memory.ReadBlock(addr, data.data(), len); |
| 979 | 979 | ||
| 980 | MemToGdbHex(reply, data.data(), len); | 980 | MemToGdbHex(reply, data.data(), len); |
| 981 | reply[len * 2] = '\0'; | 981 | reply[len * 2] = '\0'; |
| @@ -1057,7 +1057,9 @@ static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) { | |||
| 1057 | breakpoint.active = true; | 1057 | breakpoint.active = true; |
| 1058 | breakpoint.addr = addr; | 1058 | breakpoint.addr = addr; |
| 1059 | breakpoint.len = len; | 1059 | breakpoint.len = len; |
| 1060 | Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size()); | 1060 | |
| 1061 | auto& memory = Core::System::GetInstance().Memory(); | ||
| 1062 | memory.ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size()); | ||
| 1061 | 1063 | ||
| 1062 | static constexpr std::array<u8, 4> btrap{0x00, 0x7d, 0x20, 0xd4}; | 1064 | static constexpr std::array<u8, 4> btrap{0x00, 0x7d, 0x20, 0xd4}; |
| 1063 | if (type == BreakpointType::Execute) { | 1065 | if (type == BreakpointType::Execute) { |
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 7f9a559d2..07f0dac67 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp | |||
| @@ -67,12 +67,14 @@ ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) { | |||
| 67 | 67 | ||
| 68 | ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, | 68 | ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, |
| 69 | s32 num_to_wake) { | 69 | s32 num_to_wake) { |
| 70 | auto& memory = system.Memory(); | ||
| 71 | |||
| 70 | // Ensure that we can write to the address. | 72 | // Ensure that we can write to the address. |
| 71 | if (!system.Memory().IsValidVirtualAddress(address)) { | 73 | if (!memory.IsValidVirtualAddress(address)) { |
| 72 | return ERR_INVALID_ADDRESS_STATE; | 74 | return ERR_INVALID_ADDRESS_STATE; |
| 73 | } | 75 | } |
| 74 | 76 | ||
| 75 | if (static_cast<s32>(Memory::Read32(address)) != value) { | 77 | if (static_cast<s32>(memory.Read32(address)) != value) { |
| 76 | return ERR_INVALID_STATE; | 78 | return ERR_INVALID_STATE; |
| 77 | } | 79 | } |
| 78 | 80 | ||
| @@ -82,8 +84,10 @@ ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 | |||
| 82 | 84 | ||
| 83 | ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, | 85 | ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, |
| 84 | s32 num_to_wake) { | 86 | s32 num_to_wake) { |
| 87 | auto& memory = system.Memory(); | ||
| 88 | |||
| 85 | // Ensure that we can write to the address. | 89 | // Ensure that we can write to the address. |
| 86 | if (!system.Memory().IsValidVirtualAddress(address)) { | 90 | if (!memory.IsValidVirtualAddress(address)) { |
| 87 | return ERR_INVALID_ADDRESS_STATE; | 91 | return ERR_INVALID_ADDRESS_STATE; |
| 88 | } | 92 | } |
| 89 | 93 | ||
| @@ -109,7 +113,7 @@ ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr a | |||
| 109 | } | 113 | } |
| 110 | } | 114 | } |
| 111 | 115 | ||
| 112 | if (static_cast<s32>(Memory::Read32(address)) != value) { | 116 | if (static_cast<s32>(memory.Read32(address)) != value) { |
| 113 | return ERR_INVALID_STATE; | 117 | return ERR_INVALID_STATE; |
| 114 | } | 118 | } |
| 115 | 119 | ||
| @@ -134,12 +138,14 @@ ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s | |||
| 134 | 138 | ||
| 135 | ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, | 139 | ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, |
| 136 | bool should_decrement) { | 140 | bool should_decrement) { |
| 141 | auto& memory = system.Memory(); | ||
| 142 | |||
| 137 | // Ensure that we can read the address. | 143 | // Ensure that we can read the address. |
| 138 | if (!system.Memory().IsValidVirtualAddress(address)) { | 144 | if (!memory.IsValidVirtualAddress(address)) { |
| 139 | return ERR_INVALID_ADDRESS_STATE; | 145 | return ERR_INVALID_ADDRESS_STATE; |
| 140 | } | 146 | } |
| 141 | 147 | ||
| 142 | const s32 cur_value = static_cast<s32>(Memory::Read32(address)); | 148 | const s32 cur_value = static_cast<s32>(memory.Read32(address)); |
| 143 | if (cur_value >= value) { | 149 | if (cur_value >= value) { |
| 144 | return ERR_INVALID_STATE; | 150 | return ERR_INVALID_STATE; |
| 145 | } | 151 | } |
| @@ -157,15 +163,19 @@ ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s6 | |||
| 157 | } | 163 | } |
| 158 | 164 | ||
| 159 | ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { | 165 | ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { |
| 166 | auto& memory = system.Memory(); | ||
| 167 | |||
| 160 | // Ensure that we can read the address. | 168 | // Ensure that we can read the address. |
| 161 | if (!system.Memory().IsValidVirtualAddress(address)) { | 169 | if (!memory.IsValidVirtualAddress(address)) { |
| 162 | return ERR_INVALID_ADDRESS_STATE; | 170 | return ERR_INVALID_ADDRESS_STATE; |
| 163 | } | 171 | } |
| 172 | |||
| 164 | // Only wait for the address if equal. | 173 | // Only wait for the address if equal. |
| 165 | if (static_cast<s32>(Memory::Read32(address)) != value) { | 174 | if (static_cast<s32>(memory.Read32(address)) != value) { |
| 166 | return ERR_INVALID_STATE; | 175 | return ERR_INVALID_STATE; |
| 167 | } | 176 | } |
| 168 | // Short-circuit without rescheduling, if timeout is zero. | 177 | |
| 178 | // Short-circuit without rescheduling if timeout is zero. | ||
| 169 | if (timeout == 0) { | 179 | if (timeout == 0) { |
| 170 | return RESULT_TIMEOUT; | 180 | return RESULT_TIMEOUT; |
| 171 | } | 181 | } |
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index be24cef06..03745c449 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp | |||
| @@ -214,10 +214,11 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTabl | |||
| 214 | ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) { | 214 | ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) { |
| 215 | auto& owner_process = *thread.GetOwnerProcess(); | 215 | auto& owner_process = *thread.GetOwnerProcess(); |
| 216 | auto& handle_table = owner_process.GetHandleTable(); | 216 | auto& handle_table = owner_process.GetHandleTable(); |
| 217 | auto& memory = Core::System::GetInstance().Memory(); | ||
| 217 | 218 | ||
| 218 | std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf; | 219 | std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf; |
| 219 | Memory::ReadBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), | 220 | memory.ReadBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), |
| 220 | dst_cmdbuf.size() * sizeof(u32)); | 221 | dst_cmdbuf.size() * sizeof(u32)); |
| 221 | 222 | ||
| 222 | // The header was already built in the internal command buffer. Attempt to parse it to verify | 223 | // The header was already built in the internal command buffer. Attempt to parse it to verify |
| 223 | // the integrity and then copy it over to the target command buffer. | 224 | // the integrity and then copy it over to the target command buffer. |
| @@ -282,15 +283,14 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) { | |||
| 282 | std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const { | 283 | std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const { |
| 283 | std::vector<u8> buffer; | 284 | std::vector<u8> buffer; |
| 284 | const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()}; | 285 | const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()}; |
| 286 | auto& memory = Core::System::GetInstance().Memory(); | ||
| 285 | 287 | ||
| 286 | if (is_buffer_a) { | 288 | if (is_buffer_a) { |
| 287 | buffer.resize(BufferDescriptorA()[buffer_index].Size()); | 289 | buffer.resize(BufferDescriptorA()[buffer_index].Size()); |
| 288 | Memory::ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), | 290 | memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); |
| 289 | buffer.size()); | ||
| 290 | } else { | 291 | } else { |
| 291 | buffer.resize(BufferDescriptorX()[buffer_index].Size()); | 292 | buffer.resize(BufferDescriptorX()[buffer_index].Size()); |
| 292 | Memory::ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), | 293 | memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); |
| 293 | buffer.size()); | ||
| 294 | } | 294 | } |
| 295 | 295 | ||
| 296 | return buffer; | 296 | return buffer; |
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 8493d0f78..88eede436 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp | |||
| @@ -79,7 +79,7 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle, | |||
| 79 | // thread. | 79 | // thread. |
| 80 | ASSERT(requesting_thread == current_thread); | 80 | ASSERT(requesting_thread == current_thread); |
| 81 | 81 | ||
| 82 | const u32 addr_value = Memory::Read32(address); | 82 | const u32 addr_value = system.Memory().Read32(address); |
| 83 | 83 | ||
| 84 | // If the mutex isn't being held, just return success. | 84 | // If the mutex isn't being held, just return success. |
| 85 | if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) { | 85 | if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) { |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 738db528d..a6c377cfc 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -454,7 +454,8 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr | |||
| 454 | LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}", | 454 | LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}", |
| 455 | handles_address, handle_count, nano_seconds); | 455 | handles_address, handle_count, nano_seconds); |
| 456 | 456 | ||
| 457 | if (!system.Memory().IsValidVirtualAddress(handles_address)) { | 457 | auto& memory = system.Memory(); |
| 458 | if (!memory.IsValidVirtualAddress(handles_address)) { | ||
| 458 | LOG_ERROR(Kernel_SVC, | 459 | LOG_ERROR(Kernel_SVC, |
| 459 | "Handle address is not a valid virtual address, handle_address=0x{:016X}", | 460 | "Handle address is not a valid virtual address, handle_address=0x{:016X}", |
| 460 | handles_address); | 461 | handles_address); |
| @@ -476,7 +477,7 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr | |||
| 476 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | 477 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 477 | 478 | ||
| 478 | for (u64 i = 0; i < handle_count; ++i) { | 479 | for (u64 i = 0; i < handle_count; ++i) { |
| 479 | const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle)); | 480 | const Handle handle = memory.Read32(handles_address + i * sizeof(Handle)); |
| 480 | const auto object = handle_table.Get<WaitObject>(handle); | 481 | const auto object = handle_table.Get<WaitObject>(handle); |
| 481 | 482 | ||
| 482 | if (object == nullptr) { | 483 | if (object == nullptr) { |
| @@ -618,13 +619,15 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { | |||
| 618 | return; | 619 | return; |
| 619 | } | 620 | } |
| 620 | 621 | ||
| 622 | auto& memory = system.Memory(); | ||
| 623 | |||
| 621 | // This typically is an error code so we're going to assume this is the case | 624 | // This typically is an error code so we're going to assume this is the case |
| 622 | if (sz == sizeof(u32)) { | 625 | if (sz == sizeof(u32)) { |
| 623 | LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", Memory::Read32(addr)); | 626 | LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", memory.Read32(addr)); |
| 624 | } else { | 627 | } else { |
| 625 | // We don't know what's in here so we'll hexdump it | 628 | // We don't know what's in here so we'll hexdump it |
| 626 | debug_buffer.resize(sz); | 629 | debug_buffer.resize(sz); |
| 627 | Memory::ReadBlock(addr, debug_buffer.data(), sz); | 630 | memory.ReadBlock(addr, debug_buffer.data(), sz); |
| 628 | std::string hexdump; | 631 | std::string hexdump; |
| 629 | for (std::size_t i = 0; i < debug_buffer.size(); i++) { | 632 | for (std::size_t i = 0; i < debug_buffer.size(); i++) { |
| 630 | hexdump += fmt::format("{:02X} ", debug_buffer[i]); | 633 | hexdump += fmt::format("{:02X} ", debug_buffer[i]); |
| @@ -714,7 +717,7 @@ static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr addre | |||
| 714 | } | 717 | } |
| 715 | 718 | ||
| 716 | std::string str(len, '\0'); | 719 | std::string str(len, '\0'); |
| 717 | Memory::ReadBlock(address, str.data(), str.size()); | 720 | system.Memory().ReadBlock(address, str.data(), str.size()); |
| 718 | LOG_DEBUG(Debug_Emulated, "{}", str); | 721 | LOG_DEBUG(Debug_Emulated, "{}", str); |
| 719 | } | 722 | } |
| 720 | 723 | ||
| @@ -1674,6 +1677,7 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var | |||
| 1674 | 1677 | ||
| 1675 | const std::size_t current_core = system.CurrentCoreIndex(); | 1678 | const std::size_t current_core = system.CurrentCoreIndex(); |
| 1676 | auto& monitor = system.Monitor(); | 1679 | auto& monitor = system.Monitor(); |
| 1680 | auto& memory = system.Memory(); | ||
| 1677 | 1681 | ||
| 1678 | // Atomically read the value of the mutex. | 1682 | // Atomically read the value of the mutex. |
| 1679 | u32 mutex_val = 0; | 1683 | u32 mutex_val = 0; |
| @@ -1683,7 +1687,7 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var | |||
| 1683 | monitor.SetExclusive(current_core, mutex_address); | 1687 | monitor.SetExclusive(current_core, mutex_address); |
| 1684 | 1688 | ||
| 1685 | // If the mutex is not yet acquired, acquire it. | 1689 | // If the mutex is not yet acquired, acquire it. |
| 1686 | mutex_val = Memory::Read32(mutex_address); | 1690 | mutex_val = memory.Read32(mutex_address); |
| 1687 | 1691 | ||
| 1688 | if (mutex_val != 0) { | 1692 | if (mutex_val != 0) { |
| 1689 | update_val = mutex_val | Mutex::MutexHasWaitersFlag; | 1693 | update_val = mutex_val | Mutex::MutexHasWaitersFlag; |
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 6a29377e3..4fb2cbc4b 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp | |||
| @@ -43,7 +43,8 @@ public: | |||
| 43 | IAudioOut(Core::System& system, AudoutParams audio_params, AudioCore::AudioOut& audio_core, | 43 | IAudioOut(Core::System& system, AudoutParams audio_params, AudioCore::AudioOut& audio_core, |
| 44 | std::string&& device_name, std::string&& unique_name) | 44 | std::string&& device_name, std::string&& unique_name) |
| 45 | : ServiceFramework("IAudioOut"), audio_core(audio_core), | 45 | : ServiceFramework("IAudioOut"), audio_core(audio_core), |
| 46 | device_name(std::move(device_name)), audio_params(audio_params) { | 46 | device_name(std::move(device_name)), |
| 47 | audio_params(audio_params), main_memory{system.Memory()} { | ||
| 47 | // clang-format off | 48 | // clang-format off |
| 48 | static const FunctionInfo functions[] = { | 49 | static const FunctionInfo functions[] = { |
| 49 | {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, | 50 | {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, |
| @@ -137,7 +138,7 @@ private: | |||
| 137 | const u64 tag{rp.Pop<u64>()}; | 138 | const u64 tag{rp.Pop<u64>()}; |
| 138 | 139 | ||
| 139 | std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16)); | 140 | std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16)); |
| 140 | Memory::ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size); | 141 | main_memory.ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size); |
| 141 | 142 | ||
| 142 | if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) { | 143 | if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) { |
| 143 | IPC::ResponseBuilder rb{ctx, 2}; | 144 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -209,6 +210,7 @@ private: | |||
| 209 | 210 | ||
| 210 | /// This is the event handle used to check if the audio buffer was released | 211 | /// This is the event handle used to check if the audio buffer was released |
| 211 | Kernel::EventPair buffer_event; | 212 | Kernel::EventPair buffer_event; |
| 213 | Memory::Memory& main_memory; | ||
| 212 | }; | 214 | }; |
| 213 | 215 | ||
| 214 | AudOutU::AudOutU(Core::System& system_) : ServiceFramework("audout:u"), system{system_} { | 216 | AudOutU::AudOutU(Core::System& system_) : ServiceFramework("audout:u"), system{system_} { |
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 88f903bfd..157aeec88 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -140,9 +140,10 @@ public: | |||
| 140 | rb.Push(ERROR_INVALID_SIZE); | 140 | rb.Push(ERROR_INVALID_SIZE); |
| 141 | return; | 141 | return; |
| 142 | } | 142 | } |
| 143 | |||
| 143 | // Read NRR data from memory | 144 | // Read NRR data from memory |
| 144 | std::vector<u8> nrr_data(nrr_size); | 145 | std::vector<u8> nrr_data(nrr_size); |
| 145 | Memory::ReadBlock(nrr_address, nrr_data.data(), nrr_size); | 146 | system.Memory().ReadBlock(nrr_address, nrr_data.data(), nrr_size); |
| 146 | NRRHeader header; | 147 | NRRHeader header; |
| 147 | std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader)); | 148 | std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader)); |
| 148 | 149 | ||
| @@ -291,7 +292,7 @@ public: | |||
| 291 | 292 | ||
| 292 | // Read NRO data from memory | 293 | // Read NRO data from memory |
| 293 | std::vector<u8> nro_data(nro_size); | 294 | std::vector<u8> nro_data(nro_size); |
| 294 | Memory::ReadBlock(nro_address, nro_data.data(), nro_size); | 295 | system.Memory().ReadBlock(nro_address, nro_data.data(), nro_size); |
| 295 | 296 | ||
| 296 | SHA256Hash hash{}; | 297 | SHA256Hash hash{}; |
| 297 | mbedtls_sha256_ret(nro_data.data(), nro_data.size(), hash.data(), 0); | 298 | mbedtls_sha256_ret(nro_data.data(), nro_data.size(), hash.data(), 0); |
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp index 74ecaef1b..346c8f899 100644 --- a/src/core/hle/service/lm/lm.cpp +++ b/src/core/hle/service/lm/lm.cpp | |||
| @@ -36,15 +36,15 @@ private: | |||
| 36 | MessageHeader header{}; | 36 | MessageHeader header{}; |
| 37 | VAddr addr{ctx.BufferDescriptorX()[0].Address()}; | 37 | VAddr addr{ctx.BufferDescriptorX()[0].Address()}; |
| 38 | const VAddr end_addr{addr + ctx.BufferDescriptorX()[0].size}; | 38 | const VAddr end_addr{addr + ctx.BufferDescriptorX()[0].size}; |
| 39 | Memory::ReadBlock(addr, &header, sizeof(MessageHeader)); | 39 | memory.ReadBlock(addr, &header, sizeof(MessageHeader)); |
| 40 | addr += sizeof(MessageHeader); | 40 | addr += sizeof(MessageHeader); |
| 41 | 41 | ||
| 42 | FieldMap fields; | 42 | FieldMap fields; |
| 43 | while (addr < end_addr) { | 43 | while (addr < end_addr) { |
| 44 | const auto field = static_cast<Field>(Memory::Read8(addr++)); | 44 | const auto field = static_cast<Field>(memory.Read8(addr++)); |
| 45 | const auto length = Memory::Read8(addr++); | 45 | const auto length = memory.Read8(addr++); |
| 46 | 46 | ||
| 47 | if (static_cast<Field>(Memory::Read8(addr)) == Field::Skip) { | 47 | if (static_cast<Field>(memory.Read8(addr)) == Field::Skip) { |
| 48 | ++addr; | 48 | ++addr; |
| 49 | } | 49 | } |
| 50 | 50 | ||
| @@ -55,7 +55,7 @@ private: | |||
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | std::vector<u8> data(length); | 57 | std::vector<u8> data(length); |
| 58 | Memory::ReadBlock(addr, data.data(), length); | 58 | memory.ReadBlock(addr, data.data(), length); |
| 59 | fields.emplace(field, std::move(data)); | 59 | fields.emplace(field, std::move(data)); |
| 60 | } | 60 | } |
| 61 | 61 | ||
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, | |||
| 191 | std::memcpy(entries.data(), input2.data(), | 191 | std::memcpy(entries.data(), input2.data(), |
| 192 | params.num_entries * sizeof(Tegra::CommandListHeader)); | 192 | params.num_entries * sizeof(Tegra::CommandListHeader)); |
| 193 | } else { | 193 | } else { |
| 194 | Memory::ReadBlock(params.address, entries.data(), | 194 | system.Memory().ReadBlock(params.address, entries.data(), |
| 195 | params.num_entries * sizeof(Tegra::CommandListHeader)); | 195 | params.num_entries * sizeof(Tegra::CommandListHeader)); |
| 196 | } | 196 | } |
| 197 | UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0); | 197 | UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0); |
| 198 | UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0); | 198 | UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0); |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index c939e980d..699c48107 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -60,37 +60,6 @@ u8* GetPointerFromVMA(VAddr vaddr) { | |||
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | template <typename T> | 62 | template <typename T> |
| 63 | T Read(const VAddr vaddr) { | ||
| 64 | const u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; | ||
| 65 | if (page_pointer != nullptr) { | ||
| 66 | // NOTE: Avoid adding any extra logic to this fast-path block | ||
| 67 | T value; | ||
| 68 | std::memcpy(&value, &page_pointer[vaddr & PAGE_MASK], sizeof(T)); | ||
| 69 | return value; | ||
| 70 | } | ||
| 71 | |||
| 72 | const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; | ||
| 73 | switch (type) { | ||
| 74 | case Common::PageType::Unmapped: | ||
| 75 | LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr); | ||
| 76 | return 0; | ||
| 77 | case Common::PageType::Memory: | ||
| 78 | ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); | ||
| 79 | break; | ||
| 80 | case Common::PageType::RasterizerCachedMemory: { | ||
| 81 | const u8* const host_ptr{GetPointerFromVMA(vaddr)}; | ||
| 82 | Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), sizeof(T)); | ||
| 83 | T value; | ||
| 84 | std::memcpy(&value, host_ptr, sizeof(T)); | ||
| 85 | return value; | ||
| 86 | } | ||
| 87 | default: | ||
| 88 | UNREACHABLE(); | ||
| 89 | } | ||
| 90 | return {}; | ||
| 91 | } | ||
| 92 | |||
| 93 | template <typename T> | ||
| 94 | void Write(const VAddr vaddr, const T data) { | 63 | void Write(const VAddr vaddr, const T data) { |
| 95 | u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; | 64 | u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; |
| 96 | if (page_pointer != nullptr) { | 65 | if (page_pointer != nullptr) { |
| @@ -210,6 +179,22 @@ struct Memory::Impl { | |||
| 210 | return nullptr; | 179 | return nullptr; |
| 211 | } | 180 | } |
| 212 | 181 | ||
| 182 | u8 Read8(const VAddr addr) { | ||
| 183 | return Read<u8>(addr); | ||
| 184 | } | ||
| 185 | |||
| 186 | u16 Read16(const VAddr addr) { | ||
| 187 | return Read<u16_le>(addr); | ||
| 188 | } | ||
| 189 | |||
| 190 | u32 Read32(const VAddr addr) { | ||
| 191 | return Read<u32_le>(addr); | ||
| 192 | } | ||
| 193 | |||
| 194 | u64 Read64(const VAddr addr) { | ||
| 195 | return Read<u64_le>(addr); | ||
| 196 | } | ||
| 197 | |||
| 213 | std::string ReadCString(VAddr vaddr, std::size_t max_length) { | 198 | std::string ReadCString(VAddr vaddr, std::size_t max_length) { |
| 214 | std::string string; | 199 | std::string string; |
| 215 | string.reserve(max_length); | 200 | string.reserve(max_length); |
| @@ -225,6 +210,55 @@ struct Memory::Impl { | |||
| 225 | return string; | 210 | return string; |
| 226 | } | 211 | } |
| 227 | 212 | ||
| 213 | void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, | ||
| 214 | const std::size_t size) { | ||
| 215 | const auto& page_table = process.VMManager().page_table; | ||
| 216 | |||
| 217 | std::size_t remaining_size = size; | ||
| 218 | std::size_t page_index = src_addr >> PAGE_BITS; | ||
| 219 | std::size_t page_offset = src_addr & PAGE_MASK; | ||
| 220 | |||
| 221 | while (remaining_size > 0) { | ||
| 222 | const std::size_t copy_amount = | ||
| 223 | std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); | ||
| 224 | const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); | ||
| 225 | |||
| 226 | switch (page_table.attributes[page_index]) { | ||
| 227 | case Common::PageType::Unmapped: { | ||
| 228 | LOG_ERROR(HW_Memory, | ||
| 229 | "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", | ||
| 230 | current_vaddr, src_addr, size); | ||
| 231 | std::memset(dest_buffer, 0, copy_amount); | ||
| 232 | break; | ||
| 233 | } | ||
| 234 | case Common::PageType::Memory: { | ||
| 235 | DEBUG_ASSERT(page_table.pointers[page_index]); | ||
| 236 | |||
| 237 | const u8* const src_ptr = page_table.pointers[page_index] + page_offset; | ||
| 238 | std::memcpy(dest_buffer, src_ptr, copy_amount); | ||
| 239 | break; | ||
| 240 | } | ||
| 241 | case Common::PageType::RasterizerCachedMemory: { | ||
| 242 | const u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); | ||
| 243 | system.GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount); | ||
| 244 | std::memcpy(dest_buffer, host_ptr, copy_amount); | ||
| 245 | break; | ||
| 246 | } | ||
| 247 | default: | ||
| 248 | UNREACHABLE(); | ||
| 249 | } | ||
| 250 | |||
| 251 | page_index++; | ||
| 252 | page_offset = 0; | ||
| 253 | dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; | ||
| 254 | remaining_size -= copy_amount; | ||
| 255 | } | ||
| 256 | } | ||
| 257 | |||
| 258 | void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) { | ||
| 259 | ReadBlock(*system.CurrentProcess(), src_addr, dest_buffer, size); | ||
| 260 | } | ||
| 261 | |||
| 228 | void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) { | 262 | void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) { |
| 229 | const auto& page_table = process.VMManager().page_table; | 263 | const auto& page_table = process.VMManager().page_table; |
| 230 | std::size_t remaining_size = size; | 264 | std::size_t remaining_size = size; |
| @@ -425,6 +459,48 @@ struct Memory::Impl { | |||
| 425 | } | 459 | } |
| 426 | } | 460 | } |
| 427 | 461 | ||
| 462 | /** | ||
| 463 | * Reads a particular data type out of memory at the given virtual address. | ||
| 464 | * | ||
| 465 | * @param vaddr The virtual address to read the data type from. | ||
| 466 | * | ||
| 467 | * @tparam T The data type to read out of memory. This type *must* be | ||
| 468 | * trivially copyable, otherwise the behavior of this function | ||
| 469 | * is undefined. | ||
| 470 | * | ||
| 471 | * @returns The instance of T read from the specified virtual address. | ||
| 472 | */ | ||
| 473 | template <typename T> | ||
| 474 | T Read(const VAddr vaddr) { | ||
| 475 | const u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; | ||
| 476 | if (page_pointer != nullptr) { | ||
| 477 | // NOTE: Avoid adding any extra logic to this fast-path block | ||
| 478 | T value; | ||
| 479 | std::memcpy(&value, &page_pointer[vaddr & PAGE_MASK], sizeof(T)); | ||
| 480 | return value; | ||
| 481 | } | ||
| 482 | |||
| 483 | const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; | ||
| 484 | switch (type) { | ||
| 485 | case Common::PageType::Unmapped: | ||
| 486 | LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr); | ||
| 487 | return 0; | ||
| 488 | case Common::PageType::Memory: | ||
| 489 | ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); | ||
| 490 | break; | ||
| 491 | case Common::PageType::RasterizerCachedMemory: { | ||
| 492 | const u8* const host_ptr = GetPointerFromVMA(vaddr); | ||
| 493 | system.GPU().FlushRegion(ToCacheAddr(host_ptr), sizeof(T)); | ||
| 494 | T value; | ||
| 495 | std::memcpy(&value, host_ptr, sizeof(T)); | ||
| 496 | return value; | ||
| 497 | } | ||
| 498 | default: | ||
| 499 | UNREACHABLE(); | ||
| 500 | } | ||
| 501 | return {}; | ||
| 502 | } | ||
| 503 | |||
| 428 | Core::System& system; | 504 | Core::System& system; |
| 429 | }; | 505 | }; |
| 430 | 506 | ||
| @@ -470,10 +546,35 @@ const u8* Memory::GetPointer(VAddr vaddr) const { | |||
| 470 | return impl->GetPointer(vaddr); | 546 | return impl->GetPointer(vaddr); |
| 471 | } | 547 | } |
| 472 | 548 | ||
| 549 | u8 Memory::Read8(const VAddr addr) { | ||
| 550 | return impl->Read8(addr); | ||
| 551 | } | ||
| 552 | |||
| 553 | u16 Memory::Read16(const VAddr addr) { | ||
| 554 | return impl->Read16(addr); | ||
| 555 | } | ||
| 556 | |||
| 557 | u32 Memory::Read32(const VAddr addr) { | ||
| 558 | return impl->Read32(addr); | ||
| 559 | } | ||
| 560 | |||
| 561 | u64 Memory::Read64(const VAddr addr) { | ||
| 562 | return impl->Read64(addr); | ||
| 563 | } | ||
| 564 | |||
| 473 | std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) { | 565 | std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) { |
| 474 | return impl->ReadCString(vaddr, max_length); | 566 | return impl->ReadCString(vaddr, max_length); |
| 475 | } | 567 | } |
| 476 | 568 | ||
| 569 | void Memory::ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, | ||
| 570 | const std::size_t size) { | ||
| 571 | impl->ReadBlock(process, src_addr, dest_buffer, size); | ||
| 572 | } | ||
| 573 | |||
| 574 | void Memory::ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) { | ||
| 575 | impl->ReadBlock(src_addr, dest_buffer, size); | ||
| 576 | } | ||
| 577 | |||
| 477 | void Memory::ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size) { | 578 | void Memory::ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size) { |
| 478 | impl->ZeroBlock(process, dest_addr, size); | 579 | impl->ZeroBlock(process, dest_addr, size); |
| 479 | } | 580 | } |
| @@ -511,71 +612,6 @@ bool IsKernelVirtualAddress(const VAddr vaddr) { | |||
| 511 | return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END; | 612 | return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END; |
| 512 | } | 613 | } |
| 513 | 614 | ||
| 514 | u8 Read8(const VAddr addr) { | ||
| 515 | return Read<u8>(addr); | ||
| 516 | } | ||
| 517 | |||
| 518 | u16 Read16(const VAddr addr) { | ||
| 519 | return Read<u16_le>(addr); | ||
| 520 | } | ||
| 521 | |||
| 522 | u32 Read32(const VAddr addr) { | ||
| 523 | return Read<u32_le>(addr); | ||
| 524 | } | ||
| 525 | |||
| 526 | u64 Read64(const VAddr addr) { | ||
| 527 | return Read<u64_le>(addr); | ||
| 528 | } | ||
| 529 | |||
| 530 | void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, | ||
| 531 | const std::size_t size) { | ||
| 532 | const auto& page_table = process.VMManager().page_table; | ||
| 533 | |||
| 534 | std::size_t remaining_size = size; | ||
| 535 | std::size_t page_index = src_addr >> PAGE_BITS; | ||
| 536 | std::size_t page_offset = src_addr & PAGE_MASK; | ||
| 537 | |||
| 538 | while (remaining_size > 0) { | ||
| 539 | const std::size_t copy_amount = | ||
| 540 | std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); | ||
| 541 | const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); | ||
| 542 | |||
| 543 | switch (page_table.attributes[page_index]) { | ||
| 544 | case Common::PageType::Unmapped: { | ||
| 545 | LOG_ERROR(HW_Memory, | ||
| 546 | "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", | ||
| 547 | current_vaddr, src_addr, size); | ||
| 548 | std::memset(dest_buffer, 0, copy_amount); | ||
| 549 | break; | ||
| 550 | } | ||
| 551 | case Common::PageType::Memory: { | ||
| 552 | DEBUG_ASSERT(page_table.pointers[page_index]); | ||
| 553 | |||
| 554 | const u8* src_ptr = page_table.pointers[page_index] + page_offset; | ||
| 555 | std::memcpy(dest_buffer, src_ptr, copy_amount); | ||
| 556 | break; | ||
| 557 | } | ||
| 558 | case Common::PageType::RasterizerCachedMemory: { | ||
| 559 | const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)}; | ||
| 560 | Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount); | ||
| 561 | std::memcpy(dest_buffer, host_ptr, copy_amount); | ||
| 562 | break; | ||
| 563 | } | ||
| 564 | default: | ||
| 565 | UNREACHABLE(); | ||
| 566 | } | ||
| 567 | |||
| 568 | page_index++; | ||
| 569 | page_offset = 0; | ||
| 570 | dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; | ||
| 571 | remaining_size -= copy_amount; | ||
| 572 | } | ||
| 573 | } | ||
| 574 | |||
| 575 | void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) { | ||
| 576 | ReadBlock(*Core::System::GetInstance().CurrentProcess(), src_addr, dest_buffer, size); | ||
| 577 | } | ||
| 578 | |||
| 579 | void Write8(const VAddr addr, const u8 data) { | 615 | void Write8(const VAddr addr, const u8 data) { |
| 580 | Write<u8>(addr, data); | 616 | Write<u8>(addr, data); |
| 581 | } | 617 | } |
diff --git a/src/core/memory.h b/src/core/memory.h index fc0013a96..cc6ab920e 100644 --- a/src/core/memory.h +++ b/src/core/memory.h | |||
| @@ -153,6 +153,46 @@ public: | |||
| 153 | const u8* GetPointer(VAddr vaddr) const; | 153 | const u8* GetPointer(VAddr vaddr) const; |
| 154 | 154 | ||
| 155 | /** | 155 | /** |
| 156 | * Reads an 8-bit unsigned value from the current process' address space | ||
| 157 | * at the given virtual address. | ||
| 158 | * | ||
| 159 | * @param addr The virtual address to read the 8-bit value from. | ||
| 160 | * | ||
| 161 | * @returns the read 8-bit unsigned value. | ||
| 162 | */ | ||
| 163 | u8 Read8(VAddr addr); | ||
| 164 | |||
| 165 | /** | ||
| 166 | * Reads a 16-bit unsigned value from the current process' address space | ||
| 167 | * at the given virtual address. | ||
| 168 | * | ||
| 169 | * @param addr The virtual address to read the 16-bit value from. | ||
| 170 | * | ||
| 171 | * @returns the read 16-bit unsigned value. | ||
| 172 | */ | ||
| 173 | u16 Read16(VAddr addr); | ||
| 174 | |||
| 175 | /** | ||
| 176 | * Reads a 32-bit unsigned value from the current process' address space | ||
| 177 | * at the given virtual address. | ||
| 178 | * | ||
| 179 | * @param addr The virtual address to read the 32-bit value from. | ||
| 180 | * | ||
| 181 | * @returns the read 32-bit unsigned value. | ||
| 182 | */ | ||
| 183 | u32 Read32(VAddr addr); | ||
| 184 | |||
| 185 | /** | ||
| 186 | * Reads a 64-bit unsigned value from the current process' address space | ||
| 187 | * at the given virtual address. | ||
| 188 | * | ||
| 189 | * @param addr The virtual address to read the 64-bit value from. | ||
| 190 | * | ||
| 191 | * @returns the read 64-bit value. | ||
| 192 | */ | ||
| 193 | u64 Read64(VAddr addr); | ||
| 194 | |||
| 195 | /** | ||
| 156 | * Reads a null-terminated string from the given virtual address. | 196 | * Reads a null-terminated string from the given virtual address. |
| 157 | * This function will continually read characters until either: | 197 | * This function will continually read characters until either: |
| 158 | * | 198 | * |
| @@ -170,6 +210,44 @@ public: | |||
| 170 | std::string ReadCString(VAddr vaddr, std::size_t max_length); | 210 | std::string ReadCString(VAddr vaddr, std::size_t max_length); |
| 171 | 211 | ||
| 172 | /** | 212 | /** |
| 213 | * Reads a contiguous block of bytes from a specified process' address space. | ||
| 214 | * | ||
| 215 | * @param process The process to read the data from. | ||
| 216 | * @param src_addr The virtual address to begin reading from. | ||
| 217 | * @param dest_buffer The buffer to place the read bytes into. | ||
| 218 | * @param size The amount of data to read, in bytes. | ||
| 219 | * | ||
| 220 | * @note If a size of 0 is specified, then this function reads nothing and | ||
| 221 | * no attempts to access memory are made at all. | ||
| 222 | * | ||
| 223 | * @pre dest_buffer must be at least size bytes in length, otherwise a | ||
| 224 | * buffer overrun will occur. | ||
| 225 | * | ||
| 226 | * @post The range [dest_buffer, size) contains the read bytes from the | ||
| 227 | * process' address space. | ||
| 228 | */ | ||
| 229 | void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, | ||
| 230 | std::size_t size); | ||
| 231 | |||
| 232 | /** | ||
| 233 | * Reads a contiguous block of bytes from the current process' address space. | ||
| 234 | * | ||
| 235 | * @param src_addr The virtual address to begin reading from. | ||
| 236 | * @param dest_buffer The buffer to place the read bytes into. | ||
| 237 | * @param size The amount of data to read, in bytes. | ||
| 238 | * | ||
| 239 | * @note If a size of 0 is specified, then this function reads nothing and | ||
| 240 | * no attempts to access memory are made at all. | ||
| 241 | * | ||
| 242 | * @pre dest_buffer must be at least size bytes in length, otherwise a | ||
| 243 | * buffer overrun will occur. | ||
| 244 | * | ||
| 245 | * @post The range [dest_buffer, size) contains the read bytes from the | ||
| 246 | * current process' address space. | ||
| 247 | */ | ||
| 248 | void ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size); | ||
| 249 | |||
| 250 | /** | ||
| 173 | * Fills the specified address range within a process' address space with zeroes. | 251 | * Fills the specified address range within a process' address space with zeroes. |
| 174 | * | 252 | * |
| 175 | * @param process The process that will have a portion of its memory zeroed out. | 253 | * @param process The process that will have a portion of its memory zeroed out. |
| @@ -242,18 +320,11 @@ void SetCurrentPageTable(Kernel::Process& process); | |||
| 242 | /// Determines if the given VAddr is a kernel address | 320 | /// Determines if the given VAddr is a kernel address |
| 243 | bool IsKernelVirtualAddress(VAddr vaddr); | 321 | bool IsKernelVirtualAddress(VAddr vaddr); |
| 244 | 322 | ||
| 245 | u8 Read8(VAddr addr); | ||
| 246 | u16 Read16(VAddr addr); | ||
| 247 | u32 Read32(VAddr addr); | ||
| 248 | u64 Read64(VAddr addr); | ||
| 249 | |||
| 250 | void Write8(VAddr addr, u8 data); | 323 | void Write8(VAddr addr, u8 data); |
| 251 | void Write16(VAddr addr, u16 data); | 324 | void Write16(VAddr addr, u16 data); |
| 252 | void Write32(VAddr addr, u32 data); | 325 | void Write32(VAddr addr, u32 data); |
| 253 | void Write64(VAddr addr, u64 data); | 326 | void Write64(VAddr addr, u64 data); |
| 254 | 327 | ||
| 255 | void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, std::size_t size); | ||
| 256 | void ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size); | ||
| 257 | void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer, | 328 | void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer, |
| 258 | std::size_t size); | 329 | std::size_t size); |
| 259 | void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size); | 330 | void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size); |
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index b73cc9fbd..d6745af8b 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp | |||
| @@ -20,14 +20,13 @@ namespace Memory { | |||
| 20 | constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 12); | 20 | constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 12); |
| 21 | constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; | 21 | constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; |
| 22 | 22 | ||
| 23 | StandardVmCallbacks::StandardVmCallbacks(const Core::System& system, | 23 | StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata) |
| 24 | const CheatProcessMetadata& metadata) | ||
| 25 | : metadata(metadata), system(system) {} | 24 | : metadata(metadata), system(system) {} |
| 26 | 25 | ||
| 27 | StandardVmCallbacks::~StandardVmCallbacks() = default; | 26 | StandardVmCallbacks::~StandardVmCallbacks() = default; |
| 28 | 27 | ||
| 29 | void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) { | 28 | void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) { |
| 30 | ReadBlock(SanitizeAddress(address), data, size); | 29 | system.Memory().ReadBlock(SanitizeAddress(address), data, size); |
| 31 | } | 30 | } |
| 32 | 31 | ||
| 33 | void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) { | 32 | void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) { |
diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h index e3db90dac..3d6b2298a 100644 --- a/src/core/memory/cheat_engine.h +++ b/src/core/memory/cheat_engine.h | |||
| @@ -24,7 +24,7 @@ namespace Memory { | |||
| 24 | 24 | ||
| 25 | class StandardVmCallbacks : public DmntCheatVm::Callbacks { | 25 | class StandardVmCallbacks : public DmntCheatVm::Callbacks { |
| 26 | public: | 26 | public: |
| 27 | StandardVmCallbacks(const Core::System& system, const CheatProcessMetadata& metadata); | 27 | StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata); |
| 28 | ~StandardVmCallbacks() override; | 28 | ~StandardVmCallbacks() override; |
| 29 | 29 | ||
| 30 | void MemoryRead(VAddr address, void* data, u64 size) override; | 30 | void MemoryRead(VAddr address, void* data, u64 size) override; |
| @@ -37,7 +37,7 @@ private: | |||
| 37 | VAddr SanitizeAddress(VAddr address) const; | 37 | VAddr SanitizeAddress(VAddr address) const; |
| 38 | 38 | ||
| 39 | const CheatProcessMetadata& metadata; | 39 | const CheatProcessMetadata& metadata; |
| 40 | const Core::System& system; | 40 | Core::System& system; |
| 41 | }; | 41 | }; |
| 42 | 42 | ||
| 43 | // Intermediary class that parses a text file or other disk format for storing cheats into a | 43 | // Intermediary class that parses a text file or other disk format for storing cheats into a |
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index af0988d62..f95eee3b1 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp | |||
| @@ -157,7 +157,7 @@ json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer, Memor | |||
| 157 | 157 | ||
| 158 | if constexpr (read_value) { | 158 | if constexpr (read_value) { |
| 159 | std::vector<u8> data(desc.Size()); | 159 | std::vector<u8> data(desc.Size()); |
| 160 | Memory::ReadBlock(desc.Address(), data.data(), desc.Size()); | 160 | memory.ReadBlock(desc.Address(), data.data(), desc.Size()); |
| 161 | entry["data"] = Common::HexToString(data); | 161 | entry["data"] = Common::HexToString(data); |
| 162 | } | 162 | } |
| 163 | 163 | ||
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp index c7f42388f..ab66f35f9 100644 --- a/src/core/tools/freezer.cpp +++ b/src/core/tools/freezer.cpp | |||
| @@ -18,13 +18,13 @@ constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_R | |||
| 18 | u64 MemoryReadWidth(Memory::Memory& memory, u32 width, VAddr addr) { | 18 | u64 MemoryReadWidth(Memory::Memory& memory, u32 width, VAddr addr) { |
| 19 | switch (width) { | 19 | switch (width) { |
| 20 | case 1: | 20 | case 1: |
| 21 | return Memory::Read8(addr); | 21 | return memory.Read8(addr); |
| 22 | case 2: | 22 | case 2: |
| 23 | return Memory::Read16(addr); | 23 | return memory.Read16(addr); |
| 24 | case 4: | 24 | case 4: |
| 25 | return Memory::Read32(addr); | 25 | return memory.Read32(addr); |
| 26 | case 8: | 26 | case 8: |
| 27 | return Memory::Read64(addr); | 27 | return memory.Read64(addr); |
| 28 | default: | 28 | default: |
| 29 | UNREACHABLE(); | 29 | UNREACHABLE(); |
| 30 | return 0; | 30 | return 0; |
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 535b3ce90..727bd8a94 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp | |||
| @@ -80,7 +80,7 @@ QString WaitTreeText::GetText() const { | |||
| 80 | 80 | ||
| 81 | WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTable& handle_table) | 81 | WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTable& handle_table) |
| 82 | : mutex_address(mutex_address) { | 82 | : mutex_address(mutex_address) { |
| 83 | mutex_value = Memory::Read32(mutex_address); | 83 | mutex_value = Core::System::GetInstance().Memory().Read32(mutex_address); |
| 84 | owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Mutex::MutexOwnerMask); | 84 | owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Mutex::MutexOwnerMask); |
| 85 | owner = handle_table.Get<Kernel::Thread>(owner_handle); | 85 | owner = handle_table.Get<Kernel::Thread>(owner_handle); |
| 86 | } | 86 | } |
| @@ -115,10 +115,11 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() cons | |||
| 115 | std::vector<std::unique_ptr<WaitTreeItem>> list; | 115 | std::vector<std::unique_ptr<WaitTreeItem>> list; |
| 116 | 116 | ||
| 117 | constexpr std::size_t BaseRegister = 29; | 117 | constexpr std::size_t BaseRegister = 29; |
| 118 | auto& memory = Core::System::GetInstance().Memory(); | ||
| 118 | u64 base_pointer = thread.GetContext().cpu_registers[BaseRegister]; | 119 | u64 base_pointer = thread.GetContext().cpu_registers[BaseRegister]; |
| 119 | 120 | ||
| 120 | while (base_pointer != 0) { | 121 | while (base_pointer != 0) { |
| 121 | const u64 lr = Memory::Read64(base_pointer + sizeof(u64)); | 122 | const u64 lr = memory.Read64(base_pointer + sizeof(u64)); |
| 122 | if (lr == 0) { | 123 | if (lr == 0) { |
| 123 | break; | 124 | break; |
| 124 | } | 125 | } |
| @@ -126,7 +127,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() cons | |||
| 126 | list.push_back(std::make_unique<WaitTreeText>( | 127 | list.push_back(std::make_unique<WaitTreeText>( |
| 127 | tr("0x%1").arg(lr - sizeof(u32), 16, 16, QLatin1Char{'0'}))); | 128 | tr("0x%1").arg(lr - sizeof(u32), 16, 16, QLatin1Char{'0'}))); |
| 128 | 129 | ||
| 129 | base_pointer = Memory::Read64(base_pointer); | 130 | base_pointer = memory.Read64(base_pointer); |
| 130 | } | 131 | } |
| 131 | 132 | ||
| 132 | return list; | 133 | return list; |