xref: /reactos/ntoskrnl/kd64/kdinit.c (revision 09dde2cf)
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 the Debug Switch Routine and Retries */
364              KdpContext.KdpDefaultRetries = 20;
365              KiDebugSwitchRoutine = KdpSwitchProcessor;
366  
367              /* Initialize breakpoints owed flag and table */
368              KdpOweBreakpoint = FALSE;
369              for (i = 0; i < KD_BREAKPOINT_MAX; i++)
370              {
371                  KdpBreakpointTable[i].Flags   = 0;
372                  KdpBreakpointTable[i].DirectoryTableBase = 0;
373                  KdpBreakpointTable[i].Address = NULL;
374              }
375  
376              /* Initialize the Time Slip DPC */
377              KeInitializeDpc(&KdpTimeSlipDpc, KdpTimeSlipDpcRoutine, NULL);
378              KeInitializeTimer(&KdpTimeSlipTimer);
379              ExInitializeWorkItem(&KdpTimeSlipWorkItem, KdpTimeSlipWork, NULL);
380  
381              /* First-time initialization done! */
382              KdpDebuggerStructuresInitialized = TRUE;
383          }
384  
385          /* Initialize the timer */
386          KdTimerStart.QuadPart = 0;
387  
388          /* Officially enable KD */
389          KdPitchDebugger = FALSE;
390          KdDebuggerEnabled = TRUE;
391  
392          /* Let user-mode know that it's enabled as well */
393          SharedUserData->KdDebuggerEnabled = TRUE;
394  
395          /* Display separator + ReactOS version at start of the debug log */
396          MemSizeMBs = KdpGetMemorySizeInMBs(KeLoaderBlock);
397          KdpPrintBanner(MemSizeMBs);
398  
399          /* Check if the debugger should be disabled initially */
400          if (DisableKdAfterInit)
401          {
402              /* Disable it */
403              KdDisableDebuggerWithLock(FALSE);
404  
405              /*
406               * Save the enable block state and return initialized
407               * (the debugger is active but disabled).
408               */
409              KdBlockEnable = BlockEnable;
410              return TRUE;
411          }
412  
413          /* Check if we have a loader block */
414          if (LoaderBlock)
415          {
416              /* Loop boot images */
417              NextEntry = LoaderBlock->LoadOrderListHead.Flink;
418              i = 0;
419              while ((NextEntry != &LoaderBlock->LoadOrderListHead) && (i < 2))
420              {
421                  /* Get the image entry */
422                  LdrEntry = CONTAINING_RECORD(NextEntry,
423                                               LDR_DATA_TABLE_ENTRY,
424                                               InLoadOrderLinks);
425  
426                  /* Generate the image name */
427                  Name = LdrEntry->FullDllName.Buffer;
428                  Length = LdrEntry->FullDllName.Length / sizeof(WCHAR);
429                  j = 0;
430                  do
431                  {
432                      /* Do cheap Unicode to ANSI conversion */
433                      NameBuffer[j++] = (CHAR)*Name++;
434                  } while (j < Length);
435  
436                  /* Null-terminate */
437                  NameBuffer[j] = ANSI_NULL;
438  
439                  /* Load symbols for image */
440                  RtlInitString(&ImageName, NameBuffer);
441                  DbgLoadImageSymbols(&ImageName,
442                                      LdrEntry->DllBase,
443                                      (ULONG_PTR)PsGetCurrentProcessId());
444  
445                  /* Go to the next entry */
446                  NextEntry = NextEntry->Flink;
447                  i++;
448              }
449          }
450  
451          /* Check for incoming breakin and break on symbol load if we have it */
452          KdBreakAfterSymbolLoad = KdPollBreakIn();
453      }
454      else
455      {
456          /* Disable debugger */
457          KdDebuggerNotPresent = TRUE;
458      }
459  
460      /* Return initialized */
461      return TRUE;
462  }
463