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 } 421