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 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 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 111 KdUpdateDataBlock(VOID) 112 { 113 /* Update the KeUserCallbackDispatcher pointer */ 114 KdDebuggerDataBlock.KeUserCallbackDispatcher = 115 (ULONG_PTR)KeUserCallbackDispatcher; 116 } 117 118 BOOLEAN 119 NTAPI 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 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