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