xref: /reactos/ntoskrnl/kd/kdmain.c (revision cce399e7)
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