[NTOSKRNL] Add a raw implementation of !irpfind in kdbg
[reactos.git] / ntoskrnl / kd64 / kdbreak.c
index 6b3aa54..b1ac2da 100644 (file)
@@ -23,18 +23,18 @@ KdpAddBreakpoint(IN PVOID Address)
     ULONG i;
     NTSTATUS Status;
 
-    /* Loop current breakpoints */
+    /* Check whether we are not setting a breakpoint twice */
     for (i = 0; i < KD_BREAKPOINT_MAX; i++)
     {
         /* Check if the breakpoint is valid */
-        if ((KdpBreakpointTable[i].Flags & KdpBreakpointActive) &&
+        if ((KdpBreakpointTable[i].Flags & KD_BREAKPOINT_ACTIVE) &&
             (KdpBreakpointTable[i].Address == Address))
         {
-            /* Check if it's pending */
-            if ((KdpBreakpointTable[i].Flags & KdpBreakpointPending))
+            /* Were we not able to remove it earlier? */
+            if (KdpBreakpointTable[i].Flags & KD_BREAKPOINT_EXPIRED)
             {
-                /* It's not pending anymore now */
-                KdpBreakpointTable[i].Flags &= ~KdpBreakpointPending;
+                /* Just re-use it! */
+                KdpBreakpointTable[i].Flags &= ~KD_BREAKPOINT_EXPIRED;
                 return i + 1;
             }
             else
@@ -46,48 +46,181 @@ KdpAddBreakpoint(IN PVOID Address)
     }
 
     /* Find a free entry */
-    for (i = 0; i < KD_BREAKPOINT_MAX; i++) if (!(KdpBreakpointTable[i].Flags)) break;
+    for (i = 0; i < KD_BREAKPOINT_MAX; i++)
+    {
+        if (KdpBreakpointTable[i].Flags == 0)
+            break;
+    }
 
     /* Fail if no free entry was found */
     if (i == KD_BREAKPOINT_MAX) return 0;
 
-    /* Save the old instruction */
+    /* Save the breakpoint */
+    KdpBreakpointTable[i].Address = Address;
+
+    /* If we are setting the breakpoint in user space, save the active process context */
+    if (Address < KD_HIGHEST_USER_BREAKPOINT_ADDRESS)
+        KdpBreakpointTable[i].DirectoryTableBase = KeGetCurrentThread()->ApcState.Process->DirectoryTableBase[0];
+
+    /* Try to save the old instruction */
     Status = KdpCopyMemoryChunks((ULONG_PTR)Address,
                                  &Content,
                                  KD_BREAKPOINT_SIZE,
                                  0,
                                  MMDBG_COPY_UNSAFE,
                                  NULL);
-
-    if (!NT_SUCCESS(Status))
+    if (NT_SUCCESS(Status))
     {
-        /* TODO: Set it as a owed breakpoint */
-        KdpDprintf("Failed to set breakpoint at address 0x%p\n", Address);
-        return 0;
+        /* Memory accessible, set the breakpoint */
+        KdpBreakpointTable[i].Content = Content;
+        KdpBreakpointTable[i].Flags   = KD_BREAKPOINT_ACTIVE;
+
+        /* Write the breakpoint */
+        Status = KdpCopyMemoryChunks((ULONG_PTR)Address,
+                                     &KdpBreakpointInstruction,
+                                     KD_BREAKPOINT_SIZE,
+                                     0,
+                                     MMDBG_COPY_UNSAFE | MMDBG_COPY_WRITE,
+                                     NULL);
+        if (!NT_SUCCESS(Status))
+        {
+            /* This should never happen */
+            KdpDprintf("Unable to write breakpoint to address 0x%p\n", Address);
+        }
     }
-
-    /* Write the entry */
-    KdpBreakpointTable[i].Address = Address;
-    KdpBreakpointTable[i].Content = Content;
-    KdpBreakpointTable[i].Flags = KdpBreakpointActive;
-
-    /* Write the breakpoint */
-    Status = KdpCopyMemoryChunks((ULONG_PTR)Address,
-                                 &KdpBreakpointInstruction,
-                                 KD_BREAKPOINT_SIZE,
-                                 0,
-                                 MMDBG_COPY_UNSAFE | MMDBG_COPY_WRITE,
-                                 NULL);
-    if (!NT_SUCCESS(Status))
+    else
     {
-        /* This should never happen */
-        KdpDprintf("Unable to write breakpoint to address 0x%p\n", Address);
+        /* Memory is inaccessible now, setting breakpoint is deferred */
+        KdpDprintf("Failed to set breakpoint at address 0x%p, adding deferred breakpoint.\n", Address);
+        KdpBreakpointTable[i].Flags = KD_BREAKPOINT_ACTIVE | KD_BREAKPOINT_PENDING;
+        KdpOweBreakpoint = TRUE;
     }
 
     /* Return the breakpoint handle */
     return i + 1;
 }
 
+VOID
+NTAPI
+KdSetOwedBreakpoints(VOID)
+{
+    BOOLEAN Enable;
+    KD_BREAKPOINT_TYPE Content;
+    ULONG i;
+    NTSTATUS Status;
+
+    /* If we don't owe any breakpoints, just return */
+    if (!KdpOweBreakpoint) return;
+
+    /* Enter the debugger */
+    Enable = KdEnterDebugger(NULL, NULL);
+
+    /*
+     * Suppose we succeed in setting all the breakpoints.
+     * If we fail to do so, the flag will be set again.
+     */
+    KdpOweBreakpoint = FALSE;
+
+    /* Loop through current breakpoints and try to set or delete the pending ones */
+    for (i = 0; i < KD_BREAKPOINT_MAX; i++)
+    {
+        if (KdpBreakpointTable[i].Flags & (KD_BREAKPOINT_PENDING | KD_BREAKPOINT_EXPIRED))
+        {
+            /*
+             * Set the breakpoint only if it is in kernel space, or if it is
+             * in user space and the active process context matches.
+             */
+            if (KdpBreakpointTable[i].Address < KD_HIGHEST_USER_BREAKPOINT_ADDRESS &&
+                KdpBreakpointTable[i].DirectoryTableBase != KeGetCurrentThread()->ApcState.Process->DirectoryTableBase[0])
+            {
+                KdpOweBreakpoint = TRUE;
+                continue;
+            }
+
+            /* Try to save the old instruction */
+            Status = KdpCopyMemoryChunks((ULONG_PTR)KdpBreakpointTable[i].Address,
+                                         &Content,
+                                         KD_BREAKPOINT_SIZE,
+                                         0,
+                                         MMDBG_COPY_UNSAFE,
+                                         NULL);
+            if (!NT_SUCCESS(Status))
+            {
+                /* Memory is still inaccessible, breakpoint setting will be deferred again */
+                // KdpDprintf("Failed to set deferred breakpoint at address 0x%p\n",
+                //            KdpBreakpointTable[i].Address);
+                KdpOweBreakpoint = TRUE;
+                continue;
+            }
+
+            /* Check if we need to write the breakpoint */
+            if (KdpBreakpointTable[i].Flags & KD_BREAKPOINT_PENDING)
+            {
+                /* Memory accessible, set the breakpoint */
+                KdpBreakpointTable[i].Content = Content;
+
+                /* Write the breakpoint */
+                Status = KdpCopyMemoryChunks((ULONG_PTR)KdpBreakpointTable[i].Address,
+                                             &KdpBreakpointInstruction,
+                                             KD_BREAKPOINT_SIZE,
+                                             0,
+                                             MMDBG_COPY_UNSAFE | MMDBG_COPY_WRITE,
+                                             NULL);
+                if (!NT_SUCCESS(Status))
+                {
+                    /* This should never happen */
+                    KdpDprintf("Unable to write deferred breakpoint to address 0x%p\n",
+                               KdpBreakpointTable[i].Address);
+                    KdpOweBreakpoint = TRUE;
+                }
+                else
+                {
+                    KdpBreakpointTable[i].Flags = KD_BREAKPOINT_ACTIVE;
+                }
+
+                continue;
+            }
+
+            /* Check if we need to restore the original instruction */
+            if (KdpBreakpointTable[i].Flags & KD_BREAKPOINT_EXPIRED)
+            {
+                /* Write it back */
+                Status = KdpCopyMemoryChunks((ULONG_PTR)KdpBreakpointTable[i].Address,
+                                             &KdpBreakpointTable[i].Content,
+                                             KD_BREAKPOINT_SIZE,
+                                             0,
+                                             MMDBG_COPY_UNSAFE | MMDBG_COPY_WRITE,
+                                             NULL);
+                if (!NT_SUCCESS(Status))
+                {
+                    /* This should never happen */
+                    KdpDprintf("Unable to delete deferred breakpoint at address 0x%p\n",
+                               KdpBreakpointTable[i].Address);
+                    KdpOweBreakpoint = TRUE;
+                }
+                else
+                {
+                    /* Check if the breakpoint is suspended */
+                    if (KdpBreakpointTable[i].Flags & KD_BREAKPOINT_SUSPENDED)
+                    {
+                        KdpBreakpointTable[i].Flags = KD_BREAKPOINT_SUSPENDED | KD_BREAKPOINT_ACTIVE;
+                    }
+                    else
+                    {
+                        /* Invalidate it */
+                        KdpBreakpointTable[i].Flags = 0;
+                    }
+                }
+
+                continue;
+            }
+        }
+    }
+
+    /* Exit the debugger */
+    KdExitDebugger(Enable);
+}
+
 BOOLEAN
 NTAPI
 KdpLowWriteContent(IN ULONG BpIndex)
@@ -95,10 +228,10 @@ KdpLowWriteContent(IN ULONG BpIndex)
     NTSTATUS Status;
 
     /* Make sure that the breakpoint is actually active */
-    if (KdpBreakpointTable[BpIndex].Flags & KdpBreakpointPending)
+    if (KdpBreakpointTable[BpIndex].Flags & KD_BREAKPOINT_PENDING)
     {
         /* So we have a valid breakpoint, but it hasn't been used yet... */
-        KdpBreakpointTable[BpIndex].Flags &= ~KdpBreakpointPending;
+        KdpBreakpointTable[BpIndex].Flags &= ~KD_BREAKPOINT_PENDING;
         return TRUE;
     }
 
@@ -110,8 +243,7 @@ KdpLowWriteContent(IN ULONG BpIndex)
     }
 
     /* We have an active breakpoint with an instruction to bring back. Do it. */
