xref: /reactos/ntoskrnl/kd64/kdinit.c (revision c53eb190)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/kd64/kdinit.c
5  * PURPOSE:         KD64 Initialization Code
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  *                  Stefan Ginsberg (stefan.ginsberg@reactos.org)
8  */
9 
10 /* INCLUDES ******************************************************************/
11 
12 #include <ntoskrnl.h>
13 #include <reactos/buildno.h>
14 
15 #define NDEBUG
16 #include <debug.h>
17 
18 /*
19  * Override DbgPrint(), used by the debugger banner DPRINTs below,
20  * because KdInitSystem() can be called under the debugger lock by
21  * KdEnableDebugger(WithLock)().
22  */
23 #define DbgPrint(fmt, ...) (KdpDprintf(fmt, ##__VA_ARGS__), 0)
24 #define DbgPrintEx(cmpid, lvl, fmt, ...) (KdpDprintf(fmt, ##__VA_ARGS__), 0)
25 
26 /* UTILITY FUNCTIONS *********************************************************/
27 
28 #include <mm/ARM3/miarm.h> // For MiIsMemoryTypeInvisible()
29 
30 /**
31  * @brief
32  * Retrieves the total size of the memory before Mm is initialized,
33  * by counting the number of physical pages. Useful for debug logging.
34  *
35  * Adapted from mm/ARM3/mminit.c!MiScanMemoryDescriptors().
36  **/
37 static
38 SIZE_T
KdpGetMemorySizeInMBs(_In_opt_ PLOADER_PARAMETER_BLOCK LoaderBlock)39 KdpGetMemorySizeInMBs(
40     _In_opt_ PLOADER_PARAMETER_BLOCK LoaderBlock)
41 {
42     PLIST_ENTRY ListEntry;
43     PMEMORY_ALLOCATION_DESCRIPTOR Descriptor;
44     SIZE_T NumberOfPhysicalPages = 0;
45 
46     /*
47      * If no loader block is present (e.g. the debugger is initialized only
48      * much later after boot), just use the already-initialized Mm-computed
49      * number of physical pages. Otherwise do the evaluation ourselves.
50      */
51     if (!LoaderBlock)
52     {
53         NumberOfPhysicalPages = MmNumberOfPhysicalPages;
54         goto ReturnSize;
55     }
56 
57     /* Loop the memory descriptors */
58     for (ListEntry = LoaderBlock->MemoryDescriptorListHead.Flink;
59          ListEntry != &LoaderBlock->MemoryDescriptorListHead;
60          ListEntry = ListEntry->Flink)
61     {
62         /* Get the descriptor */
63         Descriptor = CONTAINING_RECORD(ListEntry,
64                                        MEMORY_ALLOCATION_DESCRIPTOR,
65                                        ListEntry);
66 
67         /* If this is invisible memory, skip this descriptor */
68         if (MiIsMemoryTypeInvisible(Descriptor->MemoryType))
69             continue;
70 
71         /* Check if this isn't bad memory */
72         if (Descriptor->MemoryType != LoaderBad)
73         {
74             /* Count it in the physical pages */
75             NumberOfPhysicalPages += Descriptor->PageCount;
76         }
77     }
78 
79 ReturnSize:
80     /* Round size up. Assumed to better match actual physical RAM size */
81     return ALIGN_UP_BY(NumberOfPhysicalPages * PAGE_SIZE, 1024 * 1024) / (1024 * 1024);
82 }
83 
84 /**
85  * @brief
86  * Displays the kernel debugger initialization banner.
87  **/
88 static
89 VOID
KdpPrintBanner(VOID)90 KdpPrintBanner(VOID)
91 {
92     SIZE_T MemSizeMBs = KdpGetMemorySizeInMBs(KeLoaderBlock);
93 
94     DPRINT1("-----------------------------------------------------\n");
95     DPRINT1("ReactOS " KERNEL_VERSION_STR " (Build " KERNEL_VERSION_BUILD_STR ") (Commit " KERNEL_VERSION_COMMIT_HASH ")\n");
96     DPRINT1("%u System Processor [%u MB Memory]\n", KeNumberProcessors, MemSizeMBs);
97 
98     if (KeLoaderBlock)
99     {
100         DPRINT1("Command Line: %s\n", KeLoaderBlock->LoadOptions);
101         DPRINT1("ARC Paths: %s %s %s %s\n",
102                 KeLoaderBlock->ArcBootDeviceName, KeLoaderBlock->NtHalPathName,
103                 KeLoaderBlock->ArcHalDeviceName, KeLoaderBlock->NtBootPathName);
104     }
105 }
106 
107 /* FUNCTIONS *****************************************************************/
108 
109 VOID
110 NTAPI
KdUpdateDataBlock(VOID)111 KdUpdateDataBlock(VOID)
112 {
113     /* Update the KeUserCallbackDispatcher pointer */
114     KdDebuggerDataBlock.KeUserCallbackDispatcher =
115         (ULONG_PTR)KeUserCallbackDispatcher;
116 }
117 
118 BOOLEAN
119 NTAPI
KdRegisterDebuggerDataBlock(IN ULONG Tag,IN PDBGKD_DEBUG_DATA_HEADER64 DataHeader,IN ULONG Size)120 KdRegisterDebuggerDataBlock(IN ULONG Tag,
121                             IN PDBGKD_DEBUG_DATA_HEADER64 DataHeader,
122                             IN ULONG Size)
123 {
124     KIRQL OldIrql;
125     PLIST_ENTRY NextEntry;
126     PDBGKD_DEBUG_DATA_HEADER64 CurrentHeader;
127 
128     /* Acquire the Data Lock */
129     KeAcquireSpinLock(&KdpDataSpinLock, &OldIrql);
130 
131     /* Loop the debugger data list */
132     NextEntry = KdpDebuggerDataListHead.Flink;
133     while (NextEntry != &KdpDebuggerDataListHead)
134     {
135         /* Get the header for this entry */
136         CurrentHeader = CONTAINING_RECORD(NextEntry,
137                                           DBGKD_DEBUG_DATA_HEADER64,
138                                           List);
139 
140         /*  Move to the next one */
141         NextEntry = NextEntry->Flink;
142 
143         /* Check if we already have this data block */
144         if ((CurrentHeader == DataHeader) || (CurrentHeader->OwnerTag == Tag))
145         {
146             /* Release the lock and fail */
147             KeReleaseSpinLock(&KdpDataSpinLock, OldIrql);
148             return FALSE;
149         }
150     }
151 
152     /* Setup the header */
153     DataHeader->OwnerTag = Tag;
154     DataHeader->Size = Size;
155 
156     /* Insert it into the list and release the lock */
157     InsertTailList(&KdpDebuggerDataListHead, (PLIST_ENTRY)&DataHeader->List);
158     KeReleaseSpinLock(&KdpDataSpinLock, OldIrql);
159     return TRUE;
160 }
161 
162 BOOLEAN
163 NTAPI
KdInitSystem(_In_ ULONG BootPhase,_In_opt_ PLOADER_PARAMETER_BLOCK LoaderBlock)164 KdInitSystem(
165     _In_ ULONG BootPhase,
166     _In_opt_ PLOADER_PARAMETER_BLOCK LoaderBlock)
167 {
168     BOOLEAN EnableKd, DisableKdAfterInit = FALSE, BlockEnable = FALSE;
169     PLDR_DATA_TABLE_ENTRY LdrEntry;
170     ULONG i;
171 
172     /* Check if this is Phase 1 */
173     if (BootPhase)
174     {
175         /* Just query the performance counter */
176         KeQueryPerformanceCounter(&KdPerformanceCounterRate);
177         return TRUE;
178     }
179 
180     /* Check if we already initialized once */
181     if (KdDebuggerEnabled)
182         return TRUE;
183 
184     /* Set the Debug Routine as the Stub for now */
185     KiDebugRoutine = KdpStub;
186 
187     /* Disable break after symbol load for now */
188     KdBreakAfterSymbolLoad = FALSE;
189 
190     /* Check if the Debugger Data Block was already initialized */
191     if (!KdpDebuggerDataListHead.Flink)
192     {
193         /* It wasn't...Initialize the KD Data Listhead */
194         InitializeListHead(&KdpDebuggerDataListHead);
195 
196         /* Register the Debugger Data Block */
197         KdRegisterDebuggerDataBlock(KDBG_TAG,
198                                     &KdDebuggerDataBlock.Header,
199                                     sizeof(KdDebuggerDataBlock));
200 
201         /* Fill out the KD Version Block */
202         KdVersionBlock.MajorVersion = (USHORT)((DBGKD_MAJOR_NT << 8) | (NtBuildNumber >> 28));
203         KdVersionBlock.MinorVersion = (USHORT)(NtBuildNumber & 0xFFFF);
204 
205 #ifdef CONFIG_SMP
206         /* This is an MP Build */
207         KdVersionBlock.Flags |= DBGKD_VERS_FLAG_MP;
208 #endif
209 
210         /* Save Pointers to Loaded Module List and Debugger Data */
211         KdVersionBlock.PsLoadedModuleList = (ULONG64)(LONG_PTR)&PsLoadedModuleList;
212         KdVersionBlock.DebuggerDataList = (ULONG64)(LONG_PTR)&KdpDebuggerDataListHead;
213 
214         /* Set protocol limits */
215         KdVersionBlock.MaxStateChange = DbgKdMaximumStateChange -
216                                         DbgKdMinimumStateChange;
217         KdVersionBlock.MaxManipulate = DbgKdMaximumManipulate -
218                                        DbgKdMinimumManipulate;
219         KdVersionBlock.Unused[0] = 0;
220 
221         /* Link us in the KPCR */
222         KeGetPcr()->KdVersionBlock = &KdVersionBlock;
223     }
224 
225     /* Check if we have a loader block */
226     if (LoaderBlock)
227     {
228         PSTR CommandLine, DebugLine;
229 
230         /* Get the image entry */
231         LdrEntry = CONTAINING_RECORD(LoaderBlock->LoadOrderListHead.Flink,
232                                      LDR_DATA_TABLE_ENTRY,
233                                      InLoadOrderLinks);
234 
235         /* Save the Kernel Base */
236         PsNtosImageBase = (ULONG_PTR)LdrEntry->DllBase;
237         KdVersionBlock.KernBase = (ULONG64)(LONG_PTR)LdrEntry->DllBase;
238 
239         /* Check if we have a command line */
240         CommandLine = LoaderBlock->LoadOptions;
241         if (CommandLine)
242         {
243             /* Upcase it */
244             _strupr(CommandLine);
245 
246             /* Assume we'll disable KD */
247             EnableKd = FALSE;
248 
249             /* Check for CRASHDEBUG, NODEBUG and just DEBUG */
250             if (strstr(CommandLine, "CRASHDEBUG"))
251             {
252                 /* Don't enable KD now, but allow it to be enabled later */
253                 KdPitchDebugger = FALSE;
254             }
255             else if (strstr(CommandLine, "NODEBUG"))
256             {
257                 /* Don't enable KD and don't let it be enabled later */
258                 KdPitchDebugger = TRUE;
259             }
260             else if ((DebugLine = strstr(CommandLine, "DEBUG")))
261             {
262                 /* Enable KD */
263                 EnableKd = TRUE;
264 
265                 /* Check if there are any options */
266                 if (DebugLine[5] == '=')
267                 {
268                     /* Save pointers */
269                     PSTR DebugOptionStart, DebugOptionEnd;
270                     DebugOptionStart = DebugOptionEnd = &DebugLine[6];
271 
272                     /* Scan the string for debug options */
273                     for (;;)
274                     {
275                         SIZE_T DebugOptionLength;
276 
277                         /* Loop until we reach the end of the string */
278                         while (*DebugOptionEnd != ANSI_NULL)
279                         {
280                             /* Check if this is a comma, a space or a tab */
281                             if ((*DebugOptionEnd == ',') ||
282                                 (*DebugOptionEnd == ' ') ||
283                                 (*DebugOptionEnd == '\t'))
284                             {
285                                 /*
286                                  * We reached the end of the option or
287                                  * the end of the string, break out.
288                                  */
289                                 break;
290                             }
291                             else
292                             {
293                                 /* Move on to the next character */
294                                 DebugOptionEnd++;
295                             }
296                         }
297 
298                         /* Calculate the length of the current option */
299                         DebugOptionLength = (DebugOptionEnd - DebugOptionStart);
300 
301                         /*
302                          * Break out if we reached the last option
303                          * or if there were no options at all.
304                          */
305                         if (!DebugOptionLength)
306                             break;
307 
308                         /* Now check which option this is */
309                         if ((DebugOptionLength == 10) &&
310                             !(strncmp(DebugOptionStart, "AUTOENABLE", 10)))
311                         {
312                             /* Disable the debugger, but
313                              * allow to re-enable it later */
314                             DisableKdAfterInit = TRUE;
315                             BlockEnable = FALSE;
316                             KdAutoEnableOnEvent = TRUE;
317                         }
318                         else if ((DebugOptionLength == 7) &&
319                                  !(strncmp(DebugOptionStart, "DISABLE", 7)))
320                         {
321                             /* Disable the debugger */
322                             DisableKdAfterInit = TRUE;
323                             BlockEnable = TRUE;
324                             KdAutoEnableOnEvent = FALSE;
325                         }
326                         else if ((DebugOptionLength == 6) &&
327                                  !(strncmp(DebugOptionStart, "NOUMEX", 6)))
328                         {
329                             /* Ignore user mode exceptions */
330                             KdIgnoreUmExceptions = TRUE;
331                         }
332 
333                         /*
334                          * If there are more options then the next character
335                          * should be a comma. Break out if it isn't.
336                          */
337                         if (*DebugOptionEnd != ',')
338                             break;
339 
340                         /* Move on to the next option */
341                         DebugOptionEnd++;
342                         DebugOptionStart = DebugOptionEnd;
343                     }
344                 }
345             }
346         }
347         else
348         {
349             /* No command line options? Disable debugger by default */
350             KdPitchDebugger = TRUE;
351             EnableKd = FALSE;
352         }
353     }
354     else
355     {
356         /* Called from a bugcheck or a re-enable. Save the Kernel Base. */
357         KdVersionBlock.KernBase = (ULONG64)(LONG_PTR)PsNtosImageBase;
358 
359         /* Unconditionally enable KD */
360         EnableKd = TRUE;
361     }
362 
363     /* Set the Kernel Base in the Data Block */
364     KdDebuggerDataBlock.KernBase = (ULONG_PTR)KdVersionBlock.KernBase;
365 
366     /* Initialize the debugger if requested */
367     if (EnableKd && (NT_SUCCESS(KdDebuggerInitialize0(LoaderBlock))))
368     {
369         /* Now set our real KD routine */
370         KiDebugRoutine = KdpTrap;
371 
372         /* Check if we've already initialized our structures */
373         if (!KdpDebuggerStructuresInitialized)
374         {
375             /* Set Retries */
376             KdpContext.KdpDefaultRetries = 20;
377 
378             /* Initialize breakpoints owed flag and table */
379             KdpOweBreakpoint = FALSE;
380             for (i = 0; i < KD_BREAKPOINT_MAX; i++)
381             {
382                 KdpBreakpointTable[i].Flags   = 0;
383                 KdpBreakpointTable[i].DirectoryTableBase = 0;
384                 KdpBreakpointTable[i].Address = NULL;
385             }
386 
387             /* Initialize the Time Slip DPC */
388             KeInitializeDpc(&KdpTimeSlipDpc, KdpTimeSlipDpcRoutine, NULL);
389             KeInitializeTimer(&KdpTimeSlipTimer);
390             ExInitializeWorkItem(&KdpTimeSlipWorkItem, KdpTimeSlipWork, NULL);
391 
392             /* First-time initialization done! */
393             KdpDebuggerStructuresInitialized = TRUE;
394         }
395 
396         /* Initialize the timer */
397         KdTimerStart.QuadPart = 0;
398 
399         /* Officially enable KD */
400         KdPitchDebugger = FALSE;
401         KdDebuggerEnabled = TRUE;
402 
403         /* Let user-mode know that it's enabled as well */
404         SharedUserData->KdDebuggerEnabled = TRUE;
405 
406         /* Display separator + ReactOS version at the start of the debug log */
407         KdpPrintBanner();
408 
409         /* Check if the debugger should be disabled initially */
410         if (DisableKdAfterInit)
411         {
412             /* Disable it */
413             KdDisableDebuggerWithLock(FALSE);
414 
415             /*
416              * Save the enable block state and return initialized
417              * (the debugger is active but disabled).
418              */
419             KdBlockEnable = BlockEnable;
420             return TRUE;
421         }
422 
423         /* Check if we have a loader block */
424         if (LoaderBlock)
425         {
426             PLIST_ENTRY NextEntry;
427             ULONG j, Length;
428             PWCHAR Name;
429             STRING ImageName;
430             CHAR NameBuffer[256];
431 
432             /* Loop over the first two boot images: HAL and kernel */
433             for (NextEntry = LoaderBlock->LoadOrderListHead.Flink, i = 0;
434                  NextEntry != &LoaderBlock->LoadOrderListHead && (i < 2);
435                  NextEntry = NextEntry->Flink, ++i)
436             {
437                 /* Get the image entry */
438                 LdrEntry = CONTAINING_RECORD(NextEntry,
439                                              LDR_DATA_TABLE_ENTRY,
440                                              InLoadOrderLinks);
441 
442                 /* Generate the image name */
443                 Name = LdrEntry->FullDllName.Buffer;
444                 Length = LdrEntry->FullDllName.Length / sizeof(WCHAR);
445                 j = 0;
446                 do
447                 {
448                     /* Do cheap Unicode to ANSI conversion */
449                     NameBuffer[j++] = (CHAR)*Name++;
450                 } while (j < Length);
451 
452                 /* Null-terminate */
453                 NameBuffer[j] = ANSI_NULL;
454 
455                 /* Load the symbols */
456                 RtlInitString(&ImageName, NameBuffer);
457                 DbgLoadImageSymbols(&ImageName,
458                                     LdrEntry->DllBase,
459                                     (ULONG_PTR)PsGetCurrentProcessId());
460             }
461 
462             /* Check for incoming break-in and break on symbol load
463              * if requested, see ex/init.c!ExpLoadBootSymbols() */
464             KdBreakAfterSymbolLoad = KdPollBreakIn();
465         }
466     }
467     else
468     {
469         /* Disable debugger */
470         KdDebuggerNotPresent = TRUE;
471     }
472 
473     /* Return initialized */
474     return TRUE;
475 }
476