diff options
| author | 2023-12-06 14:19:17 +0100 | |
|---|---|---|
| committer | 2023-12-06 14:19:17 +0100 | |
| commit | 8a79dd2d6c6445bff63ea1f2f5f1611a6afcd97a (patch) | |
| tree | 265bf3c7970a570479c6a3ac1250549995f0329c /src/core/arm/dynarmic | |
| parent | Merge pull request #12271 from liamwhite/pretext-fix (diff) | |
| parent | arm: fix context save of vector regs (diff) | |
| download | yuzu-8a79dd2d6c6445bff63ea1f2f5f1611a6afcd97a.tar.gz yuzu-8a79dd2d6c6445bff63ea1f2f5f1611a6afcd97a.tar.xz yuzu-8a79dd2d6c6445bff63ea1f2f5f1611a6afcd97a.zip | |
Merge pull request #12236 from liamwhite/cpu-refactor
core: refactor emulated cpu core activation
Diffstat (limited to 'src/core/arm/dynarmic')
| -rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_32.cpp | 325 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_32.h | 92 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_64.cpp | 340 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_64.h | 77 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/dynarmic_cp15.cpp | 4 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/dynarmic_cp15.h | 8 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/dynarmic_exclusive_monitor.h | 8 |
7 files changed, 362 insertions, 492 deletions
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 44a297cdc..f34865e26 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp | |||
| @@ -1,25 +1,13 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <cinttypes> | ||
| 5 | #include <memory> | ||
| 6 | #include <dynarmic/interface/A32/a32.h> | ||
| 7 | #include <dynarmic/interface/A32/config.h> | ||
| 8 | #include "common/assert.h" | ||
| 9 | #include "common/literals.h" | ||
| 10 | #include "common/logging/log.h" | ||
| 11 | #include "common/page_table.h" | ||
| 12 | #include "common/settings.h" | 4 | #include "common/settings.h" |
| 13 | #include "core/arm/dynarmic/arm_dynarmic.h" | 5 | #include "core/arm/dynarmic/arm_dynarmic.h" |
| 14 | #include "core/arm/dynarmic/arm_dynarmic_32.h" | 6 | #include "core/arm/dynarmic/arm_dynarmic_32.h" |
| 15 | #include "core/arm/dynarmic/dynarmic_cp15.h" | 7 | #include "core/arm/dynarmic/dynarmic_cp15.h" |
| 16 | #include "core/arm/dynarmic/dynarmic_exclusive_monitor.h" | 8 | #include "core/arm/dynarmic/dynarmic_exclusive_monitor.h" |
| 17 | #include "core/core.h" | ||
| 18 | #include "core/core_timing.h" | 9 | #include "core/core_timing.h" |
| 19 | #include "core/debugger/debugger.h" | ||
| 20 | #include "core/hle/kernel/k_process.h" | 10 | #include "core/hle/kernel/k_process.h" |
| 21 | #include "core/hle/kernel/svc.h" | ||
| 22 | #include "core/memory.h" | ||
| 23 | 11 | ||
| 24 | namespace Core { | 12 | namespace Core { |
| 25 | 13 | ||
| @@ -27,78 +15,78 @@ using namespace Common::Literals; | |||
| 27 | 15 | ||
| 28 | class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { | 16 | class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { |
| 29 | public: | 17 | public: |
| 30 | explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_) | 18 | explicit DynarmicCallbacks32(ArmDynarmic32& parent, const Kernel::KProcess* process) |
| 31 | : parent{parent_}, memory(parent.system.ApplicationMemory()), | 19 | : m_parent{parent}, m_memory(process->GetMemory()), |
| 32 | debugger_enabled{parent.system.DebuggerEnabled()}, | 20 | m_process(process), m_debugger_enabled{parent.m_system.DebuggerEnabled()}, |
| 33 | check_memory_access{debugger_enabled || | 21 | m_check_memory_access{m_debugger_enabled || |
| 34 | !Settings::values.cpuopt_ignore_memory_aborts.GetValue()} {} | 22 | !Settings::values.cpuopt_ignore_memory_aborts.GetValue()} {} |
| 35 | 23 | ||
| 36 | u8 MemoryRead8(u32 vaddr) override { | 24 | u8 MemoryRead8(u32 vaddr) override { |
| 37 | CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read); | 25 | CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read); |
| 38 | return memory.Read8(vaddr); | 26 | return m_memory.Read8(vaddr); |
| 39 | } | 27 | } |
| 40 | u16 MemoryRead16(u32 vaddr) override { | 28 | u16 MemoryRead16(u32 vaddr) override { |
| 41 | CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read); | 29 | CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read); |
| 42 | return memory.Read16(vaddr); | 30 | return m_memory.Read16(vaddr); |
| 43 | } | 31 | } |
| 44 | u32 MemoryRead32(u32 vaddr) override { | 32 | u32 MemoryRead32(u32 vaddr) override { |
| 45 | CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read); | 33 | CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read); |
| 46 | return memory.Read32(vaddr); | 34 | return m_memory.Read32(vaddr); |
| 47 | } | 35 | } |
| 48 | u64 MemoryRead64(u32 vaddr) override { | 36 | u64 MemoryRead64(u32 vaddr) override { |
| 49 | CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read); | 37 | CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read); |
| 50 | return memory.Read64(vaddr); | 38 | return m_memory.Read64(vaddr); |
| 51 | } | 39 | } |
| 52 | std::optional<u32> MemoryReadCode(u32 vaddr) override { | 40 | std::optional<u32> MemoryReadCode(u32 vaddr) override { |
| 53 | if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) { | 41 | if (!m_memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) { |
| 54 | return std::nullopt; | 42 | return std::nullopt; |
| 55 | } | 43 | } |
| 56 | return memory.Read32(vaddr); | 44 | return m_memory.Read32(vaddr); |
| 57 | } | 45 | } |
| 58 | 46 | ||
| 59 | void MemoryWrite8(u32 vaddr, u8 value) override { | 47 | void MemoryWrite8(u32 vaddr, u8 value) override { |
| 60 | if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) { | 48 | if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) { |
| 61 | memory.Write8(vaddr, value); | 49 | m_memory.Write8(vaddr, value); |
| 62 | } | 50 | } |
| 63 | } | 51 | } |
| 64 | void MemoryWrite16(u32 vaddr, u16 value) override { | 52 | void MemoryWrite16(u32 vaddr, u16 value) override { |
| 65 | if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) { | 53 | if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) { |
| 66 | memory.Write16(vaddr, value); | 54 | m_memory.Write16(vaddr, value); |
| 67 | } | 55 | } |
| 68 | } | 56 | } |
| 69 | void MemoryWrite32(u32 vaddr, u32 value) override { | 57 | void MemoryWrite32(u32 vaddr, u32 value) override { |
| 70 | if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) { | 58 | if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) { |
| 71 | memory.Write32(vaddr, value); | 59 | m_memory.Write32(vaddr, value); |
| 72 | } | 60 | } |
| 73 | } | 61 | } |
| 74 | void MemoryWrite64(u32 vaddr, u64 value) override { | 62 | void MemoryWrite64(u32 vaddr, u64 value) override { |
| 75 | if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) { | 63 | if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) { |
| 76 | memory.Write64(vaddr, value); | 64 | m_memory.Write64(vaddr, value); |
| 77 | } | 65 | } |
| 78 | } | 66 | } |
| 79 | 67 | ||
| 80 | bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override { | 68 | bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override { |
| 81 | return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) && | 69 | return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) && |
| 82 | memory.WriteExclusive8(vaddr, value, expected); | 70 | m_memory.WriteExclusive8(vaddr, value, expected); |
| 83 | } | 71 | } |
| 84 | bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override { | 72 | bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override { |
| 85 | return CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write) && | 73 | return CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write) && |
| 86 | memory.WriteExclusive16(vaddr, value, expected); | 74 | m_memory.WriteExclusive16(vaddr, value, expected); |
| 87 | } | 75 | } |
| 88 | bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override { | 76 | bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override { |
| 89 | return CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write) && | 77 | return CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write) && |
| 90 | memory.WriteExclusive32(vaddr, value, expected); | 78 | m_memory.WriteExclusive32(vaddr, value, expected); |
| 91 | } | 79 | } |
| 92 | bool MemoryWriteExclusive64(u32 vaddr, u64 value, u64 expected) override { | 80 | bool MemoryWriteExclusive64(u32 vaddr, u64 value, u64 expected) override { |
| 93 | return CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write) && | 81 | return CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write) && |
| 94 | memory.WriteExclusive64(vaddr, value, expected); | 82 | m_memory.WriteExclusive64(vaddr, value, expected); |
| 95 | } | 83 | } |
| 96 | 84 | ||
| 97 | void InterpreterFallback(u32 pc, std::size_t num_instructions) override { | 85 | void InterpreterFallback(u32 pc, std::size_t num_instructions) override { |
| 98 | parent.LogBacktrace(); | 86 | m_parent.LogBacktrace(m_process); |
| 99 | LOG_ERROR(Core_ARM, | 87 | LOG_ERROR(Core_ARM, |
| 100 | "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, | 88 | "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, |
| 101 | num_instructions, memory.Read32(pc)); | 89 | num_instructions, m_memory.Read32(pc)); |
| 102 | } | 90 | } |
| 103 | 91 | ||
| 104 | void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { | 92 | void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { |
| @@ -108,73 +96,64 @@ public: | |||
| 108 | ReturnException(pc, PrefetchAbort); | 96 | ReturnException(pc, PrefetchAbort); |
| 109 | return; | 97 | return; |
| 110 | default: | 98 | default: |
| 111 | if (debugger_enabled) { | 99 | if (m_debugger_enabled) { |
| 112 | ReturnException(pc, InstructionBreakpoint); | 100 | ReturnException(pc, InstructionBreakpoint); |
| 113 | return; | 101 | return; |
| 114 | } | 102 | } |
| 115 | 103 | ||
| 116 | parent.LogBacktrace(); | 104 | m_parent.LogBacktrace(m_process); |
| 117 | LOG_CRITICAL(Core_ARM, | 105 | LOG_CRITICAL(Core_ARM, |
| 118 | "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})", | 106 | "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})", |
| 119 | exception, pc, memory.Read32(pc), parent.IsInThumbMode()); | 107 | exception, pc, m_memory.Read32(pc), m_parent.IsInThumbMode()); |
| 120 | } | 108 | } |
| 121 | } | 109 | } |
| 122 | 110 | ||
| 123 | void CallSVC(u32 swi) override { | 111 | void CallSVC(u32 swi) override { |
| 124 | parent.svc_swi = swi; | 112 | m_parent.m_svc_swi = swi; |
| 125 | parent.jit.load()->HaltExecution(SupervisorCall); | 113 | m_parent.m_jit->HaltExecution(SupervisorCall); |
| 126 | } | 114 | } |
| 127 | 115 | ||
| 128 | void AddTicks(u64 ticks) override { | 116 | void AddTicks(u64 ticks) override { |
| 129 | if (parent.uses_wall_clock) { | 117 | ASSERT_MSG(!m_parent.m_uses_wall_clock, "Dynarmic ticking disabled"); |
| 130 | return; | ||
| 131 | } | ||
| 132 | 118 | ||
| 133 | // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a | 119 | // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a |
| 134 | // rough approximation of the amount of executed ticks in the system, it may be thrown off | 120 | // rough approximation of the amount of executed ticks in the system, it may be thrown off |
| 135 | // if not all cores are doing a similar amount of work. Instead of doing this, we should | 121 | // if not all cores are doing a similar amount of work. Instead of doing this, we should |
| 136 | // device a way so that timing is consistent across all cores without increasing the ticks 4 | 122 | // device a way so that timing is consistent across all cores without increasing the ticks 4 |
| 137 | // times. | 123 | // times. |
| 138 | u64 amortized_ticks = | 124 | u64 amortized_ticks = ticks / Core::Hardware::NUM_CPU_CORES; |
| 139 | (ticks - num_interpreted_instructions) / Core::Hardware::NUM_CPU_CORES; | ||
| 140 | // Always execute at least one tick. | 125 | // Always execute at least one tick. |
| 141 | amortized_ticks = std::max<u64>(amortized_ticks, 1); | 126 | amortized_ticks = std::max<u64>(amortized_ticks, 1); |
| 142 | 127 | ||
| 143 | parent.system.CoreTiming().AddTicks(amortized_ticks); | 128 | m_parent.m_system.CoreTiming().AddTicks(amortized_ticks); |
| 144 | num_interpreted_instructions = 0; | ||
| 145 | } | 129 | } |
| 146 | 130 | ||
| 147 | u64 GetTicksRemaining() override { | 131 | u64 GetTicksRemaining() override { |
| 148 | if (parent.uses_wall_clock) { | 132 | ASSERT_MSG(!m_parent.m_uses_wall_clock, "Dynarmic ticking disabled"); |
| 149 | if (!IsInterrupted()) { | ||
| 150 | return minimum_run_cycles; | ||
| 151 | } | ||
| 152 | return 0U; | ||
| 153 | } | ||
| 154 | 133 | ||
| 155 | return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); | 134 | return std::max<s64>(m_parent.m_system.CoreTiming().GetDowncount(), 0); |
| 156 | } | 135 | } |
| 157 | 136 | ||
| 158 | bool CheckMemoryAccess(u64 addr, u64 size, Kernel::DebugWatchpointType type) { | 137 | bool CheckMemoryAccess(u64 addr, u64 size, Kernel::DebugWatchpointType type) { |
| 159 | if (!check_memory_access) { | 138 | if (!m_check_memory_access) { |
| 160 | return true; | 139 | return true; |
| 161 | } | 140 | } |
| 162 | 141 | ||
| 163 | if (!memory.IsValidVirtualAddressRange(addr, size)) { | 142 | if (!m_memory.IsValidVirtualAddressRange(addr, size)) { |
| 164 | LOG_CRITICAL(Core_ARM, "Stopping execution due to unmapped memory access at {:#x}", | 143 | LOG_CRITICAL(Core_ARM, "Stopping execution due to unmapped memory access at {:#x}", |
| 165 | addr); | 144 | addr); |
| 166 | parent.jit.load()->HaltExecution(PrefetchAbort); | 145 | m_parent.m_jit->HaltExecution(PrefetchAbort); |
| 167 | return false; | 146 | return false; |
| 168 | } | 147 | } |
| 169 | 148 | ||
| 170 | if (!debugger_enabled) { | 149 | if (!m_debugger_enabled) { |
| 171 | return true; | 150 | return true; |
| 172 | } | 151 | } |
| 173 | 152 | ||
| 174 | const auto match{parent.MatchingWatchpoint(addr, size, type)}; | 153 | const auto match{m_parent.MatchingWatchpoint(addr, size, type)}; |
| 175 | if (match) { | 154 | if (match) { |
| 176 | parent.halted_watchpoint = match; | 155 | m_parent.m_halted_watchpoint = match; |
| 177 | parent.jit.load()->HaltExecution(DataAbort); | 156 | m_parent.m_jit->HaltExecution(DataAbort); |
| 178 | return false; | 157 | return false; |
| 179 | } | 158 | } |
| 180 | 159 | ||
| @@ -182,32 +161,31 @@ public: | |||
| 182 | } | 161 | } |
| 183 | 162 | ||
| 184 | void ReturnException(u32 pc, Dynarmic::HaltReason hr) { | 163 | void ReturnException(u32 pc, Dynarmic::HaltReason hr) { |
| 185 | parent.SaveContext(parent.breakpoint_context); | 164 | m_parent.GetContext(m_parent.m_breakpoint_context); |
| 186 | parent.breakpoint_context.cpu_registers[15] = pc; | 165 | m_parent.m_breakpoint_context.pc = pc; |
| 187 | parent.jit.load()->HaltExecution(hr); | 166 | m_parent.m_breakpoint_context.r[15] = pc; |
| 188 | } | 167 | m_parent.m_jit->HaltExecution(hr); |
| 189 | |||
| 190 | bool IsInterrupted() { | ||
| 191 | return parent.system.Kernel().PhysicalCore(parent.core_index).IsInterrupted(); | ||
| 192 | } | 168 | } |
| 193 | 169 | ||
| 194 | ARM_Dynarmic_32& parent; | 170 | ArmDynarmic32& m_parent; |
| 195 | Core::Memory::Memory& memory; | 171 | Core::Memory::Memory& m_memory; |
| 196 | std::size_t num_interpreted_instructions{}; | 172 | const Kernel::KProcess* m_process{}; |
| 197 | const bool debugger_enabled{}; | 173 | const bool m_debugger_enabled{}; |
| 198 | const bool check_memory_access{}; | 174 | const bool m_check_memory_access{}; |
| 199 | static constexpr u64 minimum_run_cycles = 10000U; | 175 | static constexpr u64 MinimumRunCycles = 10000U; |
| 200 | }; | 176 | }; |
| 201 | 177 | ||
| 202 | std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* page_table) const { | 178 | std::shared_ptr<Dynarmic::A32::Jit> ArmDynarmic32::MakeJit(Common::PageTable* page_table) const { |
| 203 | Dynarmic::A32::UserConfig config; | 179 | Dynarmic::A32::UserConfig config; |
| 204 | config.callbacks = cb.get(); | 180 | config.callbacks = m_cb.get(); |
| 205 | config.coprocessors[15] = cp15; | 181 | config.coprocessors[15] = m_cp15; |
| 206 | config.define_unpredictable_behaviour = true; | 182 | config.define_unpredictable_behaviour = true; |
| 207 | static constexpr std::size_t YUZU_PAGEBITS = 12; | 183 | |
| 208 | static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - YUZU_PAGEBITS); | ||
| 209 | if (page_table) { | 184 | if (page_table) { |
| 210 | config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>( | 185 | constexpr size_t PageBits = 12; |
| 186 | constexpr size_t NumPageTableEntries = 1 << (32 - PageBits); | ||
| 187 | |||
| 188 | config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>( | ||
| 211 | page_table->pointers.data()); | 189 | page_table->pointers.data()); |
| 212 | config.absolute_offset_page_table = true; | 190 | config.absolute_offset_page_table = true; |
| 213 | config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS; | 191 | config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS; |
| @@ -221,12 +199,12 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* | |||
| 221 | } | 199 | } |
| 222 | 200 | ||
| 223 | // Multi-process state | 201 | // Multi-process state |
| 224 | config.processor_id = core_index; | 202 | config.processor_id = m_core_index; |
| 225 | config.global_monitor = &exclusive_monitor.monitor; | 203 | config.global_monitor = &m_exclusive_monitor.monitor; |
| 226 | 204 | ||
| 227 | // Timing | 205 | // Timing |
| 228 | config.wall_clock_cntpct = uses_wall_clock; | 206 | config.wall_clock_cntpct = m_uses_wall_clock; |
| 229 | config.enable_cycle_counting = true; | 207 | config.enable_cycle_counting = !m_uses_wall_clock; |
| 230 | 208 | ||
| 231 | // Code cache size | 209 | // Code cache size |
| 232 | #ifdef ARCHITECTURE_arm64 | 210 | #ifdef ARCHITECTURE_arm64 |
| @@ -236,7 +214,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* | |||
| 236 | #endif | 214 | #endif |
| 237 | 215 | ||
| 238 | // Allow memory fault handling to work | 216 | // Allow memory fault handling to work |
| 239 | if (system.DebuggerEnabled()) { | 217 | if (m_system.DebuggerEnabled()) { |
| 240 | config.check_halt_on_memory_access = true; | 218 | config.check_halt_on_memory_access = true; |
| 241 | } | 219 | } |
| 242 | 220 | ||
| @@ -325,137 +303,140 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* | |||
| 325 | return std::make_unique<Dynarmic::A32::Jit>(config); | 303 | return std::make_unique<Dynarmic::A32::Jit>(config); |
| 326 | } | 304 | } |
| 327 | 305 | ||
| 328 | HaltReason ARM_Dynarmic_32::RunJit() { | 306 | static std::pair<u32, u32> FpscrToFpsrFpcr(u32 fpscr) { |
| 329 | return TranslateHaltReason(jit.load()->Run()); | 307 | // FPSCR bits [31:27] are mapped to FPSR[31:27]. |
| 308 | // FPSCR bit [7] is mapped to FPSR[7]. | ||
| 309 | // FPSCR bits [4:0] are mapped to FPSR[4:0]. | ||
| 310 | const u32 nzcv = fpscr & 0xf8000000; | ||
| 311 | const u32 idc = fpscr & 0x80; | ||
| 312 | const u32 fiq = fpscr & 0x1f; | ||
| 313 | const u32 fpsr = nzcv | idc | fiq; | ||
| 314 | |||
| 315 | // FPSCR bits [26:15] are mapped to FPCR[26:15]. | ||
| 316 | // FPSCR bits [12:8] are mapped to FPCR[12:8]. | ||
| 317 | const u32 round = fpscr & 0x7ff8000; | ||
| 318 | const u32 trap = fpscr & 0x1f00; | ||
| 319 | const u32 fpcr = round | trap; | ||
| 320 | |||
| 321 | return {fpsr, fpcr}; | ||
| 330 | } | 322 | } |
| 331 | 323 | ||
| 332 | HaltReason ARM_Dynarmic_32::StepJit() { | 324 | static u32 FpsrFpcrToFpscr(u64 fpsr, u64 fpcr) { |
| 333 | return TranslateHaltReason(jit.load()->Step()); | 325 | auto [s, c] = FpscrToFpsrFpcr(static_cast<u32>(fpsr | fpcr)); |
| 326 | return s | c; | ||
| 334 | } | 327 | } |
| 335 | 328 | ||
| 336 | u32 ARM_Dynarmic_32::GetSvcNumber() const { | 329 | bool ArmDynarmic32::IsInThumbMode() const { |
| 337 | return svc_swi; | 330 | return (m_jit->Cpsr() & 0x20) != 0; |
| 338 | } | 331 | } |
| 339 | 332 | ||
| 340 | const Kernel::DebugWatchpoint* ARM_Dynarmic_32::HaltedWatchpoint() const { | 333 | HaltReason ArmDynarmic32::RunThread(Kernel::KThread* thread) { |
| 341 | return halted_watchpoint; | 334 | m_jit->ClearExclusiveState(); |
| 335 | return TranslateHaltReason(m_jit->Run()); | ||
| 342 | } | 336 | } |
| 343 | 337 | ||
| 344 | void ARM_Dynarmic_32::RewindBreakpointInstruction() { | 338 | HaltReason ArmDynarmic32::StepThread(Kernel::KThread* thread) { |
| 345 | LoadContext(breakpoint_context); | 339 | m_jit->ClearExclusiveState(); |
| 340 | return TranslateHaltReason(m_jit->Step()); | ||
| 346 | } | 341 | } |
| 347 | 342 | ||
| 348 | ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, bool uses_wall_clock_, | 343 | u32 ArmDynarmic32::GetSvcNumber() const { |
| 349 | DynarmicExclusiveMonitor& exclusive_monitor_, | 344 | return m_svc_swi; |
| 350 | std::size_t core_index_) | 345 | } |
| 351 | : ARM_Interface{system_, uses_wall_clock_}, cb(std::make_unique<DynarmicCallbacks32>(*this)), | ||
| 352 | cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index_}, | ||
| 353 | exclusive_monitor{exclusive_monitor_}, null_jit{MakeJit(nullptr)}, jit{null_jit.get()} {} | ||
| 354 | 346 | ||
| 355 | ARM_Dynarmic_32::~ARM_Dynarmic_32() = default; | 347 | void ArmDynarmic32::GetSvcArguments(std::span<uint64_t, 8> args) const { |
| 348 | Dynarmic::A32::Jit& j = *m_jit; | ||
| 349 | auto& gpr = j.Regs(); | ||
| 356 | 350 | ||
| 357 | void ARM_Dynarmic_32::SetPC(u64 pc) { | 351 | for (size_t i = 0; i < 8; i++) { |
| 358 | jit.load()->Regs()[15] = static_cast<u32>(pc); | 352 | args[i] = gpr[i]; |
| 353 | } | ||
| 359 | } | 354 | } |
| 360 | 355 | ||
| 361 | u64 ARM_Dynarmic_32::GetPC() const { | 356 | void ArmDynarmic32::SetSvcArguments(std::span<const uint64_t, 8> args) { |
| 362 | return jit.load()->Regs()[15]; | 357 | Dynarmic::A32::Jit& j = *m_jit; |
| 363 | } | 358 | auto& gpr = j.Regs(); |
| 364 | 359 | ||
| 365 | u64 ARM_Dynarmic_32::GetSP() const { | 360 | for (size_t i = 0; i < 8; i++) { |
| 366 | return jit.load()->Regs()[13]; | 361 | gpr[i] = static_cast<u32>(args[i]); |
| 362 | } | ||
| 367 | } | 363 | } |
| 368 | 364 | ||
| 369 | u64 ARM_Dynarmic_32::GetReg(int index) const { | 365 | const Kernel::DebugWatchpoint* ArmDynarmic32::HaltedWatchpoint() const { |
| 370 | return jit.load()->Regs()[index]; | 366 | return m_halted_watchpoint; |
| 371 | } | 367 | } |
| 372 | 368 | ||
| 373 | void ARM_Dynarmic_32::SetReg(int index, u64 value) { | 369 | void ArmDynarmic32::RewindBreakpointInstruction() { |
| 374 | jit.load()->Regs()[index] = static_cast<u32>(value); | 370 | this->SetContext(m_breakpoint_context); |
| 375 | } | 371 | } |
| 376 | 372 | ||
| 377 | u128 ARM_Dynarmic_32::GetVectorReg(int index) const { | 373 | ArmDynarmic32::ArmDynarmic32(System& system, bool uses_wall_clock, const Kernel::KProcess* process, |
| 378 | return {}; | 374 | DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index) |
| 375 | : ArmInterface{uses_wall_clock}, m_system{system}, m_exclusive_monitor{exclusive_monitor}, | ||
| 376 | m_cb(std::make_unique<DynarmicCallbacks32>(*this, process)), | ||
| 377 | m_cp15(std::make_shared<DynarmicCP15>(*this)), m_core_index{core_index} { | ||
| 378 | auto& page_table_impl = process->GetPageTable().GetBasePageTable().GetImpl(); | ||
| 379 | m_jit = MakeJit(&page_table_impl); | ||
| 379 | } | 380 | } |
| 380 | 381 | ||
| 381 | void ARM_Dynarmic_32::SetVectorReg(int index, u128 value) {} | 382 | ArmDynarmic32::~ArmDynarmic32() = default; |
| 382 | 383 | ||
| 383 | u32 ARM_Dynarmic_32::GetPSTATE() const { | 384 | void ArmDynarmic32::SetTpidrroEl0(u64 value) { |
| 384 | return jit.load()->Cpsr(); | 385 | m_cp15->uro = static_cast<u32>(value); |
| 385 | } | 386 | } |
| 386 | 387 | ||
| 387 | void ARM_Dynarmic_32::SetPSTATE(u32 cpsr) { | 388 | void ArmDynarmic32::GetContext(Kernel::Svc::ThreadContext& ctx) const { |
| 388 | jit.load()->SetCpsr(cpsr); | 389 | Dynarmic::A32::Jit& j = *m_jit; |
| 389 | } | 390 | auto& gpr = j.Regs(); |
| 391 | auto& fpr = j.ExtRegs(); | ||
| 390 | 392 | ||
| 391 | u64 ARM_Dynarmic_32::GetTlsAddress() const { | 393 | for (size_t i = 0; i < 16; i++) { |
| 392 | return cp15->uro; | 394 | ctx.r[i] = gpr[i]; |
| 393 | } | 395 | } |
| 394 | 396 | ||
| 395 | void ARM_Dynarmic_32::SetTlsAddress(u64 address) { | 397 | ctx.fp = gpr[11]; |
| 396 | cp15->uro = static_cast<u32>(address); | 398 | ctx.sp = gpr[13]; |
| 397 | } | 399 | ctx.lr = gpr[14]; |
| 400 | ctx.pc = gpr[15]; | ||
| 401 | ctx.pstate = j.Cpsr(); | ||
| 398 | 402 | ||
| 399 | u64 ARM_Dynarmic_32::GetTPIDR_EL0() const { | 403 | static_assert(sizeof(fpr) <= sizeof(ctx.v)); |
| 400 | return cp15->uprw; | 404 | std::memcpy(ctx.v.data(), &fpr, sizeof(fpr)); |
| 401 | } | ||
| 402 | 405 | ||
| 403 | void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) { | 406 | auto [fpsr, fpcr] = FpscrToFpsrFpcr(j.Fpscr()); |
| 404 | cp15->uprw = static_cast<u32>(value); | 407 | ctx.fpcr = fpcr; |
| 408 | ctx.fpsr = fpsr; | ||
| 409 | ctx.tpidr = m_cp15->uprw; | ||
| 405 | } | 410 | } |
| 406 | 411 | ||
| 407 | void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) const { | 412 | void ArmDynarmic32::SetContext(const Kernel::Svc::ThreadContext& ctx) { |
| 408 | Dynarmic::A32::Jit* j = jit.load(); | 413 | Dynarmic::A32::Jit& j = *m_jit; |
| 409 | ctx.cpu_registers = j->Regs(); | 414 | auto& gpr = j.Regs(); |
| 410 | ctx.extension_registers = j->ExtRegs(); | 415 | auto& fpr = j.ExtRegs(); |
| 411 | ctx.cpsr = j->Cpsr(); | ||
| 412 | ctx.fpscr = j->Fpscr(); | ||
| 413 | } | ||
| 414 | 416 | ||
| 415 | void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) { | 417 | for (size_t i = 0; i < 16; i++) { |
| 416 | Dynarmic::A32::Jit* j = jit.load(); | 418 | gpr[i] = static_cast<u32>(ctx.r[i]); |
| 417 | j->Regs() = ctx.cpu_registers; | 419 | } |
| 418 | j->ExtRegs() = ctx.extension_registers; | ||
| 419 | j->SetCpsr(ctx.cpsr); | ||
| 420 | j->SetFpscr(ctx.fpscr); | ||
| 421 | } | ||
| 422 | 420 | ||
| 423 | void ARM_Dynarmic_32::SignalInterrupt() { | 421 | j.SetCpsr(ctx.pstate); |
| 424 | jit.load()->HaltExecution(BreakLoop); | ||
| 425 | } | ||
| 426 | 422 | ||
| 427 | void ARM_Dynarmic_32::ClearInterrupt() { | 423 | static_assert(sizeof(fpr) <= sizeof(ctx.v)); |
| 428 | jit.load()->ClearHalt(BreakLoop); | 424 | std::memcpy(&fpr, ctx.v.data(), sizeof(fpr)); |
| 429 | } | ||
| 430 | 425 | ||
| 431 | void ARM_Dynarmic_32::ClearInstructionCache() { | 426 | j.SetFpscr(FpsrFpcrToFpscr(ctx.fpsr, ctx.fpcr)); |
| 432 | jit.load()->ClearCache(); | 427 | m_cp15->uprw = static_cast<u32>(ctx.tpidr); |
| 433 | } | 428 | } |
| 434 | 429 | ||
| 435 | void ARM_Dynarmic_32::InvalidateCacheRange(u64 addr, std::size_t size) { | 430 | void ArmDynarmic32::SignalInterrupt(Kernel::KThread* thread) { |
| 436 | jit.load()->InvalidateCacheRange(static_cast<u32>(addr), size); | 431 | m_jit->HaltExecution(BreakLoop); |
| 437 | } | 432 | } |
| 438 | 433 | ||
| 439 | void ARM_Dynarmic_32::ClearExclusiveState() { | 434 | void ArmDynarmic32::ClearInstructionCache() { |
| 440 | jit.load()->ClearExclusiveState(); | 435 | m_jit->ClearCache(); |
| 441 | } | 436 | } |
| 442 | 437 | ||
| 443 | void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table, | 438 | void ArmDynarmic32::InvalidateCacheRange(u64 addr, std::size_t size) { |
| 444 | std::size_t new_address_space_size_in_bits) { | 439 | m_jit->InvalidateCacheRange(static_cast<u32>(addr), size); |
| 445 | ThreadContext32 ctx{}; | ||
| 446 | SaveContext(ctx); | ||
| 447 | |||
| 448 | auto key = std::make_pair(&page_table, new_address_space_size_in_bits); | ||
| 449 | auto iter = jit_cache.find(key); | ||
| 450 | if (iter != jit_cache.end()) { | ||
| 451 | jit.store(iter->second.get()); | ||
| 452 | LoadContext(ctx); | ||
| 453 | return; | ||
| 454 | } | ||
| 455 | std::shared_ptr new_jit = MakeJit(&page_table); | ||
| 456 | jit.store(new_jit.get()); | ||
| 457 | LoadContext(ctx); | ||
| 458 | jit_cache.emplace(key, std::move(new_jit)); | ||
| 459 | } | 440 | } |
| 460 | 441 | ||
| 461 | } // namespace Core | 442 | } // namespace Core |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index 92fb3f836..185ac7cbf 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h | |||
| @@ -3,14 +3,8 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <atomic> | ||
| 7 | #include <memory> | ||
| 8 | #include <unordered_map> | ||
| 9 | |||
| 10 | #include <dynarmic/interface/A32/a32.h> | 6 | #include <dynarmic/interface/A32/a32.h> |
| 11 | #include <dynarmic/interface/A64/a64.h> | 7 | |
| 12 | #include "common/common_types.h" | ||
| 13 | #include "common/hash.h" | ||
| 14 | #include "core/arm/arm_interface.h" | 8 | #include "core/arm/arm_interface.h" |
| 15 | #include "core/arm/dynarmic/dynarmic_exclusive_monitor.h" | 9 | #include "core/arm/dynarmic/dynarmic_exclusive_monitor.h" |
| 16 | 10 | ||
| @@ -20,89 +14,63 @@ class Memory; | |||
| 20 | 14 | ||
| 21 | namespace Core { | 15 | namespace Core { |
| 22 | 16 | ||
| 23 | class CPUInterruptHandler; | ||
| 24 | class DynarmicCallbacks32; | 17 | class DynarmicCallbacks32; |
| 25 | class DynarmicCP15; | 18 | class DynarmicCP15; |
| 26 | class DynarmicExclusiveMonitor; | ||
| 27 | class System; | 19 | class System; |
| 28 | 20 | ||
| 29 | class ARM_Dynarmic_32 final : public ARM_Interface { | 21 | class ArmDynarmic32 final : public ArmInterface { |
| 30 | public: | 22 | public: |
| 31 | ARM_Dynarmic_32(System& system_, bool uses_wall_clock_, | 23 | ArmDynarmic32(System& system, bool uses_wall_clock, const Kernel::KProcess* process, |
| 32 | DynarmicExclusiveMonitor& exclusive_monitor_, std::size_t core_index_); | 24 | DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index); |
| 33 | ~ARM_Dynarmic_32() override; | 25 | ~ArmDynarmic32() override; |
| 34 | |||
| 35 | void SetPC(u64 pc) override; | ||
| 36 | u64 GetPC() const override; | ||
| 37 | u64 GetSP() const override; | ||
| 38 | u64 GetReg(int index) const override; | ||
| 39 | void SetReg(int index, u64 value) override; | ||
| 40 | u128 GetVectorReg(int index) const override; | ||
| 41 | void SetVectorReg(int index, u128 value) override; | ||
| 42 | u32 GetPSTATE() const override; | ||
| 43 | void SetPSTATE(u32 pstate) override; | ||
| 44 | u64 GetTlsAddress() const override; | ||
| 45 | void SetTlsAddress(u64 address) override; | ||
| 46 | void SetTPIDR_EL0(u64 value) override; | ||
| 47 | u64 GetTPIDR_EL0() const override; | ||
| 48 | |||
| 49 | bool IsInThumbMode() const { | ||
| 50 | return (GetPSTATE() & 0x20) != 0; | ||
| 51 | } | ||
| 52 | 26 | ||
| 53 | Architecture GetArchitecture() const override { | 27 | Architecture GetArchitecture() const override { |
| 54 | return Architecture::Aarch32; | 28 | return Architecture::AArch32; |
| 55 | } | 29 | } |
| 56 | void SaveContext(ThreadContext32& ctx) const override; | ||
| 57 | void SaveContext(ThreadContext64& ctx) const override {} | ||
| 58 | void LoadContext(const ThreadContext32& ctx) override; | ||
| 59 | void LoadContext(const ThreadContext64& ctx) override {} | ||
| 60 | 30 | ||
| 61 | void SignalInterrupt() override; | 31 | bool IsInThumbMode() const; |
| 62 | void ClearInterrupt() override; | 32 | |
| 63 | void ClearExclusiveState() override; | 33 | HaltReason RunThread(Kernel::KThread* thread) override; |
| 34 | HaltReason StepThread(Kernel::KThread* thread) override; | ||
| 35 | |||
| 36 | void GetContext(Kernel::Svc::ThreadContext& ctx) const override; | ||
| 37 | void SetContext(const Kernel::Svc::ThreadContext& ctx) override; | ||
| 38 | void SetTpidrroEl0(u64 value) override; | ||
| 39 | |||
| 40 | void GetSvcArguments(std::span<uint64_t, 8> args) const override; | ||
| 41 | void SetSvcArguments(std::span<const uint64_t, 8> args) override; | ||
| 42 | u32 GetSvcNumber() const override; | ||
| 64 | 43 | ||
| 44 | void SignalInterrupt(Kernel::KThread* thread) override; | ||
| 65 | void ClearInstructionCache() override; | 45 | void ClearInstructionCache() override; |
| 66 | void InvalidateCacheRange(u64 addr, std::size_t size) override; | 46 | void InvalidateCacheRange(u64 addr, std::size_t size) override; |
| 67 | void PageTableChanged(Common::PageTable& new_page_table, | ||
| 68 | std::size_t new_address_space_size_in_bits) override; | ||
| 69 | 47 | ||
| 70 | protected: | 48 | protected: |
| 71 | HaltReason RunJit() override; | ||
| 72 | HaltReason StepJit() override; | ||
| 73 | u32 GetSvcNumber() const override; | ||
| 74 | const Kernel::DebugWatchpoint* HaltedWatchpoint() const override; | 49 | const Kernel::DebugWatchpoint* HaltedWatchpoint() const override; |
| 75 | void RewindBreakpointInstruction() override; | 50 | void RewindBreakpointInstruction() override; |
| 76 | 51 | ||
| 77 | private: | 52 | private: |
| 78 | std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const; | 53 | System& m_system; |
| 79 | 54 | DynarmicExclusiveMonitor& m_exclusive_monitor; | |
| 80 | static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr, u64 pc); | ||
| 81 | |||
| 82 | using JitCacheKey = std::pair<Common::PageTable*, std::size_t>; | ||
| 83 | using JitCacheType = | ||
| 84 | std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A32::Jit>, Common::PairHash>; | ||
| 85 | 55 | ||
| 56 | private: | ||
| 86 | friend class DynarmicCallbacks32; | 57 | friend class DynarmicCallbacks32; |
| 87 | friend class DynarmicCP15; | 58 | friend class DynarmicCP15; |
| 88 | 59 | ||
| 89 | std::unique_ptr<DynarmicCallbacks32> cb; | 60 | std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const; |
| 90 | JitCacheType jit_cache; | ||
| 91 | std::shared_ptr<DynarmicCP15> cp15; | ||
| 92 | std::size_t core_index; | ||
| 93 | DynarmicExclusiveMonitor& exclusive_monitor; | ||
| 94 | 61 | ||
| 95 | std::shared_ptr<Dynarmic::A32::Jit> null_jit; | 62 | std::unique_ptr<DynarmicCallbacks32> m_cb{}; |
| 63 | std::shared_ptr<DynarmicCP15> m_cp15{}; | ||
| 64 | std::size_t m_core_index{}; | ||
| 96 | 65 | ||
| 97 | // A raw pointer here is fine; we never delete Jit instances. | 66 | std::shared_ptr<Dynarmic::A32::Jit> m_jit{}; |
| 98 | std::atomic<Dynarmic::A32::Jit*> jit; | ||
| 99 | 67 | ||
| 100 | // SVC callback | 68 | // SVC callback |
| 101 | u32 svc_swi{}; | 69 | u32 m_svc_swi{}; |
| 102 | 70 | ||
| 103 | // Watchpoint info | 71 | // Watchpoint info |
| 104 | const Kernel::DebugWatchpoint* halted_watchpoint; | 72 | const Kernel::DebugWatchpoint* m_halted_watchpoint{}; |
| 105 | ThreadContext32 breakpoint_context; | 73 | Kernel::Svc::ThreadContext m_breakpoint_context{}; |
| 106 | }; | 74 | }; |
| 107 | 75 | ||
| 108 | } // namespace Core | 76 | } // namespace Core |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 2e3674b6d..dff14756e 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp | |||
| @@ -1,25 +1,12 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <cinttypes> | ||
| 5 | #include <memory> | ||
| 6 | #include <dynarmic/interface/A64/a64.h> | ||
| 7 | #include <dynarmic/interface/A64/config.h> | ||
| 8 | #include "common/assert.h" | ||
| 9 | #include "common/literals.h" | ||
| 10 | #include "common/logging/log.h" | ||
| 11 | #include "common/page_table.h" | ||
| 12 | #include "common/settings.h" | 4 | #include "common/settings.h" |
| 13 | #include "core/arm/dynarmic/arm_dynarmic.h" | 5 | #include "core/arm/dynarmic/arm_dynarmic.h" |
| 14 | #include "core/arm/dynarmic/arm_dynarmic_64.h" | 6 | #include "core/arm/dynarmic/arm_dynarmic_64.h" |
| 15 | #include "core/arm/dynarmic/dynarmic_exclusive_monitor.h" | 7 | #include "core/arm/dynarmic/dynarmic_exclusive_monitor.h" |
| 16 | #include "core/core.h" | ||
| 17 | #include "core/core_timing.h" | 8 | #include "core/core_timing.h" |
| 18 | #include "core/debugger/debugger.h" | ||
| 19 | #include "core/hardware_properties.h" | ||
| 20 | #include "core/hle/kernel/k_process.h" | 9 | #include "core/hle/kernel/k_process.h" |
| 21 | #include "core/hle/kernel/svc.h" | ||
| 22 | #include "core/memory.h" | ||
| 23 | 10 | ||
| 24 | namespace Core { | 11 | namespace Core { |
| 25 | 12 | ||
| @@ -28,92 +15,92 @@ using namespace Common::Literals; | |||
| 28 | 15 | ||
| 29 | class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { | 16 | class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { |
| 30 | public: | 17 | public: |
| 31 | explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_) | 18 | explicit DynarmicCallbacks64(ArmDynarmic64& parent, const Kernel::KProcess* process) |
| 32 | : parent{parent_}, memory(parent.system.ApplicationMemory()), | 19 | : m_parent{parent}, m_memory(process->GetMemory()), |
| 33 | debugger_enabled{parent.system.DebuggerEnabled()}, | 20 | m_process(process), m_debugger_enabled{parent.m_system.DebuggerEnabled()}, |
| 34 | check_memory_access{debugger_enabled || | 21 | m_check_memory_access{m_debugger_enabled || |
| 35 | !Settings::values.cpuopt_ignore_memory_aborts.GetValue()} {} | 22 | !Settings::values.cpuopt_ignore_memory_aborts.GetValue()} {} |
| 36 | 23 | ||
| 37 | u8 MemoryRead8(u64 vaddr) override { | 24 | u8 MemoryRead8(u64 vaddr) override { |
| 38 | CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read); | 25 | CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read); |
| 39 | return memory.Read8(vaddr); | 26 | return m_memory.Read8(vaddr); |
| 40 | } | 27 | } |
| 41 | u16 MemoryRead16(u64 vaddr) override { | 28 | u16 MemoryRead16(u64 vaddr) override { |
| 42 | CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read); | 29 | CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read); |
| 43 | return memory.Read16(vaddr); | 30 | return m_memory.Read16(vaddr); |
| 44 | } | 31 | } |
| 45 | u32 MemoryRead32(u64 vaddr) override { | 32 | u32 MemoryRead32(u64 vaddr) override { |
| 46 | CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read); | 33 | CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read); |
| 47 | return memory.Read32(vaddr); | 34 | return m_memory.Read32(vaddr); |
| 48 | } | 35 | } |
| 49 | u64 MemoryRead64(u64 vaddr) override { | 36 | u64 MemoryRead64(u64 vaddr) override { |
| 50 | CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read); | 37 | CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read); |
| 51 | return memory.Read64(vaddr); | 38 | return m_memory.Read64(vaddr); |
| 52 | } | 39 | } |
| 53 | Vector MemoryRead128(u64 vaddr) override { | 40 | Vector MemoryRead128(u64 vaddr) override { |
| 54 | CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read); | 41 | CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read); |
| 55 | return {memory.Read64(vaddr), memory.Read64(vaddr + 8)}; | 42 | return {m_memory.Read64(vaddr), m_memory.Read64(vaddr + 8)}; |
| 56 | } | 43 | } |
| 57 | std::optional<u32> MemoryReadCode(u64 vaddr) override { | 44 | std::optional<u32> MemoryReadCode(u64 vaddr) override { |
| 58 | if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) { | 45 | if (!m_memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) { |
| 59 | return std::nullopt; | 46 | return std::nullopt; |
| 60 | } | 47 | } |
| 61 | return memory.Read32(vaddr); | 48 | return m_memory.Read32(vaddr); |
| 62 | } | 49 | } |
| 63 | 50 | ||
| 64 | void MemoryWrite8(u64 vaddr, u8 value) override { | 51 | void MemoryWrite8(u64 vaddr, u8 value) override { |
| 65 | if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) { | 52 | if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) { |
| 66 | memory.Write8(vaddr, value); | 53 | m_memory.Write8(vaddr, value); |
| 67 | } | 54 | } |
| 68 | } | 55 | } |
| 69 | void MemoryWrite16(u64 vaddr, u16 value) override { | 56 | void MemoryWrite16(u64 vaddr, u16 value) override { |
| 70 | if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) { | 57 | if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) { |
| 71 | memory.Write16(vaddr, value); | 58 | m_memory.Write16(vaddr, value); |
| 72 | } | 59 | } |
| 73 | } | 60 | } |
| 74 | void MemoryWrite32(u64 vaddr, u32 value) override { | 61 | void MemoryWrite32(u64 vaddr, u32 value) override { |
| 75 | if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) { | 62 | if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) { |
| 76 | memory.Write32(vaddr, value); | 63 | m_memory.Write32(vaddr, value); |
| 77 | } | 64 | } |
| 78 | } | 65 | } |
| 79 | void MemoryWrite64(u64 vaddr, u64 value) override { | 66 | void MemoryWrite64(u64 vaddr, u64 value) override { |
| 80 | if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) { | 67 | if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) { |
| 81 | memory.Write64(vaddr, value); | 68 | m_memory.Write64(vaddr, value); |
| 82 | } | 69 | } |
| 83 | } | 70 | } |
| 84 | void MemoryWrite128(u64 vaddr, Vector value) override { | 71 | void MemoryWrite128(u64 vaddr, Vector value) override { |
| 85 | if (CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Write)) { | 72 | if (CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Write)) { |
| 86 | memory.Write64(vaddr, value[0]); | 73 | m_memory.Write64(vaddr, value[0]); |
| 87 | memory.Write64(vaddr + 8, value[1]); | 74 | m_memory.Write64(vaddr + 8, value[1]); |
| 88 | } | 75 | } |
| 89 | } | 76 | } |
| 90 | 77 | ||
| 91 | bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override { | 78 | bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override { |
| 92 | return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) && | 79 | return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) && |
| 93 | memory.WriteExclusive8(vaddr, value, expected); | 80 | m_memory.WriteExclusive8(vaddr, value, expected); |
| 94 | } | 81 | } |
| 95 | bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override { | 82 | bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override { |
| 96 | return CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write) && | 83 | return CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write) && |
| 97 | memory.WriteExclusive16(vaddr, value, expected); | 84 | m_memory.WriteExclusive16(vaddr, value, expected); |
| 98 | } | 85 | } |
| 99 | bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, std::uint32_t expected) override { | 86 | bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, std::uint32_t expected) override { |
| 100 | return CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write) && | 87 | return CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write) && |
| 101 | memory.WriteExclusive32(vaddr, value, expected); | 88 | m_memory.WriteExclusive32(vaddr, value, expected); |
| 102 | } | 89 | } |
| 103 | bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, std::uint64_t expected) override { | 90 | bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, std::uint64_t expected) override { |
| 104 | return CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write) && | 91 | return CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write) && |
| 105 | memory.WriteExclusive64(vaddr, value, expected); | 92 | m_memory.WriteExclusive64(vaddr, value, expected); |
| 106 | } | 93 | } |
| 107 | bool MemoryWriteExclusive128(u64 vaddr, Vector value, Vector expected) override { | 94 | bool MemoryWriteExclusive128(u64 vaddr, Vector value, Vector expected) override { |
| 108 | return CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Write) && | 95 | return CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Write) && |
| 109 | memory.WriteExclusive128(vaddr, value, expected); | 96 | m_memory.WriteExclusive128(vaddr, value, expected); |
| 110 | } | 97 | } |
| 111 | 98 | ||
| 112 | void InterpreterFallback(u64 pc, std::size_t num_instructions) override { | 99 | void InterpreterFallback(u64 pc, std::size_t num_instructions) override { |
| 113 | parent.LogBacktrace(); | 100 | m_parent.LogBacktrace(m_process); |
| 114 | LOG_ERROR(Core_ARM, | 101 | LOG_ERROR(Core_ARM, |
| 115 | "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, | 102 | "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, |
| 116 | num_instructions, memory.Read32(pc)); | 103 | num_instructions, m_memory.Read32(pc)); |
| 117 | ReturnException(pc, PrefetchAbort); | 104 | ReturnException(pc, PrefetchAbort); |
| 118 | } | 105 | } |
| 119 | 106 | ||
| @@ -124,11 +111,11 @@ public: | |||
| 124 | static constexpr u64 ICACHE_LINE_SIZE = 64; | 111 | static constexpr u64 ICACHE_LINE_SIZE = 64; |
| 125 | 112 | ||
| 126 | const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1); | 113 | const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1); |
| 127 | parent.system.InvalidateCpuInstructionCacheRange(cache_line_start, ICACHE_LINE_SIZE); | 114 | m_parent.InvalidateCacheRange(cache_line_start, ICACHE_LINE_SIZE); |
| 128 | break; | 115 | break; |
| 129 | } | 116 | } |
| 130 | case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU: | 117 | case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU: |
| 131 | parent.system.InvalidateCpuInstructionCaches(); | 118 | m_parent.ClearInstructionCache(); |
| 132 | break; | 119 | break; |
| 133 | case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable: | 120 | case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable: |
| 134 | default: | 121 | default: |
| @@ -136,7 +123,7 @@ public: | |||
| 136 | break; | 123 | break; |
| 137 | } | 124 | } |
| 138 | 125 | ||
| 139 | parent.jit.load()->HaltExecution(Dynarmic::HaltReason::CacheInvalidation); | 126 | m_parent.m_jit->HaltExecution(Dynarmic::HaltReason::CacheInvalidation); |
| 140 | } | 127 | } |
| 141 | 128 | ||
| 142 | void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override { | 129 | void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override { |
| @@ -152,26 +139,24 @@ public: | |||
| 152 | ReturnException(pc, PrefetchAbort); | 139 | ReturnException(pc, PrefetchAbort); |
| 153 | return; | 140 | return; |
| 154 | default: | 141 | default: |
| 155 | if (debugger_enabled) { | 142 | if (m_debugger_enabled) { |
| 156 | ReturnException(pc, InstructionBreakpoint); | 143 | ReturnException(pc, InstructionBreakpoint); |
| 157 | return; | 144 | return; |
| 158 | } | 145 | } |
| 159 | 146 | ||
| 160 | parent.LogBacktrace(); | 147 | m_parent.LogBacktrace(m_process); |
| 161 | LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", | 148 | LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", |
| 162 | static_cast<std::size_t>(exception), pc, memory.Read32(pc)); | 149 | static_cast<std::size_t>(exception), pc, m_memory.Read32(pc)); |
| 163 | } | 150 | } |
| 164 | } | 151 | } |
| 165 | 152 | ||
| 166 | void CallSVC(u32 swi) override { | 153 | void CallSVC(u32 svc) override { |
| 167 | parent.svc_swi = swi; | 154 | m_parent.m_svc = svc; |
| 168 | parent.jit.load()->HaltExecution(SupervisorCall); | 155 | m_parent.m_jit->HaltExecution(SupervisorCall); |
| 169 | } | 156 | } |
| 170 | 157 | ||
| 171 | void AddTicks(u64 ticks) override { | 158 | void AddTicks(u64 ticks) override { |
| 172 | if (parent.uses_wall_clock) { | 159 | ASSERT_MSG(!m_parent.m_uses_wall_clock, "Dynarmic ticking disabled"); |
| 173 | return; | ||
| 174 | } | ||
| 175 | 160 | ||
| 176 | // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a | 161 | // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a |
| 177 | // rough approximation of the amount of executed ticks in the system, it may be thrown off | 162 | // rough approximation of the amount of executed ticks in the system, it may be thrown off |
| @@ -182,44 +167,39 @@ public: | |||
| 182 | // Always execute at least one tick. | 167 | // Always execute at least one tick. |
| 183 | amortized_ticks = std::max<u64>(amortized_ticks, 1); | 168 | amortized_ticks = std::max<u64>(amortized_ticks, 1); |
| 184 | 169 | ||
| 185 | parent.system.CoreTiming().AddTicks(amortized_ticks); | 170 | m_parent.m_system.CoreTiming().AddTicks(amortized_ticks); |
| 186 | } | 171 | } |
| 187 | 172 | ||
| 188 | u64 GetTicksRemaining() override { | 173 | u64 GetTicksRemaining() override { |
| 189 | if (parent.uses_wall_clock) { | 174 | ASSERT_MSG(!m_parent.m_uses_wall_clock, "Dynarmic ticking disabled"); |
| 190 | if (!IsInterrupted()) { | ||
| 191 | return minimum_run_cycles; | ||
| 192 | } | ||
| 193 | return 0U; | ||
| 194 | } | ||
| 195 | 175 | ||
| 196 | return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); | 176 | return std::max<s64>(m_parent.m_system.CoreTiming().GetDowncount(), 0); |
| 197 | } | 177 | } |
| 198 | 178 | ||
| 199 | u64 GetCNTPCT() override { | 179 | u64 GetCNTPCT() override { |
| 200 | return parent.system.CoreTiming().GetClockTicks(); | 180 | return m_parent.m_system.CoreTiming().GetClockTicks(); |
| 201 | } | 181 | } |
| 202 | 182 | ||
| 203 | bool CheckMemoryAccess(u64 addr, u64 size, Kernel::DebugWatchpointType type) { | 183 | bool CheckMemoryAccess(u64 addr, u64 size, Kernel::DebugWatchpointType type) { |
| 204 | if (!check_memory_access) { | 184 | if (!m_check_memory_access) { |
| 205 | return true; | 185 | return true; |
| 206 | } | 186 | } |
| 207 | 187 | ||
| 208 | if (!memory.IsValidVirtualAddressRange(addr, size)) { | 188 | if (!m_memory.IsValidVirtualAddressRange(addr, size)) { |
| 209 | LOG_CRITICAL(Core_ARM, "Stopping execution due to unmapped memory access at {:#x}", | 189 | LOG_CRITICAL(Core_ARM, "Stopping execution due to unmapped memory access at {:#x}", |
| 210 | addr); | 190 | addr); |
| 211 | parent.jit.load()->HaltExecution(PrefetchAbort); | 191 | m_parent.m_jit->HaltExecution(PrefetchAbort); |
| 212 | return false; | 192 | return false; |
| 213 | } | 193 | } |
| 214 | 194 | ||
| 215 | if (!debugger_enabled) { | 195 | if (!m_debugger_enabled) { |
| 216 | return true; | 196 | return true; |
| 217 | } | 197 | } |
| 218 | 198 | ||
| 219 | const auto match{parent.MatchingWatchpoint(addr, size, type)}; | 199 | const auto match{m_parent.MatchingWatchpoint(addr, size, type)}; |
| 220 | if (match) { | 200 | if (match) { |
| 221 | parent.halted_watchpoint = match; | 201 | m_parent.m_halted_watchpoint = match; |
| 222 | parent.jit.load()->HaltExecution(DataAbort); | 202 | m_parent.m_jit->HaltExecution(DataAbort); |
| 223 | return false; | 203 | return false; |
| 224 | } | 204 | } |
| 225 | 205 | ||
| @@ -227,30 +207,27 @@ public: | |||
| 227 | } | 207 | } |
| 228 | 208 | ||
| 229 | void ReturnException(u64 pc, Dynarmic::HaltReason hr) { | 209 | void ReturnException(u64 pc, Dynarmic::HaltReason hr) { |
| 230 | parent.SaveContext(parent.breakpoint_context); | 210 | m_parent.GetContext(m_parent.m_breakpoint_context); |
| 231 | parent.breakpoint_context.pc = pc; | 211 | m_parent.m_breakpoint_context.pc = pc; |
| 232 | parent.jit.load()->HaltExecution(hr); | 212 | m_parent.m_jit->HaltExecution(hr); |
| 233 | } | 213 | } |
| 234 | 214 | ||
| 235 | bool IsInterrupted() { | 215 | ArmDynarmic64& m_parent; |
| 236 | return parent.system.Kernel().PhysicalCore(parent.core_index).IsInterrupted(); | 216 | Core::Memory::Memory& m_memory; |
| 237 | } | 217 | u64 m_tpidrro_el0{}; |
| 238 | 218 | u64 m_tpidr_el0{}; | |
| 239 | ARM_Dynarmic_64& parent; | 219 | const Kernel::KProcess* m_process{}; |
| 240 | Core::Memory::Memory& memory; | 220 | const bool m_debugger_enabled{}; |
| 241 | u64 tpidrro_el0 = 0; | 221 | const bool m_check_memory_access{}; |
| 242 | u64 tpidr_el0 = 0; | 222 | static constexpr u64 MinimumRunCycles = 10000U; |
| 243 | const bool debugger_enabled{}; | ||
| 244 | const bool check_memory_access{}; | ||
| 245 | static constexpr u64 minimum_run_cycles = 10000U; | ||
| 246 | }; | 223 | }; |
| 247 | 224 | ||
| 248 | std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* page_table, | 225 | std::shared_ptr<Dynarmic::A64::Jit> ArmDynarmic64::MakeJit(Common::PageTable* page_table, |
| 249 | std::size_t address_space_bits) const { | 226 | std::size_t address_space_bits) const { |
| 250 | Dynarmic::A64::UserConfig config; | 227 | Dynarmic::A64::UserConfig config; |
| 251 | 228 | ||
| 252 | // Callbacks | 229 | // Callbacks |
| 253 | config.callbacks = cb.get(); | 230 | config.callbacks = m_cb.get(); |
| 254 | 231 | ||
| 255 | // Memory | 232 | // Memory |
| 256 | if (page_table) { | 233 | if (page_table) { |
| @@ -271,12 +248,12 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* | |||
| 271 | } | 248 | } |
| 272 | 249 | ||
| 273 | // Multi-process state | 250 | // Multi-process state |
| 274 | config.processor_id = core_index; | 251 | config.processor_id = m_core_index; |
| 275 | config.global_monitor = &exclusive_monitor.monitor; | 252 | config.global_monitor = &m_exclusive_monitor.monitor; |
| 276 | 253 | ||
| 277 | // System registers | 254 | // System registers |
| 278 | config.tpidrro_el0 = &cb->tpidrro_el0; | 255 | config.tpidrro_el0 = &m_cb->m_tpidrro_el0; |
| 279 | config.tpidr_el0 = &cb->tpidr_el0; | 256 | config.tpidr_el0 = &m_cb->m_tpidr_el0; |
| 280 | config.dczid_el0 = 4; | 257 | config.dczid_el0 = 4; |
| 281 | config.ctr_el0 = 0x8444c004; | 258 | config.ctr_el0 = 0x8444c004; |
| 282 | config.cntfrq_el0 = Hardware::CNTFREQ; | 259 | config.cntfrq_el0 = Hardware::CNTFREQ; |
| @@ -285,8 +262,8 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* | |||
| 285 | config.define_unpredictable_behaviour = true; | 262 | config.define_unpredictable_behaviour = true; |
| 286 | 263 | ||
| 287 | // Timing | 264 | // Timing |
| 288 | config.wall_clock_cntpct = uses_wall_clock; | 265 | config.wall_clock_cntpct = m_uses_wall_clock; |
| 289 | config.enable_cycle_counting = true; | 266 | config.enable_cycle_counting = !m_uses_wall_clock; |
| 290 | 267 | ||
| 291 | // Code cache size | 268 | // Code cache size |
| 292 | #ifdef ARCHITECTURE_arm64 | 269 | #ifdef ARCHITECTURE_arm64 |
| @@ -296,7 +273,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* | |||
| 296 | #endif | 273 | #endif |
| 297 | 274 | ||
| 298 | // Allow memory fault handling to work | 275 | // Allow memory fault handling to work |
| 299 | if (system.DebuggerEnabled()) { | 276 | if (m_system.DebuggerEnabled()) { |
| 300 | config.check_halt_on_memory_access = true; | 277 | config.check_halt_on_memory_access = true; |
| 301 | } | 278 | } |
| 302 | 279 | ||
| @@ -384,147 +361,112 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* | |||
| 384 | return std::make_shared<Dynarmic::A64::Jit>(config); | 361 | return std::make_shared<Dynarmic::A64::Jit>(config); |
| 385 | } | 362 | } |
| 386 | 363 | ||
| 387 | HaltReason ARM_Dynarmic_64::RunJit() { | 364 | HaltReason ArmDynarmic64::RunThread(Kernel::KThread* thread) { |
| 388 | return TranslateHaltReason(jit.load()->Run()); | 365 | m_jit->ClearExclusiveState(); |
| 389 | } | 366 | return TranslateHaltReason(m_jit->Run()); |
| 390 | |||
| 391 | HaltReason ARM_Dynarmic_64::StepJit() { | ||
| 392 | return TranslateHaltReason(jit.load()->Step()); | ||
| 393 | } | ||
| 394 | |||
| 395 | u32 ARM_Dynarmic_64::GetSvcNumber() const { | ||
| 396 | return svc_swi; | ||
| 397 | } | ||
| 398 | |||
| 399 | const Kernel::DebugWatchpoint* ARM_Dynarmic_64::HaltedWatchpoint() const { | ||
| 400 | return halted_watchpoint; | ||
| 401 | } | ||
| 402 | |||
| 403 | void ARM_Dynarmic_64::RewindBreakpointInstruction() { | ||
| 404 | LoadContext(breakpoint_context); | ||
| 405 | } | ||
| 406 | |||
| 407 | ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, bool uses_wall_clock_, | ||
| 408 | DynarmicExclusiveMonitor& exclusive_monitor_, | ||
| 409 | std::size_t core_index_) | ||
| 410 | : ARM_Interface{system_, uses_wall_clock_}, | ||
| 411 | cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index_}, | ||
| 412 | exclusive_monitor{exclusive_monitor_}, null_jit{MakeJit(nullptr, 48)}, jit{null_jit.get()} {} | ||
| 413 | |||
| 414 | ARM_Dynarmic_64::~ARM_Dynarmic_64() = default; | ||
| 415 | |||
| 416 | void ARM_Dynarmic_64::SetPC(u64 pc) { | ||
| 417 | jit.load()->SetPC(pc); | ||
| 418 | } | ||
| 419 | |||
| 420 | u64 ARM_Dynarmic_64::GetPC() const { | ||
| 421 | return jit.load()->GetPC(); | ||
| 422 | } | 367 | } |
| 423 | 368 | ||
| 424 | u64 ARM_Dynarmic_64::GetSP() const { | 369 | HaltReason ArmDynarmic64::StepThread(Kernel::KThread* thread) { |
| 425 | return jit.load()->GetSP(); | 370 | m_jit->ClearExclusiveState(); |
| 371 | return TranslateHaltReason(m_jit->Step()); | ||
| 426 | } | 372 | } |
| 427 | 373 | ||
| 428 | u64 ARM_Dynarmic_64::GetReg(int index) const { | 374 | u32 ArmDynarmic64::GetSvcNumber() const { |
| 429 | return jit.load()->GetRegister(index); | 375 | return m_svc; |
| 430 | } | 376 | } |
| 431 | 377 | ||
| 432 | void ARM_Dynarmic_64::SetReg(int index, u64 value) { | 378 | void ArmDynarmic64::GetSvcArguments(std::span<uint64_t, 8> args) const { |
| 433 | jit.load()->SetRegister(index, value); | 379 | Dynarmic::A64::Jit& j = *m_jit; |
| 434 | } | ||
| 435 | 380 | ||
| 436 | u128 ARM_Dynarmic_64::GetVectorReg(int index) const { | 381 | for (size_t i = 0; i < 8; i++) { |
| 437 | return jit.load()->GetVector(index); | 382 | args[i] = j.GetRegister(i); |
| 383 | } | ||
| 438 | } | 384 | } |
| 439 | 385 | ||
| 440 | void ARM_Dynarmic_64::SetVectorReg(int index, u128 value) { | 386 | void ArmDynarmic64::SetSvcArguments(std::span<const uint64_t, 8> args) { |
| 441 | jit.load()->SetVector(index, value); | 387 | Dynarmic::A64::Jit& j = *m_jit; |
| 442 | } | ||
| 443 | 388 | ||
| 444 | u32 ARM_Dynarmic_64::GetPSTATE() const { | 389 | for (size_t i = 0; i < 8; i++) { |
| 445 | return jit.load()->GetPstate(); | 390 | j.SetRegister(i, args[i]); |
| 391 | } | ||
| 446 | } | 392 | } |
| 447 | 393 | ||
| 448 | void ARM_Dynarmic_64::SetPSTATE(u32 pstate) { | 394 | const Kernel::DebugWatchpoint* ArmDynarmic64::HaltedWatchpoint() const { |
| 449 | jit.load()->SetPstate(pstate); | 395 | return m_halted_watchpoint; |
| 450 | } | 396 | } |
| 451 | 397 | ||
| 452 | u64 ARM_Dynarmic_64::GetTlsAddress() const { | 398 | void ArmDynarmic64::RewindBreakpointInstruction() { |
| 453 | return cb->tpidrro_el0; | 399 | this->SetContext(m_breakpoint_context); |
| 454 | } | 400 | } |
| 455 | 401 | ||
| 456 | void ARM_Dynarmic_64::SetTlsAddress(u64 address) { | 402 | ArmDynarmic64::ArmDynarmic64(System& system, bool uses_wall_clock, const Kernel::KProcess* process, |
| 457 | cb->tpidrro_el0 = address; | 403 | DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index) |
| 404 | : ArmInterface{uses_wall_clock}, m_system{system}, m_exclusive_monitor{exclusive_monitor}, | ||
| 405 | m_cb(std::make_unique<DynarmicCallbacks64>(*this, process)), m_core_index{core_index} { | ||
| 406 | auto& page_table = process->GetPageTable().GetBasePageTable(); | ||
| 407 | auto& page_table_impl = page_table.GetImpl(); | ||
| 408 | m_jit = MakeJit(&page_table_impl, page_table.GetAddressSpaceWidth()); | ||
| 458 | } | 409 | } |
| 459 | 410 | ||
| 460 | u64 ARM_Dynarmic_64::GetTPIDR_EL0() const { | 411 | ArmDynarmic64::~ArmDynarmic64() = default; |
| 461 | return cb->tpidr_el0; | ||
| 462 | } | ||
| 463 | 412 | ||
| 464 | void ARM_Dynarmic_64::SetTPIDR_EL0(u64 value) { | 413 | void ArmDynarmic64::SetTpidrroEl0(u64 value) { |
| 465 | cb->tpidr_el0 = value; | 414 | m_cb->m_tpidrro_el0 = value; |
| 466 | } | 415 | } |
| 467 | 416 | ||
| 468 | void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) const { | 417 | void ArmDynarmic64::GetContext(Kernel::Svc::ThreadContext& ctx) const { |
| 469 | Dynarmic::A64::Jit* j = jit.load(); | 418 | Dynarmic::A64::Jit& j = *m_jit; |
| 470 | ctx.cpu_registers = j->GetRegisters(); | 419 | auto gpr = j.GetRegisters(); |
| 471 | ctx.sp = j->GetSP(); | 420 | auto fpr = j.GetVectors(); |
| 472 | ctx.pc = j->GetPC(); | ||
| 473 | ctx.pstate = j->GetPstate(); | ||
| 474 | ctx.vector_registers = j->GetVectors(); | ||
| 475 | ctx.fpcr = j->GetFpcr(); | ||
| 476 | ctx.fpsr = j->GetFpsr(); | ||
| 477 | ctx.tpidr = cb->tpidr_el0; | ||
| 478 | } | ||
| 479 | 421 | ||
| 480 | void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) { | 422 | // TODO: this is inconvenient |
| 481 | Dynarmic::A64::Jit* j = jit.load(); | 423 | for (size_t i = 0; i < 29; i++) { |
| 482 | j->SetRegisters(ctx.cpu_registers); | 424 | ctx.r[i] = gpr[i]; |
| 483 | j->SetSP(ctx.sp); | 425 | } |
| 484 | j->SetPC(ctx.pc); | 426 | ctx.fp = gpr[29]; |
| 485 | j->SetPstate(ctx.pstate); | 427 | ctx.lr = gpr[30]; |
| 486 | j->SetVectors(ctx.vector_registers); | 428 | |
| 487 | j->SetFpcr(ctx.fpcr); | 429 | ctx.sp = j.GetSP(); |
| 488 | j->SetFpsr(ctx.fpsr); | 430 | ctx.pc = j.GetPC(); |
| 489 | SetTPIDR_EL0(ctx.tpidr); | 431 | ctx.pstate = j.GetPstate(); |
| 432 | ctx.v = fpr; | ||
| 433 | ctx.fpcr = j.GetFpcr(); | ||
| 434 | ctx.fpsr = j.GetFpsr(); | ||
| 435 | ctx.tpidr = m_cb->m_tpidr_el0; | ||
| 490 | } | 436 | } |
| 491 | 437 | ||
| 492 | void ARM_Dynarmic_64::SignalInterrupt() { | 438 | void ArmDynarmic64::SetContext(const Kernel::Svc::ThreadContext& ctx) { |
| 493 | jit.load()->HaltExecution(BreakLoop); | 439 | Dynarmic::A64::Jit& j = *m_jit; |
| 494 | } | ||
| 495 | 440 | ||
| 496 | void ARM_Dynarmic_64::ClearInterrupt() { | 441 | // TODO: this is inconvenient |
| 497 | jit.load()->ClearHalt(BreakLoop); | 442 | std::array<u64, 31> gpr; |
| 498 | } | ||
| 499 | 443 | ||
| 500 | void ARM_Dynarmic_64::ClearInstructionCache() { | 444 | for (size_t i = 0; i < 29; i++) { |
| 501 | jit.load()->ClearCache(); | 445 | gpr[i] = ctx.r[i]; |
| 446 | } | ||
| 447 | gpr[29] = ctx.fp; | ||
| 448 | gpr[30] = ctx.lr; | ||
| 449 | |||
| 450 | j.SetRegisters(gpr); | ||
| 451 | j.SetSP(ctx.sp); | ||
| 452 | j.SetPC(ctx.pc); | ||
| 453 | j.SetPstate(ctx.pstate); | ||
| 454 | j.SetVectors(ctx.v); | ||
| 455 | j.SetFpcr(ctx.fpcr); | ||
| 456 | j.SetFpsr(ctx.fpsr); | ||
| 457 | m_cb->m_tpidr_el0 = ctx.tpidr; | ||
| 502 | } | 458 | } |
| 503 | 459 | ||
| 504 | void ARM_Dynarmic_64::InvalidateCacheRange(u64 addr, std::size_t size) { | 460 | void ArmDynarmic64::SignalInterrupt(Kernel::KThread* thread) { |
| 505 | jit.load()->InvalidateCacheRange(addr, size); | 461 | m_jit->HaltExecution(BreakLoop); |
| 506 | } | 462 | } |
| 507 | 463 | ||
| 508 | void ARM_Dynarmic_64::ClearExclusiveState() { | 464 | void ArmDynarmic64::ClearInstructionCache() { |
| 509 | jit.load()->ClearExclusiveState(); | 465 | m_jit->ClearCache(); |
| 510 | } | 466 | } |
| 511 | 467 | ||
| 512 | void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table, | 468 | void ArmDynarmic64::InvalidateCacheRange(u64 addr, std::size_t size) { |
| 513 | std::size_t new_address_space_size_in_bits) { | 469 | m_jit->InvalidateCacheRange(addr, size); |
| 514 | ThreadContext64 ctx{}; | ||
| 515 | SaveContext(ctx); | ||
| 516 | |||
| 517 | auto key = std::make_pair(&page_table, new_address_space_size_in_bits); | ||
| 518 | auto iter = jit_cache.find(key); | ||
| 519 | if (iter != jit_cache.end()) { | ||
| 520 | jit.store(iter->second.get()); | ||
| 521 | LoadContext(ctx); | ||
| 522 | return; | ||
| 523 | } | ||
| 524 | std::shared_ptr new_jit = MakeJit(&page_table, new_address_space_size_in_bits); | ||
| 525 | jit.store(new_jit.get()); | ||
| 526 | LoadContext(ctx); | ||
| 527 | jit_cache.emplace(key, std::move(new_jit)); | ||
| 528 | } | 470 | } |
| 529 | 471 | ||
| 530 | } // namespace Core | 472 | } // namespace Core |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index 2b88a08e2..4f3dd026f 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h | |||
| @@ -23,76 +23,55 @@ class DynarmicCallbacks64; | |||
| 23 | class DynarmicExclusiveMonitor; | 23 | class DynarmicExclusiveMonitor; |
| 24 | class System; | 24 | class System; |
| 25 | 25 | ||
| 26 | class ARM_Dynarmic_64 final : public ARM_Interface { | 26 | class ArmDynarmic64 final : public ArmInterface { |
| 27 | public: | 27 | public: |
| 28 | ARM_Dynarmic_64(System& system_, bool uses_wall_clock_, | 28 | ArmDynarmic64(System& system, bool uses_wall_clock, const Kernel::KProcess* process, |
| 29 | DynarmicExclusiveMonitor& exclusive_monitor_, std::size_t core_index_); | 29 | DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index); |
| 30 | ~ARM_Dynarmic_64() override; | 30 | ~ArmDynarmic64() override; |
| 31 | |||
| 32 | void SetPC(u64 pc) override; | ||
| 33 | u64 GetPC() const override; | ||
| 34 | u64 GetSP() const override; | ||
| 35 | u64 GetReg(int index) const override; | ||
| 36 | void SetReg(int index, u64 value) override; | ||
| 37 | u128 GetVectorReg(int index) const override; | ||
| 38 | void SetVectorReg(int index, u128 value) override; | ||
| 39 | u32 GetPSTATE() const override; | ||
| 40 | void SetPSTATE(u32 pstate) override; | ||
| 41 | u64 GetTlsAddress() const override; | ||
| 42 | void SetTlsAddress(u64 address) override; | ||
| 43 | void SetTPIDR_EL0(u64 value) override; | ||
| 44 | u64 GetTPIDR_EL0() const override; | ||
| 45 | 31 | ||
| 46 | Architecture GetArchitecture() const override { | 32 | Architecture GetArchitecture() const override { |
| 47 | return Architecture::Aarch64; | 33 | return Architecture::AArch64; |
| 48 | } | 34 | } |
| 49 | void SaveContext(ThreadContext32& ctx) const override {} | ||
| 50 | void SaveContext(ThreadContext64& ctx) const override; | ||
| 51 | void LoadContext(const ThreadContext32& ctx) override {} | ||
| 52 | void LoadContext(const ThreadContext64& ctx) override; | ||
| 53 | 35 | ||
| 54 | void SignalInterrupt() override; | 36 | HaltReason RunThread(Kernel::KThread* thread) override; |
| 55 | void ClearInterrupt() override; | 37 | HaltReason StepThread(Kernel::KThread* thread) override; |
| 56 | void ClearExclusiveState() override; | ||
| 57 | 38 | ||
| 39 | void GetContext(Kernel::Svc::ThreadContext& ctx) const override; | ||
| 40 | void SetContext(const Kernel::Svc::ThreadContext& ctx) override; | ||
| 41 | void SetTpidrroEl0(u64 value) override; | ||
| 42 | |||
| 43 | void GetSvcArguments(std::span<uint64_t, 8> args) const override; | ||
| 44 | void SetSvcArguments(std::span<const uint64_t, 8> args) override; | ||
| 45 | u32 GetSvcNumber() const override; | ||
| 46 | |||
| 47 | void SignalInterrupt(Kernel::KThread* thread) override; | ||
| 58 | void ClearInstructionCache() override; | 48 | void ClearInstructionCache() override; |
| 59 | void InvalidateCacheRange(u64 addr, std::size_t size) override; | 49 | void InvalidateCacheRange(u64 addr, std::size_t size) override; |
| 60 | void PageTableChanged(Common::PageTable& new_page_table, | ||
| 61 | std::size_t new_address_space_size_in_bits) override; | ||
| 62 | 50 | ||
| 63 | protected: | 51 | protected: |
| 64 | HaltReason RunJit() override; | ||
| 65 | HaltReason StepJit() override; | ||
| 66 | u32 GetSvcNumber() const override; | ||
| 67 | const Kernel::DebugWatchpoint* HaltedWatchpoint() const override; | 52 | const Kernel::DebugWatchpoint* HaltedWatchpoint() const override; |
| 68 | void RewindBreakpointInstruction() override; | 53 | void RewindBreakpointInstruction() override; |
| 69 | 54 | ||
| 70 | private: | 55 | private: |
| 71 | std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table, | 56 | System& m_system; |
| 72 | std::size_t address_space_bits) const; | 57 | DynarmicExclusiveMonitor& m_exclusive_monitor; |
| 73 | |||
| 74 | using JitCacheKey = std::pair<Common::PageTable*, std::size_t>; | ||
| 75 | using JitCacheType = | ||
| 76 | std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A64::Jit>, Common::PairHash>; | ||
| 77 | 58 | ||
| 59 | private: | ||
| 78 | friend class DynarmicCallbacks64; | 60 | friend class DynarmicCallbacks64; |
| 79 | std::unique_ptr<DynarmicCallbacks64> cb; | ||
| 80 | JitCacheType jit_cache; | ||
| 81 | |||
| 82 | std::size_t core_index; | ||
| 83 | DynarmicExclusiveMonitor& exclusive_monitor; | ||
| 84 | 61 | ||
| 85 | std::shared_ptr<Dynarmic::A64::Jit> null_jit; | 62 | std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table, |
| 63 | std::size_t address_space_bits) const; | ||
| 64 | std::unique_ptr<DynarmicCallbacks64> m_cb{}; | ||
| 65 | std::size_t m_core_index{}; | ||
| 86 | 66 | ||
| 87 | // A raw pointer here is fine; we never delete Jit instances. | 67 | std::shared_ptr<Dynarmic::A64::Jit> m_jit{}; |
| 88 | std::atomic<Dynarmic::A64::Jit*> jit; | ||
| 89 | 68 | ||
| 90 | // SVC callback | 69 | // SVC callback |
| 91 | u32 svc_swi{}; | 70 | u32 m_svc{}; |
| 92 | 71 | ||
| 93 | // Breakpoint info | 72 | // Watchpoint info |
| 94 | const Kernel::DebugWatchpoint* halted_watchpoint; | 73 | const Kernel::DebugWatchpoint* m_halted_watchpoint{}; |
| 95 | ThreadContext64 breakpoint_context; | 74 | Kernel::Svc::ThreadContext m_breakpoint_context{}; |
| 96 | }; | 75 | }; |
| 97 | 76 | ||
| 98 | } // namespace Core | 77 | } // namespace Core |
diff --git a/src/core/arm/dynarmic/dynarmic_cp15.cpp b/src/core/arm/dynarmic/dynarmic_cp15.cpp index 92c548db0..f3eee0d42 100644 --- a/src/core/arm/dynarmic/dynarmic_cp15.cpp +++ b/src/core/arm/dynarmic/dynarmic_cp15.cpp | |||
| @@ -124,8 +124,8 @@ CallbackOrAccessTwoWords DynarmicCP15::CompileGetTwoWords(bool two, unsigned opc | |||
| 124 | if (!two && opc == 0 && CRm == CoprocReg::C14) { | 124 | if (!two && opc == 0 && CRm == CoprocReg::C14) { |
| 125 | // CNTPCT | 125 | // CNTPCT |
| 126 | const auto callback = [](void* arg, u32, u32) -> u64 { | 126 | const auto callback = [](void* arg, u32, u32) -> u64 { |
| 127 | const auto& parent_arg = *static_cast<ARM_Dynarmic_32*>(arg); | 127 | const auto& parent_arg = *static_cast<ArmDynarmic32*>(arg); |
| 128 | return parent_arg.system.CoreTiming().GetClockTicks(); | 128 | return parent_arg.m_system.CoreTiming().GetClockTicks(); |
| 129 | }; | 129 | }; |
| 130 | return Callback{callback, &parent}; | 130 | return Callback{callback, &parent}; |
| 131 | } | 131 | } |
diff --git a/src/core/arm/dynarmic/dynarmic_cp15.h b/src/core/arm/dynarmic/dynarmic_cp15.h index d90b3e568..f3d96b0d8 100644 --- a/src/core/arm/dynarmic/dynarmic_cp15.h +++ b/src/core/arm/dynarmic/dynarmic_cp15.h | |||
| @@ -10,13 +10,13 @@ | |||
| 10 | 10 | ||
| 11 | namespace Core { | 11 | namespace Core { |
| 12 | 12 | ||
| 13 | class ARM_Dynarmic_32; | 13 | class ArmDynarmic32; |
| 14 | 14 | ||
| 15 | class DynarmicCP15 final : public Dynarmic::A32::Coprocessor { | 15 | class DynarmicCP15 final : public Dynarmic::A32::Coprocessor { |
| 16 | public: | 16 | public: |
| 17 | using CoprocReg = Dynarmic::A32::CoprocReg; | 17 | using CoprocReg = Dynarmic::A32::CoprocReg; |
| 18 | 18 | ||
| 19 | explicit DynarmicCP15(ARM_Dynarmic_32& parent_) : parent{parent_} {} | 19 | explicit DynarmicCP15(ArmDynarmic32& parent_) : parent{parent_} {} |
| 20 | 20 | ||
| 21 | std::optional<Callback> CompileInternalOperation(bool two, unsigned opc1, CoprocReg CRd, | 21 | std::optional<Callback> CompileInternalOperation(bool two, unsigned opc1, CoprocReg CRd, |
| 22 | CoprocReg CRn, CoprocReg CRm, | 22 | CoprocReg CRn, CoprocReg CRm, |
| @@ -32,11 +32,11 @@ public: | |||
| 32 | std::optional<Callback> CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd, | 32 | std::optional<Callback> CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd, |
| 33 | std::optional<u8> option) override; | 33 | std::optional<u8> option) override; |
| 34 | 34 | ||
| 35 | ARM_Dynarmic_32& parent; | 35 | ArmDynarmic32& parent; |
| 36 | u32 uprw = 0; | 36 | u32 uprw = 0; |
| 37 | u32 uro = 0; | 37 | u32 uro = 0; |
| 38 | 38 | ||
| 39 | friend class ARM_Dynarmic_32; | 39 | friend class ArmDynarmic32; |
| 40 | }; | 40 | }; |
| 41 | 41 | ||
| 42 | } // namespace Core | 42 | } // namespace Core |
diff --git a/src/core/arm/dynarmic/dynarmic_exclusive_monitor.h b/src/core/arm/dynarmic/dynarmic_exclusive_monitor.h index fbfcd8d95..c4f22ec89 100644 --- a/src/core/arm/dynarmic/dynarmic_exclusive_monitor.h +++ b/src/core/arm/dynarmic/dynarmic_exclusive_monitor.h | |||
| @@ -14,8 +14,8 @@ class Memory; | |||
| 14 | 14 | ||
| 15 | namespace Core { | 15 | namespace Core { |
| 16 | 16 | ||
| 17 | class ARM_Dynarmic_32; | 17 | class ArmDynarmic32; |
| 18 | class ARM_Dynarmic_64; | 18 | class ArmDynarmic64; |
| 19 | 19 | ||
| 20 | class DynarmicExclusiveMonitor final : public ExclusiveMonitor { | 20 | class DynarmicExclusiveMonitor final : public ExclusiveMonitor { |
| 21 | public: | 21 | public: |
| @@ -36,8 +36,8 @@ public: | |||
| 36 | bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) override; | 36 | bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) override; |
| 37 | 37 | ||
| 38 | private: | 38 | private: |
| 39 | friend class ARM_Dynarmic_32; | 39 | friend class ArmDynarmic32; |
| 40 | friend class ARM_Dynarmic_64; | 40 | friend class ArmDynarmic64; |
| 41 | Dynarmic::ExclusiveMonitor monitor; | 41 | Dynarmic::ExclusiveMonitor monitor; |
| 42 | Core::Memory::Memory& memory; | 42 | Core::Memory::Memory& memory; |
| 43 | }; | 43 | }; |