Sync to trunk r65566.
[reactos.git] / ntoskrnl / kd64 / kdbreak.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/kd64/kdbreak.c
5 * PURPOSE: KD64 Breakpoint Support
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Stefan Ginsberg (stefan.ginsberg@reactos.org)
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 /* FUNCTIONS *****************************************************************/
17
18 ULONG
19 NTAPI
20 KdpAddBreakpoint(IN PVOID Address)
21 {
22 KD_BREAKPOINT_TYPE Content;
23 ULONG i;
24 NTSTATUS Status;
25
26 /* Loop current breakpoints */
27 for (i = 0; i < KD_BREAKPOINT_MAX; i++)
28 {
29 /* Check if the breakpoint is valid */
30 if ((KdpBreakpointTable[i].Flags & KdpBreakpointActive) &&
31 (KdpBreakpointTable[i].Address == Address))
32 {
33 /* Check if it's pending */
34 if ((KdpBreakpointTable[i].Flags & KdpBreakpointPending))
35 {
36 /* It's not pending anymore now */
37 KdpBreakpointTable[i].Flags &= ~KdpBreakpointPending;
38 return i + 1;
39 }
40 else
41 {
42 /* Fail */
43 return 0;
44 }
45 }
46 }
47
48 /* Find a free entry */
49 for (i = 0; i < KD_BREAKPOINT_MAX; i++) if (!(KdpBreakpointTable[i].Flags)) break;
50
51 /* Fail if no free entry was found */
52 if (i == KD_BREAKPOINT_MAX) return 0;
53
54 /* Save the old instruction */
55 Status = KdpCopyMemoryChunks((ULONG_PTR)Address,
56 &Content,
57 KD_BREAKPOINT_SIZE,
58 0,
59 MMDBG_COPY_UNSAFE,
60 NULL);
61
62 if (!NT_SUCCESS(Status))
63 {
64 /* TODO: Set it as a owed breakpoint */
65 KdpDprintf("Failed to set breakpoint at address 0x%p\n", Address);
66 return 0;
67 }
68
69 /* Write the entry */
70 KdpBreakpointTable[i].Address = Address;
71 KdpBreakpointTable[i].Content = Content;
72 KdpBreakpointTable[i].Flags = KdpBreakpointActive;
73
74 /* Write the breakpoint */
75 Status = KdpCopyMemoryChunks((ULONG_PTR)Address,
76 &KdpBreakpointInstruction,
77 KD_BREAKPOINT_SIZE,
78 0,
79 MMDBG_COPY_UNSAFE | MMDBG_COPY_WRITE,
80 NULL);
81 if (!NT_SUCCESS(Status))
82 {
83 /* This should never happen */
84 KdpDprintf("Unable to write breakpoint to address 0x%p\n", Address);
85 }
86
87 /* Return the breakpoint handle */
88 return i + 1;
89 }
90
91 BOOLEAN
92 NTAPI
93 KdpLowWriteContent(IN ULONG BpIndex)
94 {
95 NTSTATUS Status;
96
97 /* Make sure that the breakpoint is actually active */
98 if (KdpBreakpointTable[BpIndex].Flags & KdpBreakpointPending)
99 {
100 /* So we have a valid breakpoint, but it hasn't been used yet... */
101 KdpBreakpointTable[BpIndex].Flags &= ~KdpBreakpointPending;
102 return TRUE;
103 }
104
105 /* Is the original instruction a breakpoint anyway? */
106 if (KdpBreakpointTable[BpIndex].Content == KdpBreakpointInstruction)
107 {
108 /* Then leave it that way... */
109 return TRUE;
110 }
111
112 /* We have an active breakpoint with an instruction to bring back. Do it. */
113 Status = KdpCopyMemoryChunks((ULONG_PTR)KdpBreakpointTable[BpIndex].
114 Address,
115 &KdpBreakpointTable[BpIndex].Content,
116 KD_BREAKPOINT_SIZE,
117 0,
118 MMDBG_COPY_UNSAFE | MMDBG_COPY_WRITE,
119 NULL);
120 if (!NT_SUCCESS(Status))
121 {
122 /* TODO: Set it as a owed breakpoint */
123 KdpDprintf("Failed to delete breakpoint at address 0x%p\n",
124 KdpBreakpointTable[BpIndex].Address);
125 return FALSE;
126 }
127
128 /* Everything went fine, return */
129 return TRUE;
130 }
131
132 BOOLEAN
133 NTAPI
134 KdpLowRestoreBreakpoint(IN ULONG BpIndex)
135 {
136 NTSTATUS Status;
137
138 /* Were we not able to remove it earlier? */
139 if (KdpBreakpointTable[BpIndex].Flags & KdpBreakpointExpired)
140 {
141 /* Well then, we'll just re-use it and return success! */
142 KdpBreakpointTable[BpIndex].Flags &= ~KdpBreakpointExpired;
143 return TRUE;
144 }
145
146 /* Are we merely writing a breakpoint on top of another breakpoint? */
147 if (KdpBreakpointTable[BpIndex].Content == KdpBreakpointInstruction)
148 {
149 /* Nothing to do then... */
150 return TRUE;
151 }
152
153 /* Ok, we actually have to overwrite the instruction now */
154 Status = KdpCopyMemoryChunks((ULONG_PTR)KdpBreakpointTable[BpIndex].
155 Address,
156 &KdpBreakpointInstruction,
157 KD_BREAKPOINT_SIZE,
158 0,
159 MMDBG_COPY_UNSAFE | MMDBG_COPY_WRITE,
160 NULL);
161 if (!NT_SUCCESS(Status))
162 {
163 /* FIXME: Set it as a owed breakpoint */
164 KdpDprintf("Failed to restore breakpoint at address 0x%p\n",
165 KdpBreakpointTable[BpIndex].Address);
166 return FALSE;
167 }
168
169 /* Clear any possible previous pending flag and return success */
170 KdpBreakpointTable[BpIndex].Flags &= ~KdpBreakpointPending;
171 return TRUE;
172 }
173
174 BOOLEAN
175 NTAPI
176 KdpDeleteBreakpoint(IN ULONG BpEntry)
177 {
178 ULONG BpIndex = BpEntry - 1;
179
180 /* Check for invalid breakpoint entry */
181 if (!(BpEntry) || (BpEntry > KD_BREAKPOINT_MAX)) return FALSE;
182
183 /* If the specified breakpoint table entry is not valid, then return FALSE. */
184 if (!KdpBreakpointTable[BpIndex].Flags) return FALSE;
185
186 /* Check if the breakpoint is suspended */
187 if (KdpBreakpointTable[BpIndex].Flags & KdpBreakpointSuspended)
188 {
189 /* Check if breakpoint is not ...? */
190 if (!(KdpBreakpointTable[BpIndex].Flags & KdpBreakpointExpired))
191 {
192 /* Invalidate it and return success */
193 KdpBreakpointTable[BpIndex].Flags = 0;
194 return TRUE;
195 }
196 }
197
198 /* Restore original data, then invalidate it and return success */
199 if (KdpLowWriteContent(BpIndex)) KdpBreakpointTable[BpIndex].Flags = 0;
200 return TRUE;
201 }
202
203 BOOLEAN
204 NTAPI
205 KdpDeleteBreakpointRange(IN PVOID Base,
206 IN PVOID Limit)
207 {
208 ULONG BpIndex;
209 BOOLEAN Return = FALSE;
210
211 /* Loop the breakpoint table */
212 for (BpIndex = 0; BpIndex < KD_BREAKPOINT_MAX; BpIndex++)
213 {
214 /* Make sure that the breakpoint is active and matches the range. */
215 if ((KdpBreakpointTable[BpIndex].Flags & KdpBreakpointActive) &&
216 ((KdpBreakpointTable[BpIndex].Address >= Base) &&
217 (KdpBreakpointTable[BpIndex].Address <= Limit)))
218 {
219 /* Delete it */
220 Return = Return || KdpDeleteBreakpoint(BpIndex + 1);
221 }
222 }
223
224 /* Return to caller */
225 return Return;
226 }
227
228 VOID
229 NTAPI
230 KdpRestoreAllBreakpoints(VOID)
231 {
232 ULONG BpIndex;
233
234 /* No more suspended Breakpoints */
235 BreakpointsSuspended = FALSE;
236
237 /* Loop the breakpoints */
238 for (BpIndex = 0; BpIndex < KD_BREAKPOINT_MAX; BpIndex++)
239 {
240 /* Check if they are valid, suspended breakpoints */
241 if ((KdpBreakpointTable[BpIndex].Flags & KdpBreakpointActive) &&
242 (KdpBreakpointTable[BpIndex].Flags & KdpBreakpointSuspended))
243 {
244 /* Unsuspend them */
245 KdpBreakpointTable[BpIndex].Flags &= ~KdpBreakpointSuspended;
246 KdpLowRestoreBreakpoint(BpIndex);
247 }
248 }
249 }
250
251 VOID
252 NTAPI
253 KdpSuspendBreakPoint(IN ULONG BpEntry)
254 {
255 ULONG BpIndex = BpEntry - 1;
256
257 /* Check if this is a valid, unsuspended breakpoint */
258 if ((KdpBreakpointTable[BpIndex].Flags & KdpBreakpointActive) &&
259 !(KdpBreakpointTable[BpIndex].Flags & KdpBreakpointSuspended))
260 {
261 /* Suspend it */
262 KdpBreakpointTable[BpIndex].Flags |= KdpBreakpointSuspended;
263 KdpLowWriteContent(BpIndex);
264 }
265 }
266
267 VOID
268 NTAPI
269 KdpSuspendAllBreakPoints(VOID)
270 {
271 ULONG BpEntry;
272
273 /* Breakpoints are suspended */
274 BreakpointsSuspended = TRUE;
275
276 /* Loop every breakpoint */
277 for (BpEntry = 1; BpEntry <= KD_BREAKPOINT_MAX; BpEntry++)
278 {
279 /* Suspend it */
280 KdpSuspendBreakPoint(BpEntry);
281 }
282 }