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