[NTOSKRNL] Add a raw implementation of !irpfind in kdbg
[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 /* Check whether we are not setting a breakpoint twice */
27 for (i = 0; i < KD_BREAKPOINT_MAX; i++)
28 {
29 /* Check if the breakpoint is valid */
30 if ((KdpBreakpointTable[i].Flags & KD_BREAKPOINT_ACTIVE) &&
31 (KdpBreakpointTable[i].Address == Address))
32 {
33 /* Were we not able to remove it earlier? */
34 if (KdpBreakpointTable[i].Flags & KD_BREAKPOINT_EXPIRED)
35 {
36 /* Just re-use it! */
37 KdpBreakpointTable[i].Flags &= ~KD_BREAKPOINT_EXPIRED;
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++)
50 {
51 if (KdpBreakpointTable[i].Flags == 0)
52 break;
53 }
54
55 /* Fail if no free entry was found */
56 if (i == KD_BREAKPOINT_MAX) return 0;
57
58 /* Save the breakpoint */
59 KdpBreakpointTable[i].Address = Address;
60
61 /* If we are setting the breakpoint in user space, save the active process context */
62 if (Address < KD_HIGHEST_USER_BREAKPOINT_ADDRESS)
63 KdpBreakpointTable[i].DirectoryTableBase = KeGetCurrentThread()->ApcState.Process->DirectoryTableBase[0];
64
65 /* Try to save the old instruction */
66 Status = KdpCopyMemoryChunks((ULONG_PTR)Address,
67 &Content,
68 KD_BREAKPOINT_SIZE,
69 0,
70 MMDBG_COPY_UNSAFE,
71 NULL);
72 if (NT_SUCCESS(Status))
73 {
74 /* Memory accessible, set the breakpoint */
75 KdpBreakpointTable[i].Content = Content;
76 KdpBreakpointTable[i].Flags = KD_BREAKPOINT_ACTIVE;
77
78 /* Write the breakpoint */
79 Status = KdpCopyMemoryChunks((ULONG_PTR)Address,
80 &KdpBreakpointInstruction,
81 KD_BREAKPOINT_SIZE,
82 0,
83 MMDBG_COPY_UNSAFE | MMDBG_COPY_WRITE,
84 NULL);
85 if (!NT_SUCCESS(Status))
86 {
87 /* This should never happen */
88 KdpDprintf("Unable to write breakpoint to address 0x%p\n", Address);
89 }
90 }
91 else
92 {
93 /* Memory is inaccessible now, setting breakpoint is deferred */
94 KdpDprintf("Failed to set breakpoint at address 0x%p, adding deferred breakpoint.\n", Address);
95 KdpBreakpointTable[i].Flags = KD_BREAKPOINT_ACTIVE | KD_BREAKPOINT_PENDING;
96 KdpOweBreakpoint = TRUE;
97 }
98
99 /* Return the breakpoint handle */
100 return i + 1;
101 }
102
103 VOID
104 NTAPI
105 KdSetOwedBreakpoints(VOID)
106 {
107 BOOLEAN Enable;
108 KD_BREAKPOINT_TYPE Content;
109 ULONG i;
110 NTSTATUS Status;
111
112 /* If we don't owe any breakpoints, just return */
113 if (!KdpOweBreakpoint) return;
114
115 /* Enter the debugger */
116 Enable = KdEnterDebugger(NULL, NULL);
117
118 /*
119 * Suppose we succeed in setting all the breakpoints.
120 * If we fail to do so, the flag will be set again.
121 */
122 KdpOweBreakpoint = FALSE;
123
124 /* Loop through current breakpoints and try to set or delete the pending ones */
125 for (i = 0; i < KD_BREAKPOINT_MAX; i++)
126 {
127 if (KdpBreakpointTable[i].Flags & (KD_BREAKPOINT_PENDING | KD_BREAKPOINT_EXPIRED))
128 {
129 /*
130 * Set the breakpoint only if it is in kernel space, or if it is
131 * in user space and the active process context matches.
132 */
133 if (KdpBreakpointTable[i].Address < KD_HIGHEST_USER_BREAKPOINT_ADDRESS &&
134 KdpBreakpointTable[i].DirectoryTableBase != KeGetCurrentThread()->ApcState.Process->DirectoryTableBase[0])
135 {
136 KdpOweBreakpoint = TRUE;
137 continue;
138 }
139
140 /* Try to save the old instruction */
141 Status = KdpCopyMemoryChunks((ULONG_PTR)KdpBreakpointTable[i].Address,
142 &Content,
143 KD_BREAKPOINT_SIZE,
144 0,
145 MMDBG_COPY_UNSAFE,
146 NULL);
147 if (!NT_SUCCESS(Status))
148 {
149 /* Memory is still inaccessible, breakpoint setting will be deferred again */
150 // KdpDprintf("Failed to set deferred breakpoint at address 0x%p\n",
151 // KdpBreakpointTable[i].Address);
152 KdpOweBreakpoint = TRUE;
153 continue;
154 }
155
156 /* Check if we need to write the breakpoint */
157 if (KdpBreakpointTable[i].Flags & KD_BREAKPOINT_PENDING)
158 {
159 /* Memory accessible, set the breakpoint */
160 KdpBreakpointTable[i].Content = Content;
161
162 /* Write the breakpoint */
163 Status = KdpCopyMemoryChunks((ULONG_PTR)KdpBreakpointTable[i].Address,
164 &KdpBreakpointInstruction,
165 KD_BREAKPOINT_SIZE,
166 0,
167 MMDBG_COPY_UNSAFE | MMDBG_COPY_WRITE,
168 NULL);
169 if (!NT_SUCCESS(Status))
170 {
171 /* This should never happen */
172 KdpDprintf("Unable to write deferred breakpoint to address 0x%p\n",
173 KdpBreakpointTable[i].Address);
174 KdpOweBreakpoint = TRUE;
175 }
176 else
177 {
178 KdpBreakpointTable[i].Flags = KD_BREAKPOINT_ACTIVE;
179 }
180
181 continue;
182 }
183
184 /* Check if we need to restore the original instruction */
185 if (KdpBreakpointTable[i].Flags & KD_BREAKPOINT_EXPIRED)
186 {
187 /* Write it back */
188 Status = KdpCopyMemoryChunks((ULONG_PTR)KdpBreakpointTable[i].Address,
189 &KdpBreakpointTable[i].Content,
190 KD_BREAKPOINT_SIZE,
191 0,
192 MMDBG_COPY_UNSAFE | MMDBG_COPY_WRITE,
193 NULL);
194 if (!NT_SUCCESS(Status))
195 {
196 /* This should never happen */
197 KdpDprintf("Unable to delete deferred breakpoint at address 0x%p\n",
198 KdpBreakpointTable[i].Address);
199 KdpOweBreakpoint = TRUE;
200 }
201 else
202 {
203 /* Check if the breakpoint is suspended */
204 if (KdpBreakpointTable[i].Flags & KD_BREAKPOINT_SUSPENDED)
205 {
206 KdpBreakpointTable[i].Flags = KD_BREAKPOINT_SUSPENDED | KD_BREAKPOINT_ACTIVE;
207 }
208 else
209 {
210 /* Invalidate it */
211 KdpBreakpointTable[i].Flags = 0;
212 }
213 }
214
215 continue;
216 }
217 }
218 }
219
220 /* Exit the debugger */
221 KdExitDebugger(Enable);
222 }
223
224 BOOLEAN
225 NTAPI
226 KdpLowWriteContent(IN ULONG BpIndex)
227 {
228 NTSTATUS Status;
229
230 /* Make sure that the breakpoint is actually active */
231 if (KdpBreakpointTable[BpIndex].Flags & KD_BREAKPOINT_PENDING)
232 {
233 /* So we have a valid breakpoint, but it hasn't been used yet... */
234 KdpBreakpointTable[BpIndex].Flags &= ~KD_BREAKPOINT_PENDING;
235 return TRUE;
236 }
237
238 /* Is the original instruction a breakpoint anyway? */
239 if (KdpBreakpointTable[BpIndex].Content == KdpBreakpointInstruction)
240 {
241 /* Then leave it that way... */
242 return TRUE;
243 }
244
245 /* We have an active breakpoint with an instruction to bring back. Do it. */
246 Status = KdpCopyMemoryChunks((ULONG_PTR)KdpBreakpointTable[BpIndex].Address,
247 &KdpBreakpointTable[BpIndex].Content,
248 KD_BREAKPOINT_SIZE,
249 0,
250 MMDBG_COPY_UNSAFE | MMDBG_COPY_WRITE,
251 NULL);
252 if (!NT_SUCCESS(Status))
253 {
254 /* Memory is inaccessible now, restoring original instruction is deferred */
255 // KdpDprintf("Failed to delete breakpoint at address 0x%p\n",
256 // KdpBreakpointTable[BpIndex].Address);
257 KdpBreakpointTable[BpIndex].Flags |= KD_BREAKPOINT_EXPIRED;
258 KdpOweBreakpoint = TRUE;
259 return FALSE;
260 }
261
262 /* Everything went fine, return */
263 return TRUE;
264 }
265
266 BOOLEAN
267 NTAPI
268 KdpLowRestoreBreakpoint(IN ULONG BpIndex)
269 {
270 NTSTATUS Status;
271
272 /* Were we not able to remove it earlier? */
273 if (KdpBreakpointTable[BpIndex].Flags & KD_BREAKPOINT_EXPIRED)
274 {
275 /* Just re-use it! */
276 KdpBreakpointTable[BpIndex].Flags &= ~KD_BREAKPOINT_EXPIRED;
277 return TRUE;
278 }
279
280 /* Are we merely writing a breakpoint on top of another breakpoint? */
281 if (KdpBreakpointTable[BpIndex].Content == KdpBreakpointInstruction)
282 {
283 /* Nothing to do */
284 return TRUE;
285 }
286
287 /* Ok, we actually have to overwrite the instruction now */
288 Status = KdpCopyMemoryChunks((ULONG_PTR)KdpBreakpointTable[BpIndex].Address,
289 &KdpBreakpointInstruction,
290 KD_BREAKPOINT_SIZE,
291 0,
292 MMDBG_COPY_UNSAFE | MMDBG_COPY_WRITE,
293 NULL);
294 if (!NT_SUCCESS(Status))
295 {
296 /* Memory is inaccessible now, restoring breakpoint is deferred */
297 // KdpDprintf("Failed to restore breakpoint at address 0x%p\n",
298 // KdpBreakpointTable[BpIndex].Address);
299 KdpBreakpointTable[BpIndex].Flags |= KD_BREAKPOINT_PENDING;
300 KdpOweBreakpoint = TRUE;
301 return FALSE;
302 }
303
304 /* Clear any possible previous pending flag and return success */
305 KdpBreakpointTable[BpIndex].Flags &= ~KD_BREAKPOINT_PENDING;
306 return TRUE;
307 }
308
309 BOOLEAN
310 NTAPI
311 KdpDeleteBreakpoint(IN ULONG BpEntry)
312 {
313 ULONG BpIndex = BpEntry - 1;
314
315 /* Check for invalid breakpoint entry */
316 if (!BpEntry || (BpEntry > KD_BREAKPOINT_MAX)) return FALSE;
317
318 /* If the specified breakpoint table entry is not valid, then return FALSE. */
319 if (!KdpBreakpointTable[BpIndex].Flags) return FALSE;
320
321 /* Check if the breakpoint is suspended */
322 if (KdpBreakpointTable[BpIndex].Flags & KD_BREAKPOINT_SUSPENDED)
323 {
324 /* Check if breakpoint is not being deleted */
325 if (!(KdpBreakpointTable[BpIndex].Flags & KD_BREAKPOINT_EXPIRED))
326 {
327 /* Invalidate it and return success */
328 KdpBreakpointTable[BpIndex].Flags = 0;
329 return TRUE;
330 }
331 }
332
333 /* Restore original data, then invalidate it and return success */
334 if (KdpLowWriteContent(BpIndex)) KdpBreakpointTable[BpIndex].Flags = 0;
335 return TRUE;
336 }
337
338 BOOLEAN
339 NTAPI
340 KdpDeleteBreakpointRange(IN PVOID Base,
341 IN PVOID Limit)
342 {
343 ULONG BpIndex;
344 BOOLEAN DeletedBreakpoints;
345
346 /* Assume no breakpoints will be deleted */
347 DeletedBreakpoints = FALSE;
348
349 /* Loop the breakpoint table */
350 for (BpIndex = 0; BpIndex < KD_BREAKPOINT_MAX; BpIndex++)
351 {
352 /* Make sure that the breakpoint is active and matches the range. */
353 if ((KdpBreakpointTable[BpIndex].Flags & KD_BREAKPOINT_ACTIVE) &&
354 ((KdpBreakpointTable[BpIndex].Address >= Base) &&
355 (KdpBreakpointTable[BpIndex].Address <= Limit)))
356 {
357 /* Delete it, and remember if we succeeded at least once */
358 if (KdpDeleteBreakpoint(BpIndex + 1)) DeletedBreakpoints = TRUE;
359 }
360 }
361
362 /* Return whether we deleted anything */
363 return DeletedBreakpoints;
364 }
365
366 VOID
367 NTAPI
368 KdpRestoreAllBreakpoints(VOID)
369 {
370 ULONG BpIndex;
371
372 /* No more suspended Breakpoints */
373 BreakpointsSuspended = FALSE;
374
375 /* Loop the breakpoints */
376 for (BpIndex = 0; BpIndex < KD_BREAKPOINT_MAX; BpIndex++)
377 {
378 /* Check if they are valid, suspended breakpoints */
379 if ((KdpBreakpointTable[BpIndex].Flags & KD_BREAKPOINT_ACTIVE) &&
380 (KdpBreakpointTable[BpIndex].Flags & KD_BREAKPOINT_SUSPENDED))
381 {
382 /* Unsuspend them */
383 KdpBreakpointTable[BpIndex].Flags &= ~KD_BREAKPOINT_SUSPENDED;
384 KdpLowRestoreBreakpoint(BpIndex);
385 }
386 }
387 }
388
389 VOID
390 NTAPI
391 KdpSuspendBreakPoint(IN ULONG BpEntry)
392 {
393 ULONG BpIndex = BpEntry - 1;
394
395 /* Check if this is a valid, unsuspended breakpoint */
396 if ((KdpBreakpointTable[BpIndex].Flags & KD_BREAKPOINT_ACTIVE) &&
397 !(KdpBreakpointTable[BpIndex].Flags & KD_BREAKPOINT_SUSPENDED))
398 {
399 /* Suspend it */
400 KdpBreakpointTable[BpIndex].Flags |= KD_BREAKPOINT_SUSPENDED;
401 KdpLowWriteContent(BpIndex);
402 }
403 }
404
405 VOID
406 NTAPI
407 KdpSuspendAllBreakPoints(VOID)
408 {
409 ULONG BpEntry;
410
411 /* Breakpoints are suspended */
412 BreakpointsSuspended = TRUE;
413
414 /* Loop every breakpoint */
415 for (BpEntry = 1; BpEntry <= KD_BREAKPOINT_MAX; BpEntry++)
416 {
417 /* Suspend it */
418 KdpSuspendBreakPoint(BpEntry);
419 }
420 }