-    Status = KdpCopyMemoryChunks((ULONG_PTR)KdpBreakpointTable[BpIndex].
-                                 Address,
+    Status = KdpCopyMemoryChunks((ULONG_PTR)KdpBreakpointTable[BpIndex].Address,
                                  &KdpBreakpointTable[BpIndex].Content,
                                  KD_BREAKPOINT_SIZE,
                                  0,
@@ -119,9 +251,11 @@ KdpLowWriteContent(IN ULONG BpIndex)
                                  NULL);
     if (!NT_SUCCESS(Status))
     {
-        /* TODO: Set it as a owed breakpoint */
-        KdpDprintf("Failed to delete breakpoint at address 0x%p\n",
-                   KdpBreakpointTable[BpIndex].Address);
+        /* Memory is inaccessible now, restoring original instruction is deferred */
+        // KdpDprintf("Failed to delete breakpoint at address 0x%p\n",
+        //            KdpBreakpointTable[BpIndex].Address);
+        KdpBreakpointTable[BpIndex].Flags |= KD_BREAKPOINT_EXPIRED;
+        KdpOweBreakpoint = TRUE;
         return FALSE;
     }
 
@@ -136,23 +270,22 @@ KdpLowRestoreBreakpoint(IN ULONG BpIndex)
     NTSTATUS Status;
 
     /* Were we not able to remove it earlier? */
-    if (KdpBreakpointTable[BpIndex].Flags & KdpBreakpointExpired)
+    if (KdpBreakpointTable[BpIndex].Flags & KD_BREAKPOINT_EXPIRED)
     {
-        /* Well then, we'll just re-use it and return success! */
-        KdpBreakpointTable[BpIndex].Flags &= ~KdpBreakpointExpired;
+        /* Just re-use it! */
+        KdpBreakpointTable[BpIndex].Flags &= ~KD_BREAKPOINT_EXPIRED;
         return TRUE;
     }
 
     /* Are we merely writing a breakpoint on top of another breakpoint? */
     if (KdpBreakpointTable[BpIndex].Content == KdpBreakpointInstruction)
     {
-        /* Nothing to do then... */
+        /* Nothing to do */
         return TRUE;
     }
 
     /* Ok, we actually have to overwrite the instruction now */
-    Status = KdpCopyMemoryChunks((ULONG_PTR)KdpBreakpointTable[BpIndex].
-                                 Address,
+    Status = KdpCopyMemoryChunks((ULONG_PTR)KdpBreakpointTable[BpIndex].Address,
                                  &KdpBreakpointInstruction,
                                  KD_BREAKPOINT_SIZE,
                                  0,
@@ -160,14 +293,16 @@ KdpLowRestoreBreakpoint(IN ULONG BpIndex)
                                  NULL);
     if (!NT_SUCCESS(Status))
     {
-        /* FIXME: Set it as a owed breakpoint */
-        KdpDprintf("Failed to restore breakpoint at address 0x%p\n",
-                   KdpBreakpointTable[BpIndex].Address);
+        /* Memory is inaccessible now, restoring breakpoint is deferred */
+        // KdpDprintf("Failed to restore breakpoint at address 0x%p\n",
+        //            KdpBreakpointTable[BpIndex].Address);
+        KdpBreakpointTable[BpIndex].Flags |= KD_BREAKPOINT_PENDING;
+        KdpOweBreakpoint = TRUE;
         return FALSE;
     }
 
     /* Clear any possible previous pending flag and return success */
-    KdpBreakpointTable[BpIndex].Flags &= ~KdpBreakpointPending;
+    KdpBreakpointTable[BpIndex].Flags &= ~KD_BREAKPOINT_PENDING;
     return TRUE;
 }
 
@@ -178,16 +313,16 @@ KdpDeleteBreakpoint(IN ULONG BpEntry)
     ULONG BpIndex = BpEntry - 1;
 
     /* Check for invalid breakpoint entry */
-    if (!(BpEntry) || (BpEntry > KD_BREAKPOINT_MAX)) return FALSE;
+    if (!BpEntry || (BpEntry > KD_BREAKPOINT_MAX)) return FALSE;
 
     /* If the specified breakpoint table entry is not valid, then return FALSE. */
     if (!KdpBreakpointTable[BpIndex].Flags) return FALSE;
 
     /* Check if the breakpoint is suspended */
-    if (KdpBreakpointTable[BpIndex].Flags & KdpBreakpointSuspended)
+    if (KdpBreakpointTable[BpIndex].Flags & KD_BREAKPOINT_SUSPENDED)
     {
-        /* Check if breakpoint is not ...? */
-        if (!(KdpBreakpointTable[BpIndex].Flags & KdpBreakpointExpired))
+        /* Check if breakpoint is not being deleted */
+        if (!(KdpBreakpointTable[BpIndex].Flags & KD_BREAKPOINT_EXPIRED))
         {
             /* Invalidate it and return success */
             KdpBreakpointTable[BpIndex].Flags = 0;
@@ -206,23 +341,26 @@ KdpDeleteBreakpointRange(IN PVOID Base,
                          IN PVOID Limit)
 {
     ULONG BpIndex;
-    BOOLEAN Return = FALSE;
+    BOOLEAN DeletedBreakpoints;
+
+    /* Assume no breakpoints will be deleted */
+    DeletedBreakpoints = FALSE;
 
     /* Loop the breakpoint table */
     for (BpIndex = 0; BpIndex < KD_BREAKPOINT_MAX; BpIndex++)
     {
         /* Make sure that the breakpoint is active and matches the range. */
-        if ((KdpBreakpointTable[BpIndex].Flags & KdpBreakpointActive) &&
+        if ((KdpBreakpointTable[BpIndex].Flags & KD_BREAKPOINT_ACTIVE) &&
             ((KdpBreakpointTable[BpIndex].Address >= Base) &&
              (KdpBreakpointTable[BpIndex].Address <= Limit)))
         {
-            /* Delete it */
-            Return = Return || KdpDeleteBreakpoint(BpIndex + 1);
+            /* Delete it, and remember if we succeeded at least once */
+            if (KdpDeleteBreakpoint(BpIndex + 1)) DeletedBreakpoints = TRUE;
         }
     }
 
-    /* Return to caller */
-    return Return;
+    /* Return whether we deleted anything */
+    return DeletedBreakpoints;
 }
 
 VOID
@@ -238,11 +376,11 @@ KdpRestoreAllBreakpoints(VOID)
     for (BpIndex = 0; BpIndex < KD_BREAKPOINT_MAX; BpIndex++)
     {
         /* Check if they are valid, suspended breakpoints */
-        if ((KdpBreakpointTable[BpIndex].Flags & KdpBreakpointActive) &&
-            (KdpBreakpointTable[BpIndex].Flags & KdpBreakpointSuspended))
+        if ((KdpBreakpointTable[BpIndex].Flags & KD_BREAKPOINT_ACTIVE) &&
+            (KdpBreakpointTable[BpIndex].Flags & KD_BREAKPOINT_SUSPENDED))
         {
             /* Unsuspend them */
-            KdpBreakpointTable[BpIndex].Flags &= ~KdpBreakpointSuspended;
+            KdpBreakpointTable[BpIndex].Flags &= ~KD_BREAKPOINT_SUSPENDED;
             KdpLowRestoreBreakpoint(BpIndex);
         }
     }
@@ -255,11 +393,11 @@ KdpSuspendBreakPoint(IN ULONG BpEntry)
     ULONG BpIndex = BpEntry - 1;
 
     /* Check if this is a valid, unsuspended breakpoint */
-    if ((KdpBreakpointTable[BpIndex].Flags & KdpBreakpointActive) &&
-        !(KdpBreakpointTable[BpIndex].Flags & KdpBreakpointSuspended))
+    if ((KdpBreakpointTable[BpIndex].Flags & KD_BREAKPOINT_ACTIVE) &&
+        !(KdpBreakpointTable[BpIndex].Flags & KD_BREAKPOINT_SUSPENDED))
     {
         /* Suspend it */
-        KdpBreakpointTable[BpIndex].Flags |= KdpBreakpointSuspended;
+        KdpBreakpointTable[BpIndex].Flags |= KD_BREAKPOINT_SUSPENDED;
         KdpLowWriteContent(BpIndex);
     }
 }