xref: /reactos/ntoskrnl/kd64/kdinit.c (revision d6eebaa4)
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 CODE_SEG("INIT")
30 SIZE_T
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 CODE_SEG("INIT")
71 VOID
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(
143     _In_ ULONG BootPhase,
144     _In_opt_ PLOADER_PARAMETER_BLOCK LoaderBlock)
145 {
146     BOOLEAN EnableKd, DisableKdAfterInit = FALSE, BlockEnable;
147     PSTR CommandLine, DebugLine, DebugOptionStart, DebugOptionEnd;
148     STRING ImageName;
149     PLDR_DATA_TABLE_ENTRY LdrEntry;
150     PLIST_ENTRY NextEntry;
151     ULONG i, j, Length;
152     SIZE_T DebugOptionLength;
153     SIZE_T MemSizeMBs;
154     CHAR NameBuffer[256];
155     PWCHAR Name;
156 
157 #if defined(__GNUC__)
158     /* Make gcc happy */
159     BlockEnable = FALSE;
160 #endif
161 
162     /* Check if this is Phase 1 */
163     if (BootPhase)
164     {
165         /* Just query the performance counter */
166         KeQueryPerformanceCounter(&KdPerformanceCounterRate);
167         return TRUE;
168     }
169 
170     /* Check if we already initialized once */
171     if (KdDebuggerEnabled) return TRUE;
172 
173     /* Set the Debug Routine as the Stub for now */
174     KiDebugRoutine = KdpStub;
175 
176     /* Disable break after symbol load for now */
177     KdBreakAfterSymbolLoad = FALSE;
178 
179     /* Check if the Debugger Data Block was already initialized */
180     if (!KdpDebuggerDataListHead.Flink)
181     {
182         /* It wasn't...Initialize the KD Data Listhead */
183         InitializeListHead(&KdpDebuggerDataListHead);
184 
185         /* Register the Debugger Data Block */
186         KdRegisterDebuggerDataBlock(KDBG_TAG,
187                                     &KdDebuggerDataBlock.Header,
188                                     sizeof(KdDebuggerDataBlock));
189 
190         /* Fill out the KD Version Block */
191         KdVersionBlock.MajorVersion = (USHORT)((DBGKD_MAJOR_NT << 8) | (NtBuildNumber >> 28));
192         KdVersionBlock.MinorVersion = (USHORT)(NtBuildNumber & 0xFFFF);
193 
194 #ifdef CONFIG_SMP
195         /* This is an MP Build */
196         KdVersionBlock.Flags |= DBGKD_VERS_FLAG_MP;
197 #endif
198 
199         /* Save Pointers to Loaded Module List and Debugger Data */
200         KdVersionBlock.PsLoadedModuleList = (ULONG64)(LONG_PTR)&PsLoadedModuleList;
201         KdVersionBlock.DebuggerDataList = (ULONG64)(LONG_PTR)&KdpDebuggerDataListHead;
202 
203         /* Set protocol limits */
204         KdVersionBlock.MaxStateChange = DbgKdMaximumStateChange -
205                                         DbgKdMinimumStateChange;
206         KdVersionBlock.MaxManipulate = DbgKdMaximumManipulate -
207                                        DbgKdMinimumManipulate;
208         KdVersionBlock.Unused[0] = 0;
209 
210         /* Link us in the KPCR */
211         KeGetPcr()->KdVersionBlock =  &KdVersionBlock;
212     }
213 
214     /* Check if we have a loader block */
215     if (LoaderBlock)
216     {
217         /* Get the image entry */
218         LdrEntry = CONTAINING_RECORD(LoaderBlock->LoadOrderListHead.Flink,
219                                      LDR_DATA_TABLE_ENTRY,
220                                      InLoadOrderLinks);
221 
222         /* Save the Kernel Base */
223         PsNtosImageBase = (ULONG_PTR)LdrEntry->DllBase;
224         KdVersionBlock.KernBase = (ULONG64)(LONG_PTR)LdrEntry->DllBase;
225 
226         /* Check if we have a command line */
227         CommandLine = LoaderBlock->LoadOptions;
228         if (CommandLine)
229         {
230             /* Upcase it */
231             _strupr(CommandLine);
232 
233             /* Assume we'll disable KD */
234             EnableKd = FALSE;
235 
236             /* Check for CRASHDEBUG, NODEBUG and just DEBUG */
237             if (strstr(CommandLine, "CRASHDEBUG"))
238             {
239                 /* Don't enable KD now, but allow it to be enabled later */
240                 KdPitchDebugger = FALSE;
241             }
242             else if (strstr(CommandLine, "NODEBUG"))
243             {
244                 /* Don't enable KD and don't let it be enabled later */
245                 KdPitchDebugger = TRUE;
246             }
247             else if ((DebugLine = strstr(CommandLine, "DEBUG")) != NULL)
248             {
249                 /* Enable KD */
250                 EnableKd = TRUE;
251 
252                 /* Check if there are any options */
253                 if (DebugLine[5] == '=')
254                 {
255                     /* Save pointers */
256                     DebugOptionStart = DebugOptionEnd = &DebugLine[6];
257 
258                     /* Scan the string for debug options */
259                     for (;;)
260                     {
261                         /* Loop until we reach the end of the string */
262                         while (*DebugOptionEnd != ANSI_NULL)
263                         {
264                             /* Check if this is a comma, a space or a tab */
265                             if ((*DebugOptionEnd == ',') ||
266                                 (*DebugOptionEnd == ' ') ||
267                                 (*DebugOptionEnd == '\t'))
268                             {
269                                 /*
270                                  * We reached the end of the option or
271                                  * the end of the string, break out
272                                  */
273                                 break;
274                             }
275                             else
276                             {
277                                 /* Move on to the next character */
278                                 DebugOptionEnd++;
279                             }
280                         }
281 
282                         /* Calculate the length of the current option */
283                         DebugOptionLength = (DebugOptionEnd - DebugOptionStart);
284 
285                        /*
286                         * Break out if we reached the last option
287                         * or if there were no options at all
288                         */
289                        if (!DebugOptionLength) break;
290 
291                         /* Now check which option this is */
292                         if ((DebugOptionLength == 10) &&
293                             !(strncmp(DebugOptionStart, "AUTOENABLE", 10)))
294                         {
295                             /*
296                              * Disable the debugger, but
297                              * allow it to be reenabled
298                              */
299                             DisableKdAfterInit = TRUE;
300                             BlockEnable = FALSE;
301                             KdAutoEnableOnEvent = TRUE;
302                         }
303                         else if ((DebugOptionLength == 7) &&
304                                  !(strncmp(DebugOptionStart, "DISABLE", 7)))
305                         {
306                             /* Disable the debugger */
307                             DisableKdAfterInit = TRUE;
308                             BlockEnable = TRUE;
309                             KdAutoEnableOnEvent = FALSE;
310                         }
311                         else if ((DebugOptionLength == 6) &&
312                                  !(strncmp(DebugOptionStart, "NOUMEX", 6)))
313                         {
314                             /* Ignore user mode exceptions */
315                             KdIgnoreUmExceptions = TRUE;
316                         }
317 
318                         /*
319                          * If there are more options then
320                          * the next character should be a comma
321                          */
322                         if (*DebugOptionEnd != ',')
323                         {
324                             /* It isn't, break out  */
325                             break;
326                         }
327 
328                         /* Move on to the next option */
329                         DebugOptionEnd++;
330                         DebugOptionStart = DebugOptionEnd;
331                     }
332                 }
333             }
334         }
335         else
336         {
337             /* No command line options? Disable debugger by default */
338             KdPitchDebugger = TRUE;
339             EnableKd = FALSE;
340         }
341     }
342     else
343     {
344         /* Called from a bugcheck or a re-enable. Save the Kernel Base. */
345         KdVersionBlock.KernBase = (ULONG64)(LONG_PTR)PsNtosImageBase;
346 
347         /* Unconditionally enable KD */
348         EnableKd = TRUE;
349     }
350 
351     /* Set the Kernel Base in the Data Block */
352     KdDebuggerDataBlock.KernBase = (ULONG_PTR)KdVersionBlock.KernBase;
353 
354     /* Initialize the debugger if requested */
355     if (EnableKd && (NT_SUCCESS(KdDebuggerInitialize0(LoaderBlock))))
356     {
357         /* Now set our real KD routine */
358         KiDebugRoutine = KdpTrap;
359 
360         /* Check if we've already initialized our structures */
361         if (!KdpDebuggerStructuresInitialized)
362         {
363             /* Set Retries */
364             KdpContext.KdpDefaultRetries = 20;
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