summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
m---------externals/dynarmic0
-rw-r--r--src/common/page_table.h3
-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
-rw-r--r--src/core/debugger/debugger.cpp19
-rw-r--r--src/core/debugger/debugger.h8
-rw-r--r--src/core/debugger/debugger_interface.h8
-rw-r--r--src/core/debugger/gdbstub.cpp139
-rw-r--r--src/core/debugger/gdbstub.h3
-rw-r--r--src/core/hardware_properties.h3
-rw-r--r--src/core/hle/kernel/k_process.cpp46
-rw-r--r--src/core/hle/kernel/k_process.h30
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp1
-rw-r--r--src/core/memory.cpp79
-rw-r--r--src/core/memory.h11
19 files changed, 510 insertions, 54 deletions
diff --git a/externals/dynarmic b/externals/dynarmic
Subproject 57af72a567454b93c757e087b4510a24b81911b Subproject 5ad1d02351bf4fee681a3d701d210b419f41a50
diff --git a/src/common/page_table.h b/src/common/page_table.h
index fcbd12a43..1ad3a9f8b 100644
--- a/src/common/page_table.h
+++ b/src/common/page_table.h
@@ -15,6 +15,9 @@ enum class PageType : u8 {
15 Unmapped, 15 Unmapped,
16 /// Page is mapped to regular memory. This is the only type you can get pointers to. 16 /// Page is mapped to regular memory. This is the only type you can get pointers to.
17 Memory, 17 Memory,
18 /// Page is mapped to regular memory, but inaccessible from CPU fastmem and must use
19 /// the callbacks.
20 DebugMemory,
18 /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and 21 /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
19 /// invalidation 22 /// invalidation
20 RasterizerCachedMemory, 23 RasterizerCachedMemory,
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
diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp
index ab3940922..ac64d2f9d 100644
--- a/src/core/debugger/debugger.cpp
+++ b/src/core/debugger/debugger.cpp
@@ -44,12 +44,14 @@ static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
44 44
45enum class SignalType { 45enum class SignalType {
46 Stopped, 46 Stopped,
47 Watchpoint,
47 ShuttingDown, 48 ShuttingDown,
48}; 49};
49 50
50struct SignalInfo { 51struct SignalInfo {
51 SignalType type; 52 SignalType type;
52 Kernel::KThread* thread; 53 Kernel::KThread* thread;
54 const Kernel::DebugWatchpoint* watchpoint;
53}; 55};
54 56
55namespace Core { 57namespace Core {
@@ -157,13 +159,19 @@ private:
157 void PipeData(std::span<const u8> data) { 159 void PipeData(std::span<const u8> data) {
158 switch (info.type) { 160 switch (info.type) {
159 case SignalType::Stopped: 161 case SignalType::Stopped:
162 case SignalType::Watchpoint:
160 // Stop emulation. 163 // Stop emulation.
161 PauseEmulation(); 164 PauseEmulation();
162 165
163 // Notify the client. 166 // Notify the client.
164 active_thread = info.thread; 167 active_thread = info.thread;
165 UpdateActiveThread(); 168 UpdateActiveThread();
166 frontend->Stopped(active_thread); 169
170 if (info.type == SignalType::Watchpoint) {
171 frontend->Watchpoint(active_thread, *info.watchpoint);
172 } else {
173 frontend->Stopped(active_thread);
174 }
167 175
168 break; 176 break;
169 case SignalType::ShuttingDown: 177 case SignalType::ShuttingDown:
@@ -290,12 +298,17 @@ Debugger::Debugger(Core::System& system, u16 port) {
290Debugger::~Debugger() = default; 298Debugger::~Debugger() = default;
291 299
292bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) { 300bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) {
293 return impl && impl->SignalDebugger(SignalInfo{SignalType::Stopped, thread}); 301 return impl && impl->SignalDebugger(SignalInfo{SignalType::Stopped, thread, nullptr});
302}
303
304bool Debugger::NotifyThreadWatchpoint(Kernel::KThread* thread,
305 const Kernel::DebugWatchpoint& watch) {
306 return impl && impl->SignalDebugger(SignalInfo{SignalType::Watchpoint, thread, &watch});
294} 307}
295 308
296void Debugger::NotifyShutdown() { 309void Debugger::NotifyShutdown() {
297 if (impl) { 310 if (impl) {
298 impl->SignalDebugger(SignalInfo{SignalType::ShuttingDown, nullptr}); 311 impl->SignalDebugger(SignalInfo{SignalType::ShuttingDown, nullptr, nullptr});
299 } 312 }
300} 313}
301 314
diff --git a/src/core/debugger/debugger.h b/src/core/debugger/debugger.h
index f9738ca3d..b2f503376 100644
--- a/src/core/debugger/debugger.h
+++ b/src/core/debugger/debugger.h
@@ -9,7 +9,8 @@
9 9
10namespace Kernel { 10namespace Kernel {
11class KThread; 11class KThread;
12} 12struct DebugWatchpoint;
13} // namespace Kernel
13 14
14namespace Core { 15namespace Core {
15class System; 16class System;
@@ -40,6 +41,11 @@ public:
40 */ 41 */
41 void NotifyShutdown(); 42 void NotifyShutdown();
42 43
44 /*
45 * Notify the debugger that the given thread has stopped due to hitting a watchpoint.
46 */
47 bool NotifyThreadWatchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch);
48
43private: 49private:
44 std::unique_ptr<DebuggerImpl> impl; 50 std::unique_ptr<DebuggerImpl> impl;
45}; 51};
diff --git a/src/core/debugger/debugger_interface.h b/src/core/debugger/debugger_interface.h
index c0bb4ecaf..5b31edc43 100644
--- a/src/core/debugger/debugger_interface.h
+++ b/src/core/debugger/debugger_interface.h
@@ -11,7 +11,8 @@
11 11
12namespace Kernel { 12namespace Kernel {
13class KThread; 13class KThread;
14} 14struct DebugWatchpoint;
15} // namespace Kernel
15 16
16namespace Core { 17namespace Core {
17 18
@@ -71,6 +72,11 @@ public:
71 */ 72 */
72 virtual void ShuttingDown() = 0; 73 virtual void ShuttingDown() = 0;
73 74
75 /*
76 * Called when emulation has stopped on a watchpoint.
77 */
78 virtual void Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) = 0;
79
74 /** 80 /**
75 * Called when new data is asynchronously received on the client socket. 81 * Called when new data is asynchronously received on the client socket.
76 * A list of actions to perform is returned. 82 * A list of actions to perform is returned.
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp
index 52e76f659..f5e9a303d 100644
--- a/src/core/debugger/gdbstub.cpp
+++ b/src/core/debugger/gdbstub.cpp
@@ -112,6 +112,23 @@ void GDBStub::Stopped(Kernel::KThread* thread) {
112 SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP)); 112 SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP));
113} 113}
114 114
115void GDBStub::Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) {
116 const auto status{arch->ThreadStatus(thread, GDB_STUB_SIGTRAP)};
117
118 switch (watch.type) {
119 case Kernel::DebugWatchpointType::Read:
120 SendReply(fmt::format("{}rwatch:{:x};", status, watch.start_address));
121 break;
122 case Kernel::DebugWatchpointType::Write:
123 SendReply(fmt::format("{}watch:{:x};", status, watch.start_address));
124 break;
125 case Kernel::DebugWatchpointType::ReadOrWrite:
126 default:
127 SendReply(fmt::format("{}awatch:{:x};", status, watch.start_address));
128 break;
129 }
130}
131
115std::vector<DebuggerAction> GDBStub::ClientData(std::span<const u8> data) { 132std::vector<DebuggerAction> GDBStub::ClientData(std::span<const u8> data) {
116 std::vector<DebuggerAction> actions; 133 std::vector<DebuggerAction> actions;
117 current_command.insert(current_command.end(), data.begin(), data.end()); 134 current_command.insert(current_command.end(), data.begin(), data.end());
@@ -278,41 +295,121 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
278 case 'c': 295 case 'c':
279 actions.push_back(DebuggerAction::Continue); 296 actions.push_back(DebuggerAction::Continue);
280 break; 297 break;
281 case 'Z': { 298 case 'Z':
282 const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1}; 299 HandleBreakpointInsert(command);
283 const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))}; 300 break;
301 case 'z':
302 HandleBreakpointRemove(command);
303 break;
304 default:
305 SendReply(GDB_STUB_REPLY_EMPTY);
306 break;
307 }
308}
284 309
285 if (system.Memory().IsValidVirtualAddress(addr)) { 310enum class BreakpointType {
286 replaced_instructions[addr] = system.Memory().Read32(addr); 311 Software = 0,
287 system.Memory().Write32(addr, arch->BreakpointInstruction()); 312 Hardware = 1,
288 system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32)); 313 WriteWatch = 2,
314 ReadWatch = 3,
315 AccessWatch = 4,
316};
317
318void GDBStub::HandleBreakpointInsert(std::string_view command) {
319 const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))};
320 const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
321 const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') -
322 command.begin() + 1};
323 const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
324 const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
325
326 if (!system.Memory().IsValidVirtualAddressRange(addr, size)) {
327 SendReply(GDB_STUB_REPLY_ERR);
328 return;
329 }
289 330
290 SendReply(GDB_STUB_REPLY_OK); 331 bool success{};
291 } else { 332
292 SendReply(GDB_STUB_REPLY_ERR); 333 switch (type) {
293 } 334 case BreakpointType::Software:
335 replaced_instructions[addr] = system.Memory().Read32(addr);
336 system.Memory().Write32(addr, arch->BreakpointInstruction());
337 system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
338 success = true;
339 break;
340 case BreakpointType::WriteWatch:
341 success = system.CurrentProcess()->InsertWatchpoint(system, addr, size,
342 Kernel::DebugWatchpointType::Write);
294 break; 343 break;
344 case BreakpointType::ReadWatch:
345 success = system.CurrentProcess()->InsertWatchpoint(system, addr, size,
346 Kernel::DebugWatchpointType::Read);
347 break;
348 case BreakpointType::AccessWatch:
349 success = system.CurrentProcess()->InsertWatchpoint(
350 system, addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
351 break;
352 case BreakpointType::Hardware:
353 default:
354 SendReply(GDB_STUB_REPLY_EMPTY);
355 return;
356 }
357
358 if (success) {
359 SendReply(GDB_STUB_REPLY_OK);
360 } else {
361 SendReply(GDB_STUB_REPLY_ERR);
295 } 362 }
296 case 'z': { 363}
297 const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1}; 364
298 const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))}; 365void GDBStub::HandleBreakpointRemove(std::string_view command) {
366 const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))};
367 const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
368 const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') -
369 command.begin() + 1};
370 const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
371 const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
372
373 if (!system.Memory().IsValidVirtualAddressRange(addr, size)) {
374 SendReply(GDB_STUB_REPLY_ERR);
375 return;
376 }
377
378 bool success{};
299 379
380 switch (type) {
381 case BreakpointType::Software: {
300 const auto orig_insn{replaced_instructions.find(addr)}; 382 const auto orig_insn{replaced_instructions.find(addr)};
301 if (system.Memory().IsValidVirtualAddress(addr) && 383 if (orig_insn != replaced_instructions.end()) {
302 orig_insn != replaced_instructions.end()) {
303 system.Memory().Write32(addr, orig_insn->second); 384 system.Memory().Write32(addr, orig_insn->second);
304 system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32)); 385 system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
305 replaced_instructions.erase(addr); 386 replaced_instructions.erase(addr);
306 387 success = true;
307 SendReply(GDB_STUB_REPLY_OK);
308 } else {
309 SendReply(GDB_STUB_REPLY_ERR);
310 } 388 }
311 break; 389 break;
312 } 390 }
391 case BreakpointType::WriteWatch:
392 success = system.CurrentProcess()->RemoveWatchpoint(system, addr, size,
393 Kernel::DebugWatchpointType::Write);
394 break;
395 case BreakpointType::ReadWatch:
396 success = system.CurrentProcess()->RemoveWatchpoint(system, addr, size,
397 Kernel::DebugWatchpointType::Read);
398 break;
399 case BreakpointType::AccessWatch:
400 success = system.CurrentProcess()->RemoveWatchpoint(
401 system, addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
402 break;
403 case BreakpointType::Hardware:
313 default: 404 default:
314 SendReply(GDB_STUB_REPLY_EMPTY); 405 SendReply(GDB_STUB_REPLY_EMPTY);
315 break; 406 return;
407 }
408
409 if (success) {
410 SendReply(GDB_STUB_REPLY_OK);
411 } else {
412 SendReply(GDB_STUB_REPLY_ERR);
316 } 413 }
317} 414}
318 415
diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h
index ec934c77e..0b0f56e4b 100644
--- a/src/core/debugger/gdbstub.h
+++ b/src/core/debugger/gdbstub.h
@@ -24,6 +24,7 @@ public:
24 void Connected() override; 24 void Connected() override;
25 void Stopped(Kernel::KThread* thread) override; 25 void Stopped(Kernel::KThread* thread) override;
26 void ShuttingDown() override; 26 void ShuttingDown() override;
27 void Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) override;
27 std::vector<DebuggerAction> ClientData(std::span<const u8> data) override; 28 std::vector<DebuggerAction> ClientData(std::span<const u8> data) override;
28 29
29private: 30private:
@@ -31,6 +32,8 @@ private:
31 void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions); 32 void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions);
32 void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions); 33 void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions);
33 void HandleQuery(std::string_view command); 34 void HandleQuery(std::string_view command);
35 void HandleBreakpointInsert(std::string_view command);
36 void HandleBreakpointRemove(std::string_view command);
34 std::vector<char>::const_iterator CommandEnd() const; 37 std::vector<char>::const_iterator CommandEnd() const;
35 std::optional<std::string> DetachCommand(); 38 std::optional<std::string> DetachCommand();
36 Kernel::KThread* GetThreadByID(u64 thread_id); 39 Kernel::KThread* GetThreadByID(u64 thread_id);
diff --git a/src/core/hardware_properties.h b/src/core/hardware_properties.h
index aac362c51..13cbdb734 100644
--- a/src/core/hardware_properties.h
+++ b/src/core/hardware_properties.h
@@ -25,6 +25,9 @@ constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{
25 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 25 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
26}; 26};
27 27
28// Cortex-A57 supports 4 memory watchpoints
29constexpr u64 NUM_WATCHPOINTS = 4;
30
28} // namespace Hardware 31} // namespace Hardware
29 32
30} // namespace Core 33} // namespace Core
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index cd863e715..6a73f6783 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -584,6 +584,52 @@ ResultCode KProcess::DeleteThreadLocalRegion(VAddr addr) {
584 return ResultSuccess; 584 return ResultSuccess;
585} 585}
586 586
587bool KProcess::InsertWatchpoint(Core::System& system, VAddr addr, u64 size,
588 DebugWatchpointType type) {
589 const auto watch{std::find_if(watchpoints.begin(), watchpoints.end(), [&](const auto& wp) {
590 return wp.type == DebugWatchpointType::None;
591 })};
592
593 if (watch == watchpoints.end()) {
594 return false;
595 }
596
597 watch->start_address = addr;
598 watch->end_address = addr + size;
599 watch->type = type;
600
601 for (VAddr page = Common::AlignDown(addr, PageSize); page < addr + size; page += PageSize) {
602 debug_page_refcounts[page]++;
603 system.Memory().MarkRegionDebug(page, PageSize, true);
604 }
605
606 return true;
607}
608
609bool KProcess::RemoveWatchpoint(Core::System& system, VAddr addr, u64 size,
610 DebugWatchpointType type) {
611 const auto watch{std::find_if(watchpoints.begin(), watchpoints.end(), [&](const auto& wp) {
612 return wp.start_address == addr && wp.end_address == addr + size && wp.type == type;
613 })};
614
615 if (watch == watchpoints.end()) {
616 return false;
617 }
618
619 watch->start_address = 0;
620 watch->end_address = 0;
621 watch->type = DebugWatchpointType::None;
622
623 for (VAddr page = Common::AlignDown(addr, PageSize); page < addr + size; page += PageSize) {
624 debug_page_refcounts[page]--;
625 if (!debug_page_refcounts[page]) {
626 system.Memory().MarkRegionDebug(page, PageSize, false);
627 }
628 }
629
630 return true;
631}
632
587void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) { 633void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) {
588 const auto ReprotectSegment = [&](const CodeSet::Segment& segment, 634 const auto ReprotectSegment = [&](const CodeSet::Segment& segment,
589 Svc::MemoryPermission permission) { 635 Svc::MemoryPermission permission) {
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h
index e562a79b8..c2086e5ba 100644
--- a/src/core/hle/kernel/k_process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -7,6 +7,7 @@
7#include <array> 7#include <array>
8#include <cstddef> 8#include <cstddef>
9#include <list> 9#include <list>
10#include <map>
10#include <string> 11#include <string>
11#include "common/common_types.h" 12#include "common/common_types.h"
12#include "core/hle/kernel/k_address_arbiter.h" 13#include "core/hle/kernel/k_address_arbiter.h"
@@ -68,6 +69,20 @@ enum class ProcessActivity : u32 {
68 Paused, 69 Paused,
69}; 70};
70 71
72enum class DebugWatchpointType : u8 {
73 None = 0,
74 Read = 1 << 0,
75 Write = 1 << 1,
76 ReadOrWrite = Read | Write,
77};
78DECLARE_ENUM_FLAG_OPERATORS(DebugWatchpointType);
79
80struct DebugWatchpoint {
81 VAddr start_address;
82 VAddr end_address;
83 DebugWatchpointType type;
84};
85
71class KProcess final : public KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask> { 86class KProcess final : public KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask> {
72 KERNEL_AUTOOBJECT_TRAITS(KProcess, KSynchronizationObject); 87 KERNEL_AUTOOBJECT_TRAITS(KProcess, KSynchronizationObject);
73 88
@@ -374,6 +389,19 @@ public:
374 // Frees a used TLS slot identified by the given address 389 // Frees a used TLS slot identified by the given address
375 ResultCode DeleteThreadLocalRegion(VAddr addr); 390 ResultCode DeleteThreadLocalRegion(VAddr addr);
376 391
392 ///////////////////////////////////////////////////////////////////////////////////////////////
393 // Debug watchpoint management
394
395 // Attempts to insert a watchpoint into a free slot. Returns false if none are available.
396 bool InsertWatchpoint(Core::System& system, VAddr addr, u64 size, DebugWatchpointType type);
397
398 // Attempts to remove the watchpoint specified by the given parameters.
399 bool RemoveWatchpoint(Core::System& system, VAddr addr, u64 size, DebugWatchpointType type);
400
401 const std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>& GetWatchpoints() const {
402 return watchpoints;
403 }
404
377private: 405private:
378 void PinThread(s32 core_id, KThread* thread) { 406 void PinThread(s32 core_id, KThread* thread) {
379 ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES)); 407 ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
@@ -478,6 +506,8 @@ private:
478 std::array<KThread*, Core::Hardware::NUM_CPU_CORES> running_threads{}; 506 std::array<KThread*, Core::Hardware::NUM_CPU_CORES> running_threads{};
479 std::array<u64, Core::Hardware::NUM_CPU_CORES> running_thread_idle_counts{}; 507 std::array<u64, Core::Hardware::NUM_CPU_CORES> running_thread_idle_counts{};
480 std::array<KThread*, Core::Hardware::NUM_CPU_CORES> pinned_threads{}; 508 std::array<KThread*, Core::Hardware::NUM_CPU_CORES> pinned_threads{};
509 std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS> watchpoints{};
510 std::map<VAddr, u64> debug_page_refcounts;
481 511
482 KThread* exception_thread{}; 512 KThread* exception_thread{};
483 513
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index 2d4e8637b..edd0e4eae 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -710,6 +710,7 @@ void KScheduler::Reload(KThread* thread) {
710 Core::ARM_Interface& cpu_core = system.ArmInterface(core_id); 710 Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
711 cpu_core.LoadContext(thread->GetContext32()); 711 cpu_core.LoadContext(thread->GetContext32());
712 cpu_core.LoadContext(thread->GetContext64()); 712 cpu_core.LoadContext(thread->GetContext64());
713 cpu_core.LoadWatchpointArray(thread->GetOwnerProcess()->GetWatchpoints());
713 cpu_core.SetTlsAddress(thread->GetTLSAddress()); 714 cpu_core.SetTlsAddress(thread->GetTLSAddress());
714 cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0()); 715 cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
715 cpu_core.ClearExclusiveState(); 716 cpu_core.ClearExclusiveState();
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 7534de01e..584808d50 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -67,6 +67,16 @@ struct Memory::Impl {
67 return system.DeviceMemory().GetPointer(paddr) + vaddr; 67 return system.DeviceMemory().GetPointer(paddr) + vaddr;
68 } 68 }
69 69
70 [[nodiscard]] u8* GetPointerFromDebugMemory(VAddr vaddr) const {
71 const PAddr paddr{current_page_table->backing_addr[vaddr >> PAGE_BITS]};
72
73 if (paddr == 0) {
74 return {};
75 }
76
77 return system.DeviceMemory().GetPointer(paddr) + vaddr;
78 }
79
70 u8 Read8(const VAddr addr) { 80 u8 Read8(const VAddr addr) {
71 return Read<u8>(addr); 81 return Read<u8>(addr);
72 } 82 }
@@ -187,6 +197,12 @@ struct Memory::Impl {
187 on_memory(copy_amount, mem_ptr); 197 on_memory(copy_amount, mem_ptr);
188 break; 198 break;
189 } 199 }
200 case Common::PageType::DebugMemory: {
201 DEBUG_ASSERT(pointer);
202 u8* const mem_ptr{GetPointerFromDebugMemory(current_vaddr)};
203 on_memory(copy_amount, mem_ptr);
204 break;
205 }
190 case Common::PageType::RasterizerCachedMemory: { 206 case Common::PageType::RasterizerCachedMemory: {
191 u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)}; 207 u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)};
192 on_rasterizer(current_vaddr, copy_amount, host_ptr); 208 on_rasterizer(current_vaddr, copy_amount, host_ptr);
@@ -316,6 +332,58 @@ struct Memory::Impl {
316 }); 332 });
317 } 333 }
318 334
335 void MarkRegionDebug(VAddr vaddr, u64 size, bool debug) {
336 if (vaddr == 0) {
337 return;
338 }
339
340 // Iterate over a contiguous CPU address space, marking/unmarking the region.
341 // The region is at a granularity of CPU pages.
342
343 const u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
344 for (u64 i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
345 const Common::PageType page_type{
346 current_page_table->pointers[vaddr >> PAGE_BITS].Type()};
347 if (debug) {
348 // Switch page type to debug if now debug
349 switch (page_type) {
350 case Common::PageType::Unmapped:
351 ASSERT_MSG(false, "Attempted to mark unmapped pages as debug");
352 break;
353 case Common::PageType::RasterizerCachedMemory:
354 case Common::PageType::DebugMemory:
355 // Page is already marked.
356 break;
357 case Common::PageType::Memory:
358 current_page_table->pointers[vaddr >> PAGE_BITS].Store(
359 nullptr, Common::PageType::DebugMemory);
360 break;
361 default:
362 UNREACHABLE();
363 }
364 } else {
365 // Switch page type to non-debug if now non-debug
366 switch (page_type) {
367 case Common::PageType::Unmapped:
368 ASSERT_MSG(false, "Attempted to mark unmapped pages as non-debug");
369 break;
370 case Common::PageType::RasterizerCachedMemory:
371 case Common::PageType::Memory:
372 // Don't mess with already non-debug or rasterizer memory.
373 break;
374 case Common::PageType::DebugMemory: {
375 u8* const pointer{GetPointerFromDebugMemory(vaddr & ~PAGE_MASK)};
376 current_page_table->pointers[vaddr >> PAGE_BITS].Store(
377 pointer - (vaddr & ~PAGE_MASK), Common::PageType::Memory);
378 break;
379 }
380 default:
381 UNREACHABLE();
382 }
383 }
384 }
385 }
386
319 void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { 387 void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
320 if (vaddr == 0) { 388 if (vaddr == 0) {
321 return; 389 return;
@@ -342,6 +410,7 @@ struct Memory::Impl {
342 // It is not necessary for a process to have this region mapped into its address 410 // It is not necessary for a process to have this region mapped into its address
343 // space, for example, a system module need not have a VRAM mapping. 411 // space, for example, a system module need not have a VRAM mapping.
344 break; 412 break;
413 case Common::PageType::DebugMemory:
345 case Common::PageType::Memory: 414 case Common::PageType::Memory:
346 current_page_table->pointers[vaddr >> PAGE_BITS].Store( 415 current_page_table->pointers[vaddr >> PAGE_BITS].Store(
347 nullptr, Common::PageType::RasterizerCachedMemory); 416 nullptr, Common::PageType::RasterizerCachedMemory);
@@ -360,6 +429,7 @@ struct Memory::Impl {
360 // It is not necessary for a process to have this region mapped into its address 429 // It is not necessary for a process to have this region mapped into its address
361 // space, for example, a system module need not have a VRAM mapping. 430 // space, for example, a system module need not have a VRAM mapping.
362 break; 431 break;
432 case Common::PageType::DebugMemory:
363 case Common::PageType::Memory: 433 case Common::PageType::Memory:
364 // There can be more than one GPU region mapped per CPU region, so it's common 434 // There can be more than one GPU region mapped per CPU region, so it's common
365 // that this area is already unmarked as cached. 435 // that this area is already unmarked as cached.
@@ -460,6 +530,8 @@ struct Memory::Impl {
460 case Common::PageType::Memory: 530 case Common::PageType::Memory:
461 ASSERT_MSG(false, "Mapped memory page without a pointer @ 0x{:016X}", vaddr); 531 ASSERT_MSG(false, "Mapped memory page without a pointer @ 0x{:016X}", vaddr);
462 return nullptr; 532 return nullptr;
533 case Common::PageType::DebugMemory:
534 return GetPointerFromDebugMemory(vaddr);
463 case Common::PageType::RasterizerCachedMemory: { 535 case Common::PageType::RasterizerCachedMemory: {
464 u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; 536 u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
465 on_rasterizer(); 537 on_rasterizer();
@@ -591,7 +663,8 @@ bool Memory::IsValidVirtualAddress(const VAddr vaddr) const {
591 return false; 663 return false;
592 } 664 }
593 const auto [pointer, type] = page_table.pointers[page].PointerType(); 665 const auto [pointer, type] = page_table.pointers[page].PointerType();
594 return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory; 666 return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory ||
667 type == Common::PageType::DebugMemory;
595} 668}
596 669
597bool Memory::IsValidVirtualAddressRange(VAddr base, u64 size) const { 670bool Memory::IsValidVirtualAddressRange(VAddr base, u64 size) const {
@@ -707,4 +780,8 @@ void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
707 impl->RasterizerMarkRegionCached(vaddr, size, cached); 780 impl->RasterizerMarkRegionCached(vaddr, size, cached);
708} 781}
709 782
783void Memory::MarkRegionDebug(VAddr vaddr, u64 size, bool debug) {
784 impl->MarkRegionDebug(vaddr, size, debug);
785}
786
710} // namespace Core::Memory 787} // namespace Core::Memory
diff --git a/src/core/memory.h b/src/core/memory.h
index 58cc27b29..f22c0a2d8 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -446,6 +446,17 @@ public:
446 */ 446 */
447 void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached); 447 void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached);
448 448
449 /**
450 * Marks each page within the specified address range as debug or non-debug.
451 * Debug addresses are not accessible from fastmem pointers.
452 *
453 * @param vaddr The virtual address indicating the start of the address range.
454 * @param size The size of the address range in bytes.
455 * @param debug Whether or not any pages within the address range should be
456 * marked as debug or non-debug.
457 */
458 void MarkRegionDebug(VAddr vaddr, u64 size, bool debug);
459
449private: 460private:
450 Core::System& system; 461 Core::System& system;
451 462