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
KdpGetMemorySizeInMBs(IN PLOADER_PARAMETER_BLOCK LoaderBlock)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
KdpPrintBanner(IN SIZE_T MemSizeMBs)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
KdUpdateDataBlock(VOID)89 KdUpdateDataBlock(VOID)
90 {
91 /* Update the KeUserCallbackDispatcher pointer */
92 KdDebuggerDataBlock.KeUserCallbackDispatcher =
93 (ULONG_PTR)KeUserCallbackDispatcher;
94 }
95
96 BOOLEAN
97 NTAPI
KdRegisterDebuggerDataBlock(IN ULONG Tag,IN PDBGKD_DEBUG_DATA_HEADER64 DataHeader,IN ULONG Size)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
KdInitSystem(_In_ ULONG BootPhase,_In_opt_ PLOADER_PARAMETER_BLOCK LoaderBlock)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