xref: /reactos/ntoskrnl/kd64/kdbreak.c (revision c2c66aff)
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
KdpAddBreakpoint(IN PVOID Address)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
KdSetOwedBreakpoints(VOID)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
KdpLowWriteContent(IN ULONG BpIndex)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
KdpLowRestoreBreakpoint(IN ULONG BpIndex)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
KdpDeleteBreakpoint(IN ULONG BpEntry)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
KdpDeleteBreakpointRange(IN PVOID Base,IN PVOID Limit)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
KdpRestoreAllBreakpoints(VOID)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
KdpSuspendBreakPoint(IN ULONG BpEntry)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
KdpSuspendAllBreakPoints(VOID)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 }
421