diff options
| author | 2022-06-06 12:56:01 -0400 | |
|---|---|---|
| committer | 2022-06-16 13:18:07 -0400 | |
| commit | 208ed712f42cfd277405a22663197dc1c5e84cfe (patch) | |
| tree | 56c1a3cbddf392d700e817cd4093564e3f096013 /src/core/arm | |
| parent | Merge pull request #8457 from liamwhite/kprocess-suspend (diff) | |
| download | yuzu-208ed712f42cfd277405a22663197dc1c5e84cfe.tar.gz yuzu-208ed712f42cfd277405a22663197dc1c5e84cfe.tar.xz yuzu-208ed712f42cfd277405a22663197dc1c5e84cfe.zip | |
core/debugger: memory breakpoint support
Diffstat (limited to 'src/core/arm')
| -rw-r--r-- | src/core/arm/arm_interface.cpp | 41 | ||||
| -rw-r--r-- | src/core/arm/arm_interface.h | 13 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_32.cpp | 69 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_32.h | 6 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_64.cpp | 79 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_64.h | 6 |
6 files changed, 187 insertions, 27 deletions
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index 9a285dfc6..6425e131f 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp | |||
| @@ -121,8 +121,15 @@ void ARM_Interface::Run() { | |||
| 121 | 121 | ||
| 122 | // Notify the debugger and go to sleep if a breakpoint was hit. | 122 | // Notify the debugger and go to sleep if a breakpoint was hit. |
| 123 | if (Has(hr, breakpoint)) { | 123 | if (Has(hr, breakpoint)) { |
| 124 | RewindBreakpointInstruction(); | ||
| 124 | system.GetDebugger().NotifyThreadStopped(current_thread); | 125 | system.GetDebugger().NotifyThreadStopped(current_thread); |
| 125 | current_thread->RequestSuspend(Kernel::SuspendType::Debug); | 126 | current_thread->RequestSuspend(SuspendType::Debug); |
| 127 | break; | ||
| 128 | } | ||
| 129 | if (Has(hr, watchpoint)) { | ||
| 130 | RewindBreakpointInstruction(); | ||
| 131 | system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint()); | ||
| 132 | current_thread->RequestSuspend(SuspendType::Debug); | ||
| 126 | break; | 133 | break; |
| 127 | } | 134 | } |
| 128 | 135 | ||
| @@ -136,4 +143,36 @@ void ARM_Interface::Run() { | |||
| 136 | } | 143 | } |
| 137 | } | 144 | } |
| 138 | 145 | ||
| 146 | void ARM_Interface::LoadWatchpointArray(const WatchpointArray& wp) { | ||
| 147 | watchpoints = ℘ | ||
| 148 | } | ||
| 149 | |||
| 150 | const Kernel::DebugWatchpoint* ARM_Interface::MatchingWatchpoint( | ||
| 151 | VAddr addr, u64 size, Kernel::DebugWatchpointType access_type) const { | ||
| 152 | if (!watchpoints) { | ||
| 153 | return nullptr; | ||
| 154 | } | ||
| 155 | |||
| 156 | const VAddr start_address{addr}; | ||
| 157 | const VAddr end_address{addr + size}; | ||
| 158 | |||
| 159 | for (size_t i = 0; i < Core::Hardware::NUM_WATCHPOINTS; i++) { | ||
| 160 | const auto& watch{(*watchpoints)[i]}; | ||
| 161 | |||
| 162 | if (end_address <= watch.start_address) { | ||
| 163 | continue; | ||
| 164 | } | ||
| 165 | if (start_address >= watch.end_address) { | ||
| 166 | continue; | ||
| 167 | } | ||
| 168 | if ((access_type & watch.type) == Kernel::DebugWatchpointType::None) { | ||
| 169 | continue; | ||
| 170 | } | ||
| 171 | |||
| 172 | return &watch; | ||
| 173 | } | ||
| 174 | |||
| 175 | return nullptr; | ||
| 176 | } | ||
| 177 | |||
| 139 | } // namespace Core | 178 | } // namespace Core |
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 66f6107e9..4e431e27a 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <span> | ||
| 8 | #include <vector> | 9 | #include <vector> |
| 9 | 10 | ||
| 10 | #include <dynarmic/interface/halt_reason.h> | 11 | #include <dynarmic/interface/halt_reason.h> |
| @@ -19,13 +20,16 @@ struct PageTable; | |||
| 19 | 20 | ||
| 20 | namespace Kernel { | 21 | namespace Kernel { |
| 21 | enum class VMAPermission : u8; | 22 | enum class VMAPermission : u8; |
| 22 | } | 23 | enum class DebugWatchpointType : u8; |
| 24 | struct DebugWatchpoint; | ||
| 25 | } // namespace Kernel | ||
| 23 | 26 | ||
| 24 | namespace Core { | 27 | namespace Core { |
| 25 | class System; | 28 | class System; |
| 26 | class CPUInterruptHandler; | 29 | class CPUInterruptHandler; |
| 27 | 30 | ||
| 28 | using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>; | 31 | using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>; |
| 32 | using WatchpointArray = std::array<Kernel::DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>; | ||
| 29 | 33 | ||
| 30 | /// Generic ARMv8 CPU interface | 34 | /// Generic ARMv8 CPU interface |
| 31 | class ARM_Interface { | 35 | class ARM_Interface { |
| @@ -170,6 +174,7 @@ public: | |||
| 170 | virtual void SaveContext(ThreadContext64& ctx) = 0; | 174 | virtual void SaveContext(ThreadContext64& ctx) = 0; |
| 171 | virtual void LoadContext(const ThreadContext32& ctx) = 0; | 175 | virtual void LoadContext(const ThreadContext32& ctx) = 0; |
| 172 | virtual void LoadContext(const ThreadContext64& ctx) = 0; | 176 | virtual void LoadContext(const ThreadContext64& ctx) = 0; |
| 177 | void LoadWatchpointArray(const WatchpointArray& wp); | ||
| 173 | 178 | ||
| 174 | /// Clears the exclusive monitor's state. | 179 | /// Clears the exclusive monitor's state. |
| 175 | virtual void ClearExclusiveState() = 0; | 180 | virtual void ClearExclusiveState() = 0; |
| @@ -198,18 +203,24 @@ public: | |||
| 198 | static constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2; | 203 | static constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2; |
| 199 | static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3; | 204 | static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3; |
| 200 | static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4; | 205 | static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4; |
| 206 | static constexpr Dynarmic::HaltReason watchpoint = Dynarmic::HaltReason::UserDefined5; | ||
| 201 | 207 | ||
| 202 | protected: | 208 | protected: |
| 203 | /// System context that this ARM interface is running under. | 209 | /// System context that this ARM interface is running under. |
| 204 | System& system; | 210 | System& system; |
| 205 | CPUInterrupts& interrupt_handlers; | 211 | CPUInterrupts& interrupt_handlers; |
| 212 | const WatchpointArray* watchpoints; | ||
| 206 | bool uses_wall_clock; | 213 | bool uses_wall_clock; |
| 207 | 214 | ||
| 208 | static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out); | 215 | static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out); |
| 216 | const Kernel::DebugWatchpoint* MatchingWatchpoint( | ||
| 217 | VAddr addr, u64 size, Kernel::DebugWatchpointType access_type) const; | ||
| 209 | 218 | ||
| 210 | virtual Dynarmic::HaltReason RunJit() = 0; | 219 | virtual Dynarmic::HaltReason RunJit() = 0; |
| 211 | virtual Dynarmic::HaltReason StepJit() = 0; | 220 | virtual Dynarmic::HaltReason StepJit() = 0; |
| 212 | virtual u32 GetSvcNumber() const = 0; | 221 | virtual u32 GetSvcNumber() const = 0; |
| 222 | virtual const Kernel::DebugWatchpoint* HaltedWatchpoint() const = 0; | ||
| 223 | virtual void RewindBreakpointInstruction() = 0; | ||
| 213 | }; | 224 | }; |
| 214 | 225 | ||
| 215 | } // namespace Core | 226 | } // namespace Core |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 7c82d0b96..8c90c8be0 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp | |||
| @@ -29,45 +29,62 @@ using namespace Common::Literals; | |||
| 29 | class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { | 29 | class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { |
| 30 | public: | 30 | public: |
| 31 | explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_) | 31 | explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_) |
| 32 | : parent{parent_}, memory(parent.system.Memory()) {} | 32 | : parent{parent_}, |
| 33 | memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()} {} | ||
| 33 | 34 | ||
| 34 | u8 MemoryRead8(u32 vaddr) override { | 35 | u8 MemoryRead8(u32 vaddr) override { |
| 36 | CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read); | ||
| 35 | return memory.Read8(vaddr); | 37 | return memory.Read8(vaddr); |
| 36 | } | 38 | } |
| 37 | u16 MemoryRead16(u32 vaddr) override { | 39 | u16 MemoryRead16(u32 vaddr) override { |
| 40 | CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read); | ||
| 38 | return memory.Read16(vaddr); | 41 | return memory.Read16(vaddr); |
| 39 | } | 42 | } |
| 40 | u32 MemoryRead32(u32 vaddr) override { | 43 | u32 MemoryRead32(u32 vaddr) override { |
| 44 | CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read); | ||
| 41 | return memory.Read32(vaddr); | 45 | return memory.Read32(vaddr); |
| 42 | } | 46 | } |
| 43 | u64 MemoryRead64(u32 vaddr) override { | 47 | u64 MemoryRead64(u32 vaddr) override { |
| 48 | CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read); | ||
| 44 | return memory.Read64(vaddr); | 49 | return memory.Read64(vaddr); |
| 45 | } | 50 | } |
| 46 | 51 | ||
| 47 | void MemoryWrite8(u32 vaddr, u8 value) override { | 52 | void MemoryWrite8(u32 vaddr, u8 value) override { |
| 48 | memory.Write8(vaddr, value); | 53 | if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) { |
| 54 | memory.Write8(vaddr, value); | ||
| 55 | } | ||
| 49 | } | 56 | } |
| 50 | void MemoryWrite16(u32 vaddr, u16 value) override { | 57 | void MemoryWrite16(u32 vaddr, u16 value) override { |
| 51 | memory.Write16(vaddr, value); | 58 | if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) { |
| 59 | memory.Write16(vaddr, value); | ||
| 60 | } | ||
| 52 | } | 61 | } |
| 53 | void MemoryWrite32(u32 vaddr, u32 value) override { | 62 | void MemoryWrite32(u32 vaddr, u32 value) override { |
| 54 | memory.Write32(vaddr, value); | 63 | if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) { |
| 64 | memory.Write32(vaddr, value); | ||
| 65 | } | ||
| 55 | } | 66 | } |
| 56 | void MemoryWrite64(u32 vaddr, u64 value) override { | 67 | void MemoryWrite64(u32 vaddr, u64 value) override { |
| 57 | memory.Write64(vaddr, value); | 68 | if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) { |
| 69 | memory.Write64(vaddr, value); | ||
| 70 | } | ||
| 58 | } | 71 | } |
| 59 | 72 | ||
| 60 | bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override { | 73 | bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override { |
| 61 | return memory.WriteExclusive8(vaddr, value, expected); | 74 | return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) && |
| 75 | memory.WriteExclusive8(vaddr, value, expected); | ||
| 62 | } | 76 | } |
| 63 | bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override { | 77 | bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override { |
| 64 | return memory.WriteExclusive16(vaddr, value, expected); | 78 | return CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write) && |
| 79 | memory.WriteExclusive16(vaddr, value, expected); | ||
| 65 | } | 80 | } |
| 66 | bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override { | 81 | bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override { |
| 67 | return memory.WriteExclusive32(vaddr, value, expected); | 82 | return CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write) && |
| 83 | memory.WriteExclusive32(vaddr, value, expected); | ||
| 68 | } | 84 | } |
| 69 | bool MemoryWriteExclusive64(u32 vaddr, u64 value, u64 expected) override { | 85 | bool MemoryWriteExclusive64(u32 vaddr, u64 value, u64 expected) override { |
| 70 | return memory.WriteExclusive64(vaddr, value, expected); | 86 | return CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write) && |
| 87 | memory.WriteExclusive64(vaddr, value, expected); | ||
| 71 | } | 88 | } |
| 72 | 89 | ||
| 73 | void InterpreterFallback(u32 pc, std::size_t num_instructions) override { | 90 | void InterpreterFallback(u32 pc, std::size_t num_instructions) override { |
| @@ -77,8 +94,8 @@ public: | |||
| 77 | } | 94 | } |
| 78 | 95 | ||
| 79 | void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { | 96 | void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { |
| 80 | if (parent.system.DebuggerEnabled()) { | 97 | if (debugger_enabled) { |
| 81 | parent.jit.load()->Regs()[15] = pc; | 98 | parent.SaveContext(parent.breakpoint_context); |
| 82 | parent.jit.load()->HaltExecution(ARM_Interface::breakpoint); | 99 | parent.jit.load()->HaltExecution(ARM_Interface::breakpoint); |
| 83 | return; | 100 | return; |
| 84 | } | 101 | } |
| @@ -117,9 +134,26 @@ public: | |||
| 117 | return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); | 134 | return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); |
| 118 | } | 135 | } |
| 119 | 136 | ||
| 137 | bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) { | ||
| 138 | if (!debugger_enabled) { | ||
| 139 | return true; | ||
| 140 | } | ||
| 141 | |||
| 142 | const auto match{parent.MatchingWatchpoint(addr, size, type)}; | ||
| 143 | if (match) { | ||
| 144 | parent.SaveContext(parent.breakpoint_context); | ||
| 145 | parent.jit.load()->HaltExecution(ARM_Interface::watchpoint); | ||
| 146 | parent.halted_watchpoint = match; | ||
| 147 | return false; | ||
| 148 | } | ||
| 149 | |||
| 150 | return true; | ||
| 151 | } | ||
| 152 | |||
| 120 | ARM_Dynarmic_32& parent; | 153 | ARM_Dynarmic_32& parent; |
| 121 | Core::Memory::Memory& memory; | 154 | Core::Memory::Memory& memory; |
| 122 | std::size_t num_interpreted_instructions{}; | 155 | std::size_t num_interpreted_instructions{}; |
| 156 | bool debugger_enabled{}; | ||
| 123 | static constexpr u64 minimum_run_cycles = 1000U; | 157 | static constexpr u64 minimum_run_cycles = 1000U; |
| 124 | }; | 158 | }; |
| 125 | 159 | ||
| @@ -154,6 +188,11 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* | |||
| 154 | config.code_cache_size = 512_MiB; | 188 | config.code_cache_size = 512_MiB; |
| 155 | config.far_code_offset = 400_MiB; | 189 | config.far_code_offset = 400_MiB; |
| 156 | 190 | ||
| 191 | // Allow memory fault handling to work | ||
| 192 | if (system.DebuggerEnabled()) { | ||
| 193 | config.check_halt_on_memory_access = true; | ||
| 194 | } | ||
| 195 | |||
| 157 | // null_jit | 196 | // null_jit |
| 158 | if (!page_table) { | 197 | if (!page_table) { |
| 159 | // Don't waste too much memory on null_jit | 198 | // Don't waste too much memory on null_jit |
| @@ -248,6 +287,14 @@ u32 ARM_Dynarmic_32::GetSvcNumber() const { | |||
| 248 | return svc_swi; | 287 | return svc_swi; |
| 249 | } | 288 | } |
| 250 | 289 | ||
| 290 | const Kernel::DebugWatchpoint* ARM_Dynarmic_32::HaltedWatchpoint() const { | ||
| 291 | return halted_watchpoint; | ||
| 292 | } | ||
| 293 | |||
| 294 | void ARM_Dynarmic_32::RewindBreakpointInstruction() { | ||
| 295 | LoadContext(breakpoint_context); | ||
| 296 | } | ||
| 297 | |||
| 251 | ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_, | 298 | ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_, |
| 252 | bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_, | 299 | bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_, |
| 253 | std::size_t core_index_) | 300 | std::size_t core_index_) |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index 5b1d60005..fcbe24f0c 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h | |||
| @@ -72,6 +72,8 @@ protected: | |||
| 72 | Dynarmic::HaltReason RunJit() override; | 72 | Dynarmic::HaltReason RunJit() override; |
| 73 | Dynarmic::HaltReason StepJit() override; | 73 | Dynarmic::HaltReason StepJit() override; |
| 74 | u32 GetSvcNumber() const override; | 74 | u32 GetSvcNumber() const override; |
| 75 | const Kernel::DebugWatchpoint* HaltedWatchpoint() const override; | ||
| 76 | void RewindBreakpointInstruction() override; | ||
| 75 | 77 | ||
| 76 | private: | 78 | private: |
| 77 | std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const; | 79 | std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const; |
| @@ -98,6 +100,10 @@ private: | |||
| 98 | 100 | ||
| 99 | // SVC callback | 101 | // SVC callback |
| 100 | u32 svc_swi{}; | 102 | u32 svc_swi{}; |
| 103 | |||
| 104 | // Watchpoint info | ||
| 105 | const Kernel::DebugWatchpoint* halted_watchpoint; | ||
| 106 | ThreadContext32 breakpoint_context; | ||
| 101 | }; | 107 | }; |
| 102 | 108 | ||
| 103 | } // namespace Core | 109 | } // namespace Core |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index d4c67eafd..4370ca294 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp | |||
| @@ -29,55 +29,76 @@ using namespace Common::Literals; | |||
| 29 | class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { | 29 | class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { |
| 30 | public: | 30 | public: |
| 31 | explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_) | 31 | explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_) |
| 32 | : parent{parent_}, memory(parent.system.Memory()) {} | 32 | : parent{parent_}, |
| 33 | memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()} {} | ||
| 33 | 34 | ||
| 34 | u8 MemoryRead8(u64 vaddr) override { | 35 | u8 MemoryRead8(u64 vaddr) override { |
| 36 | CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read); | ||
| 35 | return memory.Read8(vaddr); | 37 | return memory.Read8(vaddr); |
| 36 | } | 38 | } |
| 37 | u16 MemoryRead16(u64 vaddr) override { | 39 | u16 MemoryRead16(u64 vaddr) override { |
| 40 | CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read); | ||
| 38 | return memory.Read16(vaddr); | 41 | return memory.Read16(vaddr); |
| 39 | } | 42 | } |
| 40 | u32 MemoryRead32(u64 vaddr) override { | 43 | u32 MemoryRead32(u64 vaddr) override { |
| 44 | CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read); | ||
| 41 | return memory.Read32(vaddr); | 45 | return memory.Read32(vaddr); |
| 42 | } | 46 | } |
| 43 | u64 MemoryRead64(u64 vaddr) override { | 47 | u64 MemoryRead64(u64 vaddr) override { |
| 48 | CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read); | ||
| 44 | return memory.Read64(vaddr); | 49 | return memory.Read64(vaddr); |
| 45 | } | 50 | } |
| 46 | Vector MemoryRead128(u64 vaddr) override { | 51 | Vector MemoryRead128(u64 vaddr) override { |
| 52 | CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read); | ||
| 47 | return {memory.Read64(vaddr), memory.Read64(vaddr + 8)}; | 53 | return {memory.Read64(vaddr), memory.Read64(vaddr + 8)}; |
| 48 | } | 54 | } |
| 49 | 55 | ||
| 50 | void MemoryWrite8(u64 vaddr, u8 value) override { | 56 | void MemoryWrite8(u64 vaddr, u8 value) override { |
| 51 | memory.Write8(vaddr, value); | 57 | if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) { |
| 58 | memory.Write8(vaddr, value); | ||
| 59 | } | ||
| 52 | } | 60 | } |
| 53 | void MemoryWrite16(u64 vaddr, u16 value) override { | 61 | void MemoryWrite16(u64 vaddr, u16 value) override { |
| 54 | memory.Write16(vaddr, value); | 62 | if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) { |
| 63 | memory.Write16(vaddr, value); | ||
| 64 | } | ||
| 55 | } | 65 | } |
| 56 | void MemoryWrite32(u64 vaddr, u32 value) override { | 66 | void MemoryWrite32(u64 vaddr, u32 value) override { |
| 57 | memory.Write32(vaddr, value); | 67 | if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) { |
| 68 | memory.Write32(vaddr, value); | ||
| 69 | } | ||
| 58 | } | 70 | } |
| 59 | void MemoryWrite64(u64 vaddr, u64 value) override { | 71 | void MemoryWrite64(u64 vaddr, u64 value) override { |
| 60 | memory.Write64(vaddr, value); | 72 | if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) { |
| 73 | memory.Write64(vaddr, value); | ||
| 74 | } | ||
| 61 | } | 75 | } |
| 62 | void MemoryWrite128(u64 vaddr, Vector value) override { | 76 | void MemoryWrite128(u64 vaddr, Vector value) override { |
| 63 | memory.Write64(vaddr, value[0]); | 77 | if (CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Write)) { |
| 64 | memory.Write64(vaddr + 8, value[1]); | 78 | memory.Write64(vaddr, value[0]); |
| 79 | memory.Write64(vaddr + 8, value[1]); | ||
| 80 | } | ||
| 65 | } | 81 | } |
| 66 | 82 | ||
| 67 | bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override { | 83 | bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override { |
| 68 | return memory.WriteExclusive8(vaddr, value, expected); | 84 | return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) && |
| 85 | memory.WriteExclusive8(vaddr, value, expected); | ||
| 69 | } | 86 | } |
| 70 | bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override { | 87 | bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override { |
| 71 | return memory.WriteExclusive16(vaddr, value, expected); | 88 | return CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write) && |
| 89 | memory.WriteExclusive16(vaddr, value, expected); | ||
| 72 | } | 90 | } |
| 73 | bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, std::uint32_t expected) override { | 91 | bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, std::uint32_t expected) override { |
| 74 | return memory.WriteExclusive32(vaddr, value, expected); | 92 | return CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write) && |
| 93 | memory.WriteExclusive32(vaddr, value, expected); | ||
| 75 | } | 94 | } |
| 76 | bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, std::uint64_t expected) override { | 95 | bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, std::uint64_t expected) override { |
| 77 | return memory.WriteExclusive64(vaddr, value, expected); | 96 | return CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write) && |
| 97 | memory.WriteExclusive64(vaddr, value, expected); | ||
| 78 | } | 98 | } |
| 79 | bool MemoryWriteExclusive128(u64 vaddr, Vector value, Vector expected) override { | 99 | bool MemoryWriteExclusive128(u64 vaddr, Vector value, Vector expected) override { |
| 80 | return memory.WriteExclusive128(vaddr, value, expected); | 100 | return CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Write) && |
| 101 | memory.WriteExclusive128(vaddr, value, expected); | ||
| 81 | } | 102 | } |
| 82 | 103 | ||
| 83 | void InterpreterFallback(u64 pc, std::size_t num_instructions) override { | 104 | void InterpreterFallback(u64 pc, std::size_t num_instructions) override { |
| @@ -118,8 +139,8 @@ public: | |||
| 118 | case Dynarmic::A64::Exception::Yield: | 139 | case Dynarmic::A64::Exception::Yield: |
| 119 | return; | 140 | return; |
| 120 | default: | 141 | default: |
| 121 | if (parent.system.DebuggerEnabled()) { | 142 | if (debugger_enabled) { |
| 122 | parent.jit.load()->SetPC(pc); | 143 | parent.SaveContext(parent.breakpoint_context); |
| 123 | parent.jit.load()->HaltExecution(ARM_Interface::breakpoint); | 144 | parent.jit.load()->HaltExecution(ARM_Interface::breakpoint); |
| 124 | return; | 145 | return; |
| 125 | } | 146 | } |
| @@ -160,10 +181,27 @@ public: | |||
| 160 | return parent.system.CoreTiming().GetClockTicks(); | 181 | return parent.system.CoreTiming().GetClockTicks(); |
| 161 | } | 182 | } |
| 162 | 183 | ||
| 184 | bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) { | ||
| 185 | if (!debugger_enabled) { | ||
| 186 | return true; | ||
| 187 | } | ||
| 188 | |||
| 189 | const auto match{parent.MatchingWatchpoint(addr, size, type)}; | ||
| 190 | if (match) { | ||
| 191 | parent.SaveContext(parent.breakpoint_context); | ||
| 192 | parent.jit.load()->HaltExecution(ARM_Interface::watchpoint); | ||
| 193 | parent.halted_watchpoint = match; | ||
| 194 | return false; | ||
| 195 | } | ||
| 196 | |||
| 197 | return true; | ||
| 198 | } | ||
| 199 | |||
| 163 | ARM_Dynarmic_64& parent; | 200 | ARM_Dynarmic_64& parent; |
| 164 | Core::Memory::Memory& memory; | 201 | Core::Memory::Memory& memory; |
| 165 | u64 tpidrro_el0 = 0; | 202 | u64 tpidrro_el0 = 0; |
| 166 | u64 tpidr_el0 = 0; | 203 | u64 tpidr_el0 = 0; |
| 204 | bool debugger_enabled{}; | ||
| 167 | static constexpr u64 minimum_run_cycles = 1000U; | 205 | static constexpr u64 minimum_run_cycles = 1000U; |
| 168 | }; | 206 | }; |
| 169 | 207 | ||
| @@ -214,6 +252,11 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* | |||
| 214 | config.code_cache_size = 512_MiB; | 252 | config.code_cache_size = 512_MiB; |
| 215 | config.far_code_offset = 400_MiB; | 253 | config.far_code_offset = 400_MiB; |
| 216 | 254 | ||
| 255 | // Allow memory fault handling to work | ||
| 256 | if (system.DebuggerEnabled()) { | ||
| 257 | config.check_halt_on_memory_access = true; | ||
| 258 | } | ||
| 259 | |||
| 217 | // null_jit | 260 | // null_jit |
| 218 | if (!page_table) { | 261 | if (!page_table) { |
| 219 | // Don't waste too much memory on null_jit | 262 | // Don't waste too much memory on null_jit |
| @@ -308,6 +351,14 @@ u32 ARM_Dynarmic_64::GetSvcNumber() const { | |||
| 308 | return svc_swi; | 351 | return svc_swi; |
| 309 | } | 352 | } |
| 310 | 353 | ||
| 354 | const Kernel::DebugWatchpoint* ARM_Dynarmic_64::HaltedWatchpoint() const { | ||
| 355 | return halted_watchpoint; | ||
| 356 | } | ||
| 357 | |||
| 358 | void ARM_Dynarmic_64::RewindBreakpointInstruction() { | ||
| 359 | LoadContext(breakpoint_context); | ||
| 360 | } | ||
| 361 | |||
| 311 | ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_, | 362 | ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_, |
| 312 | bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_, | 363 | bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_, |
| 313 | std::size_t core_index_) | 364 | std::size_t core_index_) |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index abfbc3c3f..71dbaac5e 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h | |||
| @@ -66,6 +66,8 @@ protected: | |||
| 66 | Dynarmic::HaltReason RunJit() override; | 66 | Dynarmic::HaltReason RunJit() override; |
| 67 | Dynarmic::HaltReason StepJit() override; | 67 | Dynarmic::HaltReason StepJit() override; |
| 68 | u32 GetSvcNumber() const override; | 68 | u32 GetSvcNumber() const override; |
| 69 | const Kernel::DebugWatchpoint* HaltedWatchpoint() const override; | ||
| 70 | void RewindBreakpointInstruction() override; | ||
| 69 | 71 | ||
| 70 | private: | 72 | private: |
| 71 | std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table, | 73 | std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table, |
| @@ -91,6 +93,10 @@ private: | |||
| 91 | 93 | ||
| 92 | // SVC callback | 94 | // SVC callback |
| 93 | u32 svc_swi{}; | 95 | u32 svc_swi{}; |
| 96 | |||
| 97 | // Breakpoint info | ||
| 98 | const Kernel::DebugWatchpoint* halted_watchpoint; | ||
| 99 | ThreadContext64 breakpoint_context; | ||
| 94 | }; | 100 | }; |
| 95 | 101 | ||
| 96 | } // namespace Core | 102 | } // namespace Core |