1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Kernel Debugger Initialization 5 * COPYRIGHT: Copyright 2005 Alex Ionescu <alex.ionescu@reactos.org> 6 * Copyright 2020 Hervé Poussineau <hpoussin@reactos.org> 7 * Copyright 2023 Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org> 8 */ 9 10 #include <ntoskrnl.h> 11 #include "kd.h" 12 #include "kdterminal.h" 13 14 #define NDEBUG 15 #include <debug.h> 16 17 #undef KdD0Transition 18 #undef KdD3Transition 19 #undef KdSave 20 #undef KdRestore 21 22 /* PUBLIC FUNCTIONS *********************************************************/ 23 24 static VOID 25 KdpGetTerminalSettings( 26 _In_ PCSTR p1) 27 { 28 #define CONST_STR_LEN(x) (sizeof(x)/sizeof(x[0]) - 1) 29 30 while (p1 && *p1) 31 { 32 /* Skip leading whitespace */ 33 while (*p1 == ' ') ++p1; 34 35 if (!_strnicmp(p1, "KDSERIAL", CONST_STR_LEN("KDSERIAL"))) 36 { 37 p1 += CONST_STR_LEN("KDSERIAL"); 38 KdbDebugState |= KD_DEBUG_KDSERIAL; 39 KdpDebugMode.Serial = TRUE; 40 } 41 else if (!_strnicmp(p1, "KDNOECHO", CONST_STR_LEN("KDNOECHO"))) 42 { 43 p1 += CONST_STR_LEN("KDNOECHO"); 44 KdbDebugState |= KD_DEBUG_KDNOECHO; 45 } 46 47 /* Move on to the next option */ 48 p1 = strchr(p1, ' '); 49 } 50 } 51 52 static PCHAR 53 KdpGetDebugMode( 54 _In_ PCHAR Currentp2) 55 { 56 PCHAR p1, p2 = Currentp2; 57 ULONG Value; 58 59 /* Check for Screen Debugging */ 60 if (!_strnicmp(p2, "SCREEN", 6)) 61 { 62 /* Enable It */ 63 p2 += 6; 64 KdpDebugMode.Screen = TRUE; 65 } 66 /* Check for Serial Debugging */ 67 else if (!_strnicmp(p2, "COM", 3)) 68 { 69 /* Check for a valid Serial Port */ 70 p2 += 3; 71 if (*p2 != ':') 72 { 73 Value = (ULONG)atol(p2); 74 if (Value > 0 && Value < 5) 75 { 76 /* Valid port found, enable Serial Debugging */ 77 KdpDebugMode.Serial = TRUE; 78 79 /* Set the port to use */ 80 SerialPortNumber = Value; 81 } 82 } 83 else 84 { 85 Value = strtoul(p2 + 1, NULL, 0); 86 if (Value) 87 { 88 KdpDebugMode.Serial = TRUE; 89 SerialPortInfo.Address = UlongToPtr(Value); 90 SerialPortNumber = 0; 91 } 92 } 93 } 94 /* Check for Debug Log Debugging */ 95 else if (!_strnicmp(p2, "FILE", 4)) 96 { 97 /* Enable It */ 98 p2 += 4; 99 KdpDebugMode.File = TRUE; 100 if (*p2 == ':') 101 { 102 p2++; 103 p1 = p2; 104 while (*p2 != '\0' && *p2 != ' ') p2++; 105 KdpLogFileName.MaximumLength = KdpLogFileName.Length = p2 - p1; 106 KdpLogFileName.Buffer = p1; 107 } 108 } 109 110 return p2; 111 } 112 113 NTSTATUS 114 NTAPI 115 KdDebuggerInitialize0( 116 _In_opt_ PLOADER_PARAMETER_BLOCK LoaderBlock) 117 { 118 PCHAR CommandLine, Port = NULL; 119 ULONG i; 120 BOOLEAN Success = FALSE; 121 122 if (LoaderBlock) 123 { 124 /* Check if we have a command line */ 125 CommandLine = LoaderBlock->LoadOptions; 126 if (CommandLine) 127 { 128 /* Upcase it */ 129 _strupr(CommandLine); 130 131 /* Get terminal settings */ 132 KdpGetTerminalSettings(CommandLine); 133 134 /* Get the port */ 135 Port = strstr(CommandLine, "DEBUGPORT"); 136 } 137 } 138 139 /* Check if we got the /DEBUGPORT parameter(s) */ 140 while (Port) 141 { 142 /* Move past the actual string, to reach the port*/ 143 Port += sizeof("DEBUGPORT") - 1; 144 145 /* Now get past any spaces and skip the equal sign */ 146 while (*Port == ' ') Port++; 147 Port++; 148 149 /* Get the debug mode and wrapper */ 150 Port = KdpGetDebugMode(Port); 151 Port = strstr(Port, "DEBUGPORT"); 152 } 153 154 /* Use serial port then */ 155 if (KdpDebugMode.Value == 0) 156 KdpDebugMode.Serial = TRUE; 157 158 /* Call the providers at Phase 0 */ 159 for (i = 0; i < RTL_NUMBER_OF(DispatchTable); i++) 160 { 161 DispatchTable[i].InitStatus = InitRoutines[i](&DispatchTable[i], 0); 162 Success = (Success || NT_SUCCESS(DispatchTable[i].InitStatus)); 163 } 164 165 /* Return success if at least one of the providers succeeded */ 166 return (Success ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL); 167 } 168 169 170 /** 171 * @brief Reinitialization routine. 172 * DRIVER_REINITIALIZE 173 * 174 * Calls each registered provider for reinitialization at Phase >= 2. 175 * I/O is now set up for disk access, at different phases. 176 **/ 177 static VOID 178 NTAPI 179 KdpDriverReinit( 180 _In_ PDRIVER_OBJECT DriverObject, 181 _In_opt_ PVOID Context, 182 _In_ ULONG Count) 183 { 184 PLIST_ENTRY CurrentEntry; 185 PKD_DISPATCH_TABLE CurrentTable; 186 PKDP_INIT_ROUTINE KdpInitRoutine; 187 ULONG BootPhase = (Count + 1); // Do BootPhase >= 2 188 BOOLEAN ScheduleReinit = FALSE; 189 190 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); 191 192 DPRINT("*** KD %sREINITIALIZATION - Phase %d ***\n", 193 Context ? "" : "BOOT ", BootPhase); 194 195 /* Call the registered providers */ 196 for (CurrentEntry = KdProviders.Flink; 197 CurrentEntry != &KdProviders; NOTHING) 198 { 199 /* Get the provider */ 200 CurrentTable = CONTAINING_RECORD(CurrentEntry, 201 KD_DISPATCH_TABLE, 202 KdProvidersList); 203 /* Go to the next entry (the Init routine may unlink us) */ 204 CurrentEntry = CurrentEntry->Flink; 205 206 /* Call it if it requires a reinitialization */ 207 if (CurrentTable->KdpInitRoutine) 208 { 209 /* Get the initialization routine and reset it */ 210 KdpInitRoutine = CurrentTable->KdpInitRoutine; 211 CurrentTable->KdpInitRoutine = NULL; 212 CurrentTable->InitStatus = KdpInitRoutine(CurrentTable, BootPhase); 213 DPRINT("KdpInitRoutine(%p) returned 0x%08lx\n", 214 CurrentTable, CurrentTable->InitStatus); 215 216 /* Check whether it needs to be reinitialized again */ 217 ScheduleReinit = (ScheduleReinit || CurrentTable->KdpInitRoutine); 218 } 219 } 220 221 DPRINT("ScheduleReinit: %s\n", ScheduleReinit ? "TRUE" : "FALSE"); 222 if (ScheduleReinit) 223 { 224 /* 225 * Determine when to reinitialize. 226 * If Context == NULL, we are doing a boot-driver reinitialization. 227 * It is initially done once (Count == 1), and is rescheduled once 228 * after all other boot drivers get loaded (Count == 2). 229 * If further reinitialization is needed, switch to system-driver 230 * reinitialization and do it again, not more than twice. 231 */ 232 if (Count <= 1) 233 { 234 IoRegisterBootDriverReinitialization(DriverObject, 235 KdpDriverReinit, 236 (PVOID)FALSE); 237 } 238 else if (Count <= 3) 239 { 240 IoRegisterDriverReinitialization(DriverObject, 241 KdpDriverReinit, 242 (PVOID)TRUE); 243 } 244 else 245 { 246 /* Too late, no more reinitializations! */ 247 DPRINT("Cannot reinitialize anymore!\n"); 248 ScheduleReinit = FALSE; 249 } 250 } 251 252 if (!ScheduleReinit) 253 { 254 /* All the necessary reinitializations are done, 255 * the driver object is not needed anymore. */ 256 ObMakeTemporaryObject(DriverObject); 257 IoDeleteDriver(DriverObject); 258 } 259 } 260 261 /** 262 * @brief Entry point for the auxiliary driver. 263 * DRIVER_INITIALIZE 264 **/ 265 static NTSTATUS 266 NTAPI 267 KdpDriverEntry( 268 _In_ PDRIVER_OBJECT DriverObject, 269 _In_ PUNICODE_STRING RegistryPath) 270 { 271 UNREFERENCED_PARAMETER(RegistryPath); 272 273 /* Register for reinitialization after the other drivers are loaded */ 274 IoRegisterBootDriverReinitialization(DriverObject, 275 KdpDriverReinit, 276 (PVOID)FALSE); 277 278 /* Set the driver as initialized */ 279 DriverObject->Flags |= DRVO_INITIALIZED; 280 return STATUS_SUCCESS; 281 } 282 283 /** 284 * @brief Hooked HalInitPnpDriver() callback. 285 * It is initially set by the HAL when HalInitSystem(0, ...) 286 * is called earlier on. 287 **/ 288 static pHalInitPnpDriver orgHalInitPnpDriver = NULL; 289 290 /** 291 * @brief 292 * HalInitPnpDriver() callback hook installed by KdDebuggerInitialize1(). 293 * 294 * It is called during initialization of the I/O manager and is where 295 * the auxiliary driver is created. This driver is needed for receiving 296 * reinitialization callbacks in KdpDriverReinit() later. 297 * This hook must *always* call the original HalInitPnpDriver() function 298 * and return its returned value, or return STATUS_SUCCESS. 299 **/ 300 static NTSTATUS 301 NTAPI 302 KdpInitDriver(VOID) 303 { 304 static BOOLEAN InitCalled = FALSE; 305 NTSTATUS Status; 306 UNICODE_STRING DriverName = RTL_CONSTANT_STRING(L"\\Driver\\KdDriver"); 307 308 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); 309 310 /* Ensure we are not called more than once */ 311 if (_InterlockedCompareExchange8((char*)&InitCalled, TRUE, FALSE) != FALSE) 312 return STATUS_SUCCESS; 313 314 /* Create the driver */ 315 Status = IoCreateDriver(&DriverName, KdpDriverEntry); 316 if (!NT_SUCCESS(Status)) 317 DPRINT1("IoCreateDriver failed: 0x%08lx\n", Status); 318 /* Ignore any failure from IoCreateDriver(). If it fails, no I/O-related 319 * initialization will happen (no file log debugging, etc.). */ 320 321 /* Finally, restore and call the original HalInitPnpDriver() */ 322 InterlockedExchangePointer((PVOID*)&HalInitPnpDriver, orgHalInitPnpDriver); 323 return (HalInitPnpDriver ? HalInitPnpDriver() : STATUS_SUCCESS); 324 } 325 326 NTSTATUS 327 NTAPI 328 KdDebuggerInitialize1( 329 _In_opt_ PLOADER_PARAMETER_BLOCK LoaderBlock) 330 { 331 PLIST_ENTRY CurrentEntry; 332 PKD_DISPATCH_TABLE CurrentTable; 333 PKDP_INIT_ROUTINE KdpInitRoutine; 334 BOOLEAN Success = FALSE; 335 BOOLEAN ReinitForPhase2 = FALSE; 336 337 /* Make space for the displayed providers' signons */ 338 HalDisplayString("\r\n"); 339 340 /* Call the registered providers */ 341 for (CurrentEntry = KdProviders.Flink; 342 CurrentEntry != &KdProviders; NOTHING) 343 { 344 /* Get the provider */ 345 CurrentTable = CONTAINING_RECORD(CurrentEntry, 346 KD_DISPATCH_TABLE, 347 KdProvidersList); 348 /* Go to the next entry (the Init routine may unlink us) */ 349 CurrentEntry = CurrentEntry->Flink; 350 351 /* Get the initialization routine and reset it */ 352 ASSERT(CurrentTable->KdpInitRoutine); 353 KdpInitRoutine = CurrentTable->KdpInitRoutine; 354 CurrentTable->KdpInitRoutine = NULL; 355 356 /* Call it */ 357 CurrentTable->InitStatus = KdpInitRoutine(CurrentTable, 1); 358 359 /* Check whether it needs to be reinitialized for Phase 2 */ 360 Success = (Success || NT_SUCCESS(CurrentTable->InitStatus)); 361 ReinitForPhase2 = (ReinitForPhase2 || CurrentTable->KdpInitRoutine); 362 } 363 364 /* Make space for the displayed providers' signons */ 365 HalDisplayString("\r\n"); 366 367 NtGlobalFlag |= FLG_STOP_ON_EXCEPTION; 368 369 /* If we don't need to reinitialize providers for Phase 2, we are done */ 370 if (!ReinitForPhase2) 371 { 372 /* Return success if at least one of them succeeded */ 373 return (Success ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL); 374 } 375 376 /** 377 * We want to be able to perform I/O-related initialization (starting a 378 * logger thread for file log debugging, loading KDBinit file for KDBG, 379 * etc.). A good place for this would be as early as possible, once the 380 * I/O Manager has started the storage and the boot filesystem drivers. 381 * 382 * Here is an overview of the initialization steps of the NT Kernel and 383 * Executive: 384 * ---- 385 * KiSystemStartup(KeLoaderBlock) 386 * if (Cpu == 0) KdInitSystem(0, KeLoaderBlock); 387 * KiSwitchToBootStack() -> KiSystemStartupBootStack() 388 * -> KiInitializeKernel() -> ExpInitializeExecutive(Cpu, KeLoaderBlock) 389 * 390 * (NOTE: Any unexpected debugger break will call KdInitSystem(0, NULL); ) 391 * KdInitSystem(0, LoaderBlock) -> KdDebuggerInitialize0(LoaderBlock); 392 * 393 * ExpInitializeExecutive(Cpu == 0): ExpInitializationPhase = 0; 394 * HalInitSystem(0, KeLoaderBlock); <-- Sets HalInitPnpDriver callback. 395 * ... 396 * PsInitSystem(LoaderBlock) 397 * PsCreateSystemThread(Phase1Initialization) 398 * 399 * Phase1Initialization(Discard): ExpInitializationPhase = 1; 400 * HalInitSystem(1, KeLoaderBlock); 401 * ... 402 * Early initialization of Ob, Ex, Ke. 403 * KdInitSystem(1, KeLoaderBlock); 404 * ... 405 * KdDebuggerInitialize1(LoaderBlock); 406 * ... 407 * IoInitSystem(LoaderBlock); 408 * ... 409 * ---- 410 * As we can see, KdDebuggerInitialize1() is the last KD initialization 411 * routine the kernel calls, and is called *before* the I/O Manager starts. 412 * Thus, direct Nt/ZwCreateFile ... calls done there would fail. Also, 413 * we want to do the I/O initialization as soon as possible. There does 414 * not seem to be any exported way to be notified about the I/O manager 415 * initialization steps... that is, unless we somehow become a driver and 416 * insert ourselves in the flow! 417 * 418 * Since we are not a regular driver, we need to invoke IoCreateDriver() 419 * to create one. However, remember that we are currently running *before* 420 * IoInitSystem(), the I/O subsystem is not initialized yet. Due to this, 421 * calling IoCreateDriver(), much like any other IO functions, would lead 422 * to a crash, because it calls 423 * ObCreateObject(..., IoDriverObjectType, ...), and IoDriverObjectType 424 * is non-initialized yet (it's NULL). 425 * 426 * The chosen solution is to hook a "known" exported callback: namely, the 427 * HalInitPnpDriver() callback (it initializes the "HAL Root Bus Driver"). 428 * It is set very early on by the HAL via the HalInitSystem(0, ...) call, 429 * and is called early on by IoInitSystem() before any driver is loaded, 430 * but after the I/O Manager has been minimally set up so that new drivers 431 * can be created. 432 * When the hook: KdpInitDriver() is called, we create our driver with 433 * IoCreateDriver(), specifying its entrypoint KdpDriverEntry(), then 434 * restore and call the original HalInitPnpDriver() callback. 435 * 436 * Another possible unexplored alternative, could be to insert ourselves 437 * in the KeLoaderBlock->LoadOrderListHead boot modules list, or in the 438 * KeLoaderBlock->BootDriverListHead boot-driver list. (Note that while 439 * we may be able to do this, because boot-drivers are resident in memory, 440 * much like we are, we cannot insert ourselves in the system-driver list 441 * however, since those drivers are expected to come from PE image files.) 442 * 443 * Once the KdpDriverEntry() driver entrypoint is called, we register 444 * KdpDriverReinit() for re-initialization with the I/O Manager, in order 445 * to provide more initialization points. KdpDriverReinit() calls the KD 446 * providers at BootPhase >= 2, and schedules further reinitializations 447 * (at most 3 more) if any of the providers request so. 448 **/ 449 orgHalInitPnpDriver = 450 InterlockedExchangePointer((PVOID*)&HalInitPnpDriver, KdpInitDriver); 451 return STATUS_SUCCESS; 452 } 453 454 NTSTATUS 455 NTAPI 456 KdD0Transition(VOID) 457 { 458 /* Nothing to do */ 459 return STATUS_SUCCESS; 460 } 461 462 NTSTATUS 463 NTAPI 464 KdD3Transition(VOID) 465 { 466 /* Nothing to do */ 467 return STATUS_SUCCESS; 468 } 469 470 NTSTATUS 471 NTAPI 472 KdSave( 473 _In_ BOOLEAN SleepTransition) 474 { 475 /* Nothing to do */ 476 return STATUS_SUCCESS; 477 } 478 479 NTSTATUS 480 NTAPI 481 KdRestore( 482 _In_ BOOLEAN SleepTransition) 483 { 484 /* Nothing to do */ 485 return STATUS_SUCCESS; 486 } 487 488 /* EOF */ 489