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