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