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