src/arm/debugger/debugger.c (view raw)
1/* Copyright (c) 2013-2016 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include <mgba/internal/arm/debugger/debugger.h>
7
8#include <mgba/core/core.h>
9#include <mgba/internal/arm/arm.h>
10#include <mgba/internal/arm/isa-inlines.h>
11#include <mgba/internal/arm/debugger/memory-debugger.h>
12
13DEFINE_VECTOR(ARMDebugBreakpointList, struct ARMDebugBreakpoint);
14DEFINE_VECTOR(ARMDebugWatchpointList, struct ARMDebugWatchpoint);
15
16static struct ARMDebugBreakpoint* _lookupBreakpoint(struct ARMDebugBreakpointList* breakpoints, uint32_t address) {
17 size_t i;
18 for (i = 0; i < ARMDebugBreakpointListSize(breakpoints); ++i) {
19 if (ARMDebugBreakpointListGetPointer(breakpoints, i)->address == address) {
20 return ARMDebugBreakpointListGetPointer(breakpoints, i);
21 }
22 }
23 return 0;
24}
25
26static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform* d) {
27 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
28 int instructionLength;
29 enum ExecutionMode mode = ARMPSRIsT(debugger->cpu->cpsr);
30 if (mode == MODE_ARM) {
31 instructionLength = WORD_SIZE_ARM;
32 } else {
33 instructionLength = WORD_SIZE_THUMB;
34 }
35 struct ARMDebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->breakpoints, debugger->cpu->gprs[ARM_PC] - instructionLength);
36 if (!breakpoint) {
37 return;
38 }
39 struct mDebuggerEntryInfo info = {
40 .address = breakpoint->address,
41 .breakType = BREAKPOINT_HARDWARE
42 };
43 mDebuggerEnter(d->p, DEBUGGER_ENTER_BREAKPOINT, &info);
44}
45
46static void ARMDebuggerInit(void* cpu, struct mDebuggerPlatform* platform);
47static void ARMDebuggerDeinit(struct mDebuggerPlatform* platform);
48
49static void ARMDebuggerEnter(struct mDebuggerPlatform* d, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info);
50
51static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform*, uint32_t address);
52static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform*, uint32_t address);
53static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform*, uint32_t address, enum mWatchpointType type);
54static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address);
55static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform*);
56static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform*);
57
58struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) {
59 struct mDebuggerPlatform* platform = (struct mDebuggerPlatform*) malloc(sizeof(struct ARMDebugger));
60 platform->entered = ARMDebuggerEnter;
61 platform->init = ARMDebuggerInit;
62 platform->deinit = ARMDebuggerDeinit;
63 platform->setBreakpoint = ARMDebuggerSetBreakpoint;
64 platform->clearBreakpoint = ARMDebuggerClearBreakpoint;
65 platform->setWatchpoint = ARMDebuggerSetWatchpoint;
66 platform->clearWatchpoint = ARMDebuggerClearWatchpoint;
67 platform->checkBreakpoints = ARMDebuggerCheckBreakpoints;
68 platform->hasBreakpoints = ARMDebuggerHasBreakpoints;
69 return platform;
70}
71
72void ARMDebuggerInit(void* cpu, struct mDebuggerPlatform* platform) {
73 struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
74 debugger->cpu = cpu;
75 debugger->originalMemory = debugger->cpu->memory;
76 ARMDebugBreakpointListInit(&debugger->breakpoints, 0);
77 ARMDebugBreakpointListInit(&debugger->swBreakpoints, 0);
78 ARMDebugWatchpointListInit(&debugger->watchpoints, 0);
79}
80
81void ARMDebuggerDeinit(struct mDebuggerPlatform* platform) {
82 struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
83 if (debugger->clearSoftwareBreakpoint) {
84 // Clear the stack backwards in case any overlap
85 size_t b;
86 for (b = ARMDebugBreakpointListSize(&debugger->swBreakpoints); b; --b) {
87 struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListGetPointer(&debugger->swBreakpoints, b - 1);
88 debugger->clearSoftwareBreakpoint(debugger, breakpoint->address, breakpoint->sw.mode, breakpoint->sw.opcode);
89 }
90 }
91 ARMDebuggerRemoveMemoryShim(debugger);
92
93 ARMDebugBreakpointListDeinit(&debugger->breakpoints);
94 ARMDebugBreakpointListDeinit(&debugger->swBreakpoints);
95 ARMDebugWatchpointListDeinit(&debugger->watchpoints);
96}
97
98static void ARMDebuggerEnter(struct mDebuggerPlatform* platform, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
99 struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
100 struct ARMCore* cpu = debugger->cpu;
101 cpu->nextEvent = cpu->cycles;
102 if (reason == DEBUGGER_ENTER_BREAKPOINT) {
103 struct ARMDebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->swBreakpoints, _ARMPCAddress(cpu));
104 if (breakpoint && breakpoint->isSw) {
105 info->address = breakpoint->address;
106 if (debugger->clearSoftwareBreakpoint) {
107 debugger->clearSoftwareBreakpoint(debugger, breakpoint->address, breakpoint->sw.mode, breakpoint->sw.opcode);
108 }
109
110 ARMRunFake(cpu, breakpoint->sw.opcode);
111
112 if (debugger->setSoftwareBreakpoint) {
113 debugger->setSoftwareBreakpoint(debugger, breakpoint->address, breakpoint->sw.mode, &breakpoint->sw.opcode);
114 }
115 }
116 }
117 if (debugger->d.p->entered) {
118 debugger->d.p->entered(debugger->d.p, reason, info);
119 }
120}
121
122bool ARMDebuggerSetSoftwareBreakpoint(struct mDebuggerPlatform* d, uint32_t address, enum ExecutionMode mode) {
123 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
124 uint32_t opcode;
125 if (!debugger->setSoftwareBreakpoint || !debugger->setSoftwareBreakpoint(debugger, address, mode, &opcode)) {
126 return false;
127 }
128
129 struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->swBreakpoints);
130 breakpoint->address = address;
131 breakpoint->isSw = true;
132 breakpoint->sw.opcode = opcode;
133 breakpoint->sw.mode = mode;
134
135 return true;
136}
137
138void ARMDebuggerClearSoftwareBreakpoint(struct mDebuggerPlatform* d, uint32_t address) {
139 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
140 if (!debugger->clearSoftwareBreakpoint) {
141 return;
142 }
143
144 struct ARMDebugBreakpoint* breakpoint = NULL;
145 // Clear the stack backwards in case any overlap
146 size_t b;
147 for (b = ARMDebugBreakpointListSize(&debugger->swBreakpoints); b; --b) {
148 breakpoint = ARMDebugBreakpointListGetPointer(&debugger->swBreakpoints, b - 1);
149 if (breakpoint->address == address) {
150 break;
151 }
152 breakpoint = NULL;
153 }
154
155 if (breakpoint) {
156 debugger->clearSoftwareBreakpoint(debugger, address, breakpoint->sw.mode, breakpoint->sw.opcode);
157 }
158}
159
160static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t address) {
161 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
162 struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->breakpoints);
163 breakpoint->address = address;
164 breakpoint->isSw = false;
165}
166
167static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform* d, uint32_t address) {
168 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
169 struct ARMDebugBreakpointList* breakpoints = &debugger->breakpoints;
170 size_t i;
171 for (i = 0; i < ARMDebugBreakpointListSize(breakpoints); ++i) {
172 if (ARMDebugBreakpointListGetPointer(breakpoints, i)->address == address) {
173 ARMDebugBreakpointListShift(breakpoints, i, 1);
174 }
175 }
176}
177
178static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform* d) {
179 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
180 return ARMDebugBreakpointListSize(&debugger->breakpoints) || ARMDebugWatchpointListSize(&debugger->watchpoints);
181}
182
183static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t address, enum mWatchpointType type) {
184 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
185 if (!ARMDebugWatchpointListSize(&debugger->watchpoints)) {
186 ARMDebuggerInstallMemoryShim(debugger);
187 }
188 struct ARMDebugWatchpoint* watchpoint = ARMDebugWatchpointListAppend(&debugger->watchpoints);
189 watchpoint->address = address;
190 watchpoint->type = type;
191}
192
193static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t address) {
194 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
195 struct ARMDebugWatchpointList* watchpoints = &debugger->watchpoints;
196 size_t i;
197 for (i = 0; i < ARMDebugWatchpointListSize(watchpoints); ++i) {
198 if (ARMDebugWatchpointListGetPointer(watchpoints, i)->address == address) {
199 ARMDebugWatchpointListShift(watchpoints, i, 1);
200 }
201 }
202 if (!ARMDebugWatchpointListSize(&debugger->watchpoints)) {
203 ARMDebuggerRemoveMemoryShim(debugger);
204 }
205}