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