summaryrefslogtreecommitdiff
path: root/src/core/arm
diff options
context:
space:
mode:
authorGravatar Liam2022-06-06 12:56:01 -0400
committerGravatar Liam2022-06-16 13:18:07 -0400
commit208ed712f42cfd277405a22663197dc1c5e84cfe (patch)
tree56c1a3cbddf392d700e817cd4093564e3f096013 /src/core/arm
parentMerge pull request #8457 from liamwhite/kprocess-suspend (diff)
downloadyuzu-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.cpp41
-rw-r--r--src/core/arm/arm_interface.h13
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp69
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h6
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp79
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h6
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
146void ARM_Interface::LoadWatchpointArray(const WatchpointArray& wp) {
147 watchpoints = ℘
148}
149
150const 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
20namespace Kernel { 21namespace Kernel {
21enum class VMAPermission : u8; 22enum class VMAPermission : u8;
22} 23enum class DebugWatchpointType : u8;
24struct DebugWatchpoint;
25} // namespace Kernel
23 26
24namespace Core { 27namespace Core {
25class System; 28class System;
26class CPUInterruptHandler; 29class CPUInterruptHandler;
27 30
28using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>; 31using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>;
32using WatchpointArray = std::array<Kernel::DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>;
29 33
30/// Generic ARMv8 CPU interface 34/// Generic ARMv8 CPU interface
31class ARM_Interface { 35class 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
202protected: 208protected:
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;
29class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { 29class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
30public: 30public:
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
290const Kernel::DebugWatchpoint* ARM_Dynarmic_32::HaltedWatchpoint() const {
291 return halted_watchpoint;
292}
293
294void ARM_Dynarmic_32::RewindBreakpointInstruction() {
295 LoadContext(breakpoint_context);
296}
297
251ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_, 298ARM_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
76private: 78private:
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;
29class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { 29class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
30public: 30public:
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
354const Kernel::DebugWatchpoint* ARM_Dynarmic_64::HaltedWatchpoint() const {
355 return halted_watchpoint;
356}
357
358void ARM_Dynarmic_64::RewindBreakpointInstruction() {
359 LoadContext(breakpoint_context);
360}
361
311ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_, 362ARM_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
70private: 72private:
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