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