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
KdpGetTerminalSettings(_In_ PCSTR p1)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
KdpGetDebugMode(_In_ PCHAR Currentp2)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
KdDebuggerInitialize0(_In_opt_ PLOADER_PARAMETER_BLOCK LoaderBlock)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
KdpDriverReinit(_In_ PDRIVER_OBJECT DriverObject,_In_opt_ PVOID Context,_In_ ULONG Count)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
KdpDriverEntry(_In_ PDRIVER_OBJECT DriverObject,_In_ PUNICODE_STRING RegistryPath)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
KdpInitDriver(VOID)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
KdDebuggerInitialize1(_In_opt_ PLOADER_PARAMETER_BLOCK LoaderBlock)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
KdD0Transition(VOID)456 KdD0Transition(VOID)
457 {
458 /* Nothing to do */
459 return STATUS_SUCCESS;
460 }
461
462 NTSTATUS
463 NTAPI
KdD3Transition(VOID)464 KdD3Transition(VOID)
465 {
466 /* Nothing to do */
467 return STATUS_SUCCESS;
468 }
469
470 NTSTATUS
471 NTAPI
KdSave(_In_ BOOLEAN SleepTransition)472 KdSave(
473 _In_ BOOLEAN SleepTransition)
474 {
475 /* Nothing to do */
476 return STATUS_SUCCESS;
477 }
478
479 NTSTATUS
480 NTAPI
KdRestore(_In_ BOOLEAN SleepTransition)481 KdRestore(
482 _In_ BOOLEAN SleepTransition)
483 {
484 /* Nothing to do */
485 return STATUS_SUCCESS;
486 }
487
488 /* EOF */
489