1 /* 2 * PROJECT: FreeLoader 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: Windows-compatible NT OS Loader. 5 * COPYRIGHT: Copyright 2006-2019 Aleksey Bragin <aleksey@reactos.org> 6 */ 7 8 #include <freeldr.h> 9 #include <ndk/ldrtypes.h> 10 #include "winldr.h" 11 #include "registry.h" 12 13 #include <debug.h> 14 DBG_DEFAULT_CHANNEL(WINDOWS); 15 16 // FIXME: Find a better way to retrieve ARC disk information 17 extern ULONG reactos_disk_count; 18 extern ARC_DISK_SIGNATURE_EX reactos_arc_disk_info[]; 19 20 extern ULONG LoaderPagesSpanned; 21 extern BOOLEAN AcpiPresent; 22 23 extern HEADLESS_LOADER_BLOCK LoaderRedirectionInformation; 24 extern BOOLEAN WinLdrTerminalConnected; 25 extern void WinLdrSetupEms(IN PCHAR BootOptions); 26 27 PLOADER_SYSTEM_BLOCK WinLdrSystemBlock; 28 29 BOOLEAN VirtualBias = FALSE; 30 BOOLEAN SosEnabled = FALSE; 31 BOOLEAN PaeEnabled = FALSE; 32 BOOLEAN PaeDisabled = FALSE; 33 BOOLEAN SafeBoot = FALSE; 34 BOOLEAN BootLogo = FALSE; 35 BOOLEAN NoexecuteDisabled = FALSE; 36 BOOLEAN NoexecuteEnabled = FALSE; 37 38 // debug stuff 39 VOID DumpMemoryAllocMap(VOID); 40 41 // Init "phase 0" 42 VOID 43 AllocateAndInitLPB( 44 IN USHORT VersionToBoot, 45 OUT PLOADER_PARAMETER_BLOCK* OutLoaderBlock) 46 { 47 PLOADER_PARAMETER_BLOCK LoaderBlock; 48 PLOADER_PARAMETER_EXTENSION Extension; 49 50 /* Allocate and zero-init the Loader Parameter Block */ 51 WinLdrSystemBlock = MmAllocateMemoryWithType(sizeof(LOADER_SYSTEM_BLOCK), 52 LoaderSystemBlock); 53 if (WinLdrSystemBlock == NULL) 54 { 55 UiMessageBox("Failed to allocate memory for system block!"); 56 return; 57 } 58 59 RtlZeroMemory(WinLdrSystemBlock, sizeof(LOADER_SYSTEM_BLOCK)); 60 61 LoaderBlock = &WinLdrSystemBlock->LoaderBlock; 62 LoaderBlock->NlsData = &WinLdrSystemBlock->NlsDataBlock; 63 64 /* Initialize the Loader Block Extension */ 65 Extension = &WinLdrSystemBlock->Extension; 66 LoaderBlock->Extension = Extension; 67 Extension->Size = sizeof(LOADER_PARAMETER_EXTENSION); 68 Extension->MajorVersion = (VersionToBoot & 0xFF00) >> 8; 69 Extension->MinorVersion = (VersionToBoot & 0xFF); 70 71 /* Init three critical lists, used right away */ 72 InitializeListHead(&LoaderBlock->LoadOrderListHead); 73 InitializeListHead(&LoaderBlock->MemoryDescriptorListHead); 74 InitializeListHead(&LoaderBlock->BootDriverListHead); 75 76 *OutLoaderBlock = LoaderBlock; 77 } 78 79 // Init "phase 1" 80 VOID 81 WinLdrInitializePhase1(PLOADER_PARAMETER_BLOCK LoaderBlock, 82 PCSTR Options, 83 PCSTR SystemRoot, 84 PCSTR BootPath, 85 USHORT VersionToBoot) 86 { 87 /* 88 * Examples of correct options and paths: 89 * CHAR Options[] = "/DEBUGPORT=COM1 /BAUDRATE=115200"; 90 * CHAR Options[] = "/NODEBUG"; 91 * CHAR SystemRoot[] = "\\WINNT\\"; 92 * CHAR ArcBoot[] = "multi(0)disk(0)rdisk(0)partition(1)"; 93 */ 94 95 PSTR LoadOptions, NewLoadOptions; 96 CHAR HalPath[] = "\\"; 97 CHAR ArcBoot[MAX_PATH+1]; 98 CHAR MiscFiles[MAX_PATH+1]; 99 ULONG i; 100 ULONG_PTR PathSeparator; 101 PLOADER_PARAMETER_EXTENSION Extension; 102 103 /* Construct SystemRoot and ArcBoot from SystemPath */ 104 PathSeparator = strstr(BootPath, "\\") - BootPath; 105 RtlStringCbCopyNA(ArcBoot, sizeof(ArcBoot), BootPath, PathSeparator); 106 107 TRACE("ArcBoot: '%s'\n", ArcBoot); 108 TRACE("SystemRoot: '%s'\n", SystemRoot); 109 TRACE("Options: '%s'\n", Options); 110 111 /* Fill ARC BootDevice */ 112 LoaderBlock->ArcBootDeviceName = WinLdrSystemBlock->ArcBootDeviceName; 113 RtlStringCbCopyA(LoaderBlock->ArcBootDeviceName, sizeof(WinLdrSystemBlock->ArcBootDeviceName), ArcBoot); 114 LoaderBlock->ArcBootDeviceName = PaToVa(LoaderBlock->ArcBootDeviceName); 115 116 // 117 // IMPROVE!! 118 // SetupBlock->ArcSetupDeviceName must be the path to the setup **SOURCE**, 119 // and not the setup boot path. Indeed they may differ!! 120 // 121 if (LoaderBlock->SetupLdrBlock) 122 { 123 PSETUP_LOADER_BLOCK SetupBlock = LoaderBlock->SetupLdrBlock; 124 125 /* Adjust the ARC path in the setup block - Matches ArcBoot path */ 126 SetupBlock->ArcSetupDeviceName = WinLdrSystemBlock->ArcBootDeviceName; 127 SetupBlock->ArcSetupDeviceName = PaToVa(SetupBlock->ArcSetupDeviceName); 128 129 /* Convert the setup block pointer */ 130 LoaderBlock->SetupLdrBlock = PaToVa(LoaderBlock->SetupLdrBlock); 131 } 132 133 /* Fill ARC HalDevice, it matches ArcBoot path */ 134 LoaderBlock->ArcHalDeviceName = WinLdrSystemBlock->ArcBootDeviceName; 135 LoaderBlock->ArcHalDeviceName = PaToVa(LoaderBlock->ArcHalDeviceName); 136 137 /* Fill SystemRoot */ 138 LoaderBlock->NtBootPathName = WinLdrSystemBlock->NtBootPathName; 139 RtlStringCbCopyA(LoaderBlock->NtBootPathName, sizeof(WinLdrSystemBlock->NtBootPathName), SystemRoot); 140 LoaderBlock->NtBootPathName = PaToVa(LoaderBlock->NtBootPathName); 141 142 /* Fill NtHalPathName */ 143 LoaderBlock->NtHalPathName = WinLdrSystemBlock->NtHalPathName; 144 RtlStringCbCopyA(LoaderBlock->NtHalPathName, sizeof(WinLdrSystemBlock->NtHalPathName), HalPath); 145 LoaderBlock->NtHalPathName = PaToVa(LoaderBlock->NtHalPathName); 146 147 /* Fill LoadOptions and strip the '/' switch symbol in front of each option */ 148 NewLoadOptions = LoadOptions = LoaderBlock->LoadOptions = WinLdrSystemBlock->LoadOptions; 149 RtlStringCbCopyA(LoaderBlock->LoadOptions, sizeof(WinLdrSystemBlock->LoadOptions), Options); 150 151 do 152 { 153 while (*LoadOptions == '/') 154 ++LoadOptions; 155 156 *NewLoadOptions++ = *LoadOptions; 157 } while (*LoadOptions++); 158 159 LoaderBlock->LoadOptions = PaToVa(LoaderBlock->LoadOptions); 160 161 /* ARC devices */ 162 LoaderBlock->ArcDiskInformation = &WinLdrSystemBlock->ArcDiskInformation; 163 InitializeListHead(&LoaderBlock->ArcDiskInformation->DiskSignatureListHead); 164 165 /* Convert ARC disk information from freeldr to a correct format */ 166 for (i = 0; i < reactos_disk_count; i++) 167 { 168 PARC_DISK_SIGNATURE_EX ArcDiskSig; 169 170 /* Allocate the ARC structure */ 171 ArcDiskSig = FrLdrHeapAlloc(sizeof(ARC_DISK_SIGNATURE_EX), 'giSD'); 172 173 /* Copy the data over */ 174 RtlCopyMemory(ArcDiskSig, &reactos_arc_disk_info[i], sizeof(ARC_DISK_SIGNATURE_EX)); 175 176 /* Set the ARC Name pointer */ 177 ArcDiskSig->DiskSignature.ArcName = PaToVa(ArcDiskSig->ArcName); 178 179 /* Insert into the list */ 180 InsertTailList(&LoaderBlock->ArcDiskInformation->DiskSignatureListHead, 181 &ArcDiskSig->DiskSignature.ListEntry); 182 } 183 184 /* Convert all lists to Virtual address */ 185 186 /* Convert the ArcDisks list to virtual address */ 187 List_PaToVa(&LoaderBlock->ArcDiskInformation->DiskSignatureListHead); 188 LoaderBlock->ArcDiskInformation = PaToVa(LoaderBlock->ArcDiskInformation); 189 190 /* Convert configuration entries to VA */ 191 ConvertConfigToVA(LoaderBlock->ConfigurationRoot); 192 LoaderBlock->ConfigurationRoot = PaToVa(LoaderBlock->ConfigurationRoot); 193 194 /* Convert all DTE into virtual addresses */ 195 List_PaToVa(&LoaderBlock->LoadOrderListHead); 196 197 /* This one will be converted right before switching to virtual paging mode */ 198 //List_PaToVa(&LoaderBlock->MemoryDescriptorListHead); 199 200 /* Convert list of boot drivers */ 201 List_PaToVa(&LoaderBlock->BootDriverListHead); 202 203 Extension = LoaderBlock->Extension; 204 205 /* FIXME! HACK value for docking profile */ 206 Extension->Profile.Status = 2; 207 208 /* Check if FreeLdr detected a ACPI table */ 209 if (AcpiPresent) 210 { 211 /* Set the pointer to something for compatibility */ 212 Extension->AcpiTable = (PVOID)1; 213 // FIXME: Extension->AcpiTableSize; 214 } 215 216 #ifdef _M_IX86 217 /* Set headless block pointer */ 218 if (WinLdrTerminalConnected) 219 { 220 Extension->HeadlessLoaderBlock = &WinLdrSystemBlock->HeadlessLoaderBlock; 221 RtlCopyMemory(Extension->HeadlessLoaderBlock, 222 &LoaderRedirectionInformation, 223 sizeof(HEADLESS_LOADER_BLOCK)); 224 Extension->HeadlessLoaderBlock = PaToVa(Extension->HeadlessLoaderBlock); 225 } 226 #endif 227 /* Load drivers database */ 228 RtlStringCbCopyA(MiscFiles, sizeof(MiscFiles), BootPath); 229 RtlStringCbCatA(MiscFiles, sizeof(MiscFiles), "AppPatch\\drvmain.sdb"); 230 Extension->DrvDBImage = PaToVa(WinLdrLoadModule(MiscFiles, 231 &Extension->DrvDBSize, 232 LoaderRegistryData)); 233 234 /* Convert the extension block pointer */ 235 LoaderBlock->Extension = PaToVa(LoaderBlock->Extension); 236 237 TRACE("WinLdrInitializePhase1() completed\n"); 238 } 239 240 static BOOLEAN 241 WinLdrLoadDeviceDriver(PLIST_ENTRY LoadOrderListHead, 242 PCSTR BootPath, 243 PUNICODE_STRING FilePath, 244 ULONG Flags, 245 PLDR_DATA_TABLE_ENTRY *DriverDTE) 246 { 247 CHAR FullPath[1024]; 248 CHAR DriverPath[1024]; 249 CHAR DllName[1024]; 250 PCHAR DriverNamePos; 251 BOOLEAN Success; 252 PVOID DriverBase = NULL; 253 254 // Separate the path to file name and directory path 255 RtlStringCbPrintfA(DriverPath, sizeof(DriverPath), "%wZ", FilePath); 256 DriverNamePos = strrchr(DriverPath, '\\'); 257 if (DriverNamePos != NULL) 258 { 259 // Copy the name 260 RtlStringCbCopyA(DllName, sizeof(DllName), DriverNamePos+1); 261 262 // Cut out the name from the path 263 *(DriverNamePos+1) = ANSI_NULL; 264 } 265 else 266 { 267 // There is no directory in the path 268 RtlStringCbCopyA(DllName, sizeof(DllName), DriverPath); 269 *DriverPath = ANSI_NULL; 270 } 271 272 TRACE("DriverPath: '%s', DllName: '%s', LPB\n", DriverPath, DllName); 273 274 // Check if driver is already loaded 275 Success = PeLdrCheckForLoadedDll(LoadOrderListHead, DllName, DriverDTE); 276 if (Success) 277 { 278 // We've got the pointer to its DTE, just return success 279 return TRUE; 280 } 281 282 // It's not loaded, we have to load it 283 RtlStringCbPrintfA(FullPath, sizeof(FullPath), "%s%wZ", BootPath, FilePath); 284 Success = PeLdrLoadImage(FullPath, LoaderBootDriver, &DriverBase); 285 if (!Success) 286 return FALSE; 287 288 // Allocate a DTE for it 289 Success = PeLdrAllocateDataTableEntry(LoadOrderListHead, DllName, DllName, DriverBase, DriverDTE); 290 if (!Success) 291 { 292 ERR("PeLdrAllocateDataTableEntry() failed\n"); 293 return FALSE; 294 } 295 296 // Modify any flags, if needed 297 (*DriverDTE)->Flags |= Flags; 298 299 // Look for any dependencies it may have, and load them too 300 RtlStringCbPrintfA(FullPath, sizeof(FullPath), "%s%s", BootPath, DriverPath); 301 Success = PeLdrScanImportDescriptorTable(LoadOrderListHead, FullPath, *DriverDTE); 302 if (!Success) 303 { 304 ERR("PeLdrScanImportDescriptorTable() failed for %s\n", FullPath); 305 return FALSE; 306 } 307 308 return TRUE; 309 } 310 311 BOOLEAN 312 WinLdrLoadBootDrivers(PLOADER_PARAMETER_BLOCK LoaderBlock, 313 PCSTR BootPath) 314 { 315 PLIST_ENTRY NextBd; 316 PBOOT_DRIVER_LIST_ENTRY BootDriver; 317 BOOLEAN Success; 318 BOOLEAN ret = TRUE; 319 320 // Walk through the boot drivers list 321 NextBd = LoaderBlock->BootDriverListHead.Flink; 322 323 while (NextBd != &LoaderBlock->BootDriverListHead) 324 { 325 BootDriver = CONTAINING_RECORD(NextBd, BOOT_DRIVER_LIST_ENTRY, Link); 326 327 TRACE("BootDriver %wZ DTE %08X RegPath: %wZ\n", &BootDriver->FilePath, 328 BootDriver->LdrEntry, &BootDriver->RegistryPath); 329 330 // Paths are relative (FIXME: Are they always relative?) 331 332 // Load it 333 Success = WinLdrLoadDeviceDriver(&LoaderBlock->LoadOrderListHead, 334 BootPath, 335 &BootDriver->FilePath, 336 0, 337 &BootDriver->LdrEntry); 338 339 if (Success) 340 { 341 // Convert the RegistryPath and DTE addresses to VA since we are not going to use it anymore 342 BootDriver->RegistryPath.Buffer = PaToVa(BootDriver->RegistryPath.Buffer); 343 BootDriver->FilePath.Buffer = PaToVa(BootDriver->FilePath.Buffer); 344 BootDriver->LdrEntry = PaToVa(BootDriver->LdrEntry); 345 } 346 else 347 { 348 // Loading failed - cry loudly 349 ERR("Can't load boot driver '%wZ'!\n", &BootDriver->FilePath); 350 UiMessageBox("Can't load boot driver '%wZ'!", &BootDriver->FilePath); 351 ret = FALSE; 352 353 // Remove it from the list and try to continue 354 RemoveEntryList(NextBd); 355 } 356 357 NextBd = BootDriver->Link.Flink; 358 } 359 360 return ret; 361 } 362 363 PVOID 364 WinLdrLoadModule(PCSTR ModuleName, 365 PULONG Size, 366 TYPE_OF_MEMORY MemoryType) 367 { 368 ULONG FileId; 369 PVOID PhysicalBase; 370 FILEINFORMATION FileInfo; 371 ULONG FileSize; 372 ARC_STATUS Status; 373 ULONG BytesRead; 374 375 //CHAR ProgressString[256]; 376 377 /* Inform user we are loading files */ 378 //UiDrawBackdrop(); 379 //RtlStringCbPrintfA(ProgressString, sizeof(ProgressString), "Loading %s...", FileName); 380 //UiDrawProgressBarCenter(1, 100, ProgressString); 381 382 TRACE("Loading module %s\n", ModuleName); 383 *Size = 0; 384 385 /* Open the image file */ 386 Status = ArcOpen((PSTR)ModuleName, OpenReadOnly, &FileId); 387 if (Status != ESUCCESS) 388 { 389 /* In case of errors, we just return, without complaining to the user */ 390 WARN("Error while opening '%s', Status: %u\n", ModuleName, Status); 391 return NULL; 392 } 393 394 /* Retrieve its size */ 395 Status = ArcGetFileInformation(FileId, &FileInfo); 396 if (Status != ESUCCESS) 397 { 398 ArcClose(FileId); 399 return NULL; 400 } 401 FileSize = FileInfo.EndingAddress.LowPart; 402 *Size = FileSize; 403 404 /* Allocate memory */ 405 PhysicalBase = MmAllocateMemoryWithType(FileSize, MemoryType); 406 if (PhysicalBase == NULL) 407 { 408 ERR("Could not allocate memory for '%s'\n", ModuleName); 409 ArcClose(FileId); 410 return NULL; 411 } 412 413 /* Load the whole file */ 414 Status = ArcRead(FileId, PhysicalBase, FileSize, &BytesRead); 415 ArcClose(FileId); 416 if (Status != ESUCCESS) 417 { 418 WARN("Error while reading '%s', Status: %u\n", ModuleName, Status); 419 return NULL; 420 } 421 422 TRACE("Loaded %s at 0x%x with size 0x%x\n", ModuleName, PhysicalBase, FileSize); 423 424 return PhysicalBase; 425 } 426 427 USHORT 428 WinLdrDetectVersion(VOID) 429 { 430 LONG rc; 431 HKEY hKey; 432 433 rc = RegOpenKey(NULL, 434 L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server", 435 &hKey); 436 if (rc != ERROR_SUCCESS) 437 { 438 /* Key doesn't exist; assume NT 4.0 */ 439 return _WIN32_WINNT_NT4; 440 } 441 442 /* We may here want to read the value of ProductVersion */ 443 return _WIN32_WINNT_WS03; 444 } 445 446 static 447 BOOLEAN 448 LoadModule( 449 IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock, 450 IN PCCH Path, 451 IN PCCH File, 452 IN PCCH ImportName, // BaseDllName 453 IN TYPE_OF_MEMORY MemoryType, 454 OUT PLDR_DATA_TABLE_ENTRY *Dte, 455 IN ULONG Percentage) 456 { 457 BOOLEAN Success; 458 CHAR FullFileName[MAX_PATH]; 459 CHAR ProgressString[256]; 460 PVOID BaseAddress = NULL; 461 462 UiDrawBackdrop(); 463 RtlStringCbPrintfA(ProgressString, sizeof(ProgressString), "Loading %s...", File); 464 UiDrawProgressBarCenter(Percentage, 100, ProgressString); 465 466 RtlStringCbCopyA(FullFileName, sizeof(FullFileName), Path); 467 RtlStringCbCatA(FullFileName, sizeof(FullFileName), File); 468 469 Success = PeLdrLoadImage(FullFileName, MemoryType, &BaseAddress); 470 if (!Success) 471 { 472 TRACE("Loading %s failed\n", File); 473 return FALSE; 474 } 475 TRACE("%s loaded successfully at %p\n", File, BaseAddress); 476 477 /* 478 * Cheat about the base DLL name if we are loading 479 * the Kernel Debugger Transport DLL, to make the 480 * PE loader happy. 481 */ 482 Success = PeLdrAllocateDataTableEntry(&LoaderBlock->LoadOrderListHead, 483 ImportName, 484 FullFileName, 485 BaseAddress, 486 Dte); 487 488 return Success; 489 } 490 491 static 492 BOOLEAN 493 LoadWindowsCore(IN USHORT OperatingSystemVersion, 494 IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock, 495 IN PCSTR BootOptions, 496 IN PCSTR BootPath, 497 IN OUT PLDR_DATA_TABLE_ENTRY* KernelDTE) 498 { 499 BOOLEAN Success; 500 PCSTR Options; 501 CHAR DirPath[MAX_PATH]; 502 CHAR HalFileName[MAX_PATH]; 503 CHAR KernelFileName[MAX_PATH]; 504 CHAR KdTransportDllName[MAX_PATH]; 505 PLDR_DATA_TABLE_ENTRY HalDTE, KdComDTE = NULL; 506 507 if (!KernelDTE) return FALSE; 508 509 /* Initialize SystemRoot\System32 path */ 510 RtlStringCbCopyA(DirPath, sizeof(DirPath), BootPath); 511 RtlStringCbCatA(DirPath, sizeof(DirPath), "system32\\"); 512 513 /* 514 * Default HAL and KERNEL file names. 515 * See the following links to know how the file names are actually chosen: 516 * https://www.geoffchappell.com/notes/windows/boot/bcd/osloader/detecthal.htm 517 * https://www.geoffchappell.com/notes/windows/boot/bcd/osloader/hal.htm 518 * https://www.geoffchappell.com/notes/windows/boot/bcd/osloader/kernel.htm 519 */ 520 RtlStringCbCopyA(HalFileName , sizeof(HalFileName) , "hal.dll"); 521 RtlStringCbCopyA(KernelFileName, sizeof(KernelFileName), "ntoskrnl.exe"); 522 523 /* Find any "/HAL=" or "/KERNEL=" switch in the boot options */ 524 Options = BootOptions; 525 while (Options) 526 { 527 /* Skip possible initial whitespace */ 528 Options += strspn(Options, " \t"); 529 530 /* Check whether a new option starts and it is either HAL or KERNEL */ 531 if (*Options != '/' || (++Options, 532 !(_strnicmp(Options, "HAL=", 4) == 0 || 533 _strnicmp(Options, "KERNEL=", 7) == 0)) ) 534 { 535 /* Search for another whitespace */ 536 Options = strpbrk(Options, " \t"); 537 continue; 538 } 539 else 540 { 541 size_t i = strcspn(Options, " \t"); /* Skip whitespace */ 542 if (i == 0) 543 { 544 /* Use the default values */ 545 break; 546 } 547 548 /* We have found either HAL or KERNEL options */ 549 if (_strnicmp(Options, "HAL=", 4) == 0) 550 { 551 Options += 4; i -= 4; 552 RtlStringCbCopyNA(HalFileName, sizeof(HalFileName), Options, i); 553 _strupr(HalFileName); 554 } 555 else if (_strnicmp(Options, "KERNEL=", 7) == 0) 556 { 557 Options += 7; i -= 7; 558 RtlStringCbCopyNA(KernelFileName, sizeof(KernelFileName), Options, i); 559 _strupr(KernelFileName); 560 } 561 } 562 } 563 564 TRACE("HAL file = '%s' ; Kernel file = '%s'\n", HalFileName, KernelFileName); 565 566 /* Load the Kernel */ 567 LoadModule(LoaderBlock, DirPath, KernelFileName, "ntoskrnl.exe", LoaderSystemCode, KernelDTE, 30); 568 569 /* Load the HAL */ 570 LoadModule(LoaderBlock, DirPath, HalFileName, "hal.dll", LoaderHalCode, &HalDTE, 45); 571 572 /* Load the Kernel Debugger Transport DLL */ 573 if (OperatingSystemVersion > _WIN32_WINNT_WIN2K) 574 { 575 /* 576 * According to http://www.nynaeve.net/?p=173 : 577 * "[...] Another enhancement that could be done Microsoft-side would be 578 * a better interface for replacing KD transport modules. Right now, due 579 * to the fact that ntoskrnl is static linked to KDCOM.DLL, the OS loader 580 * has a hardcoded hack that interprets the KD type in the OS loader options, 581 * loads one of the (hardcoded filenames) "kdcom.dll", "kd1394.dll", or 582 * "kdusb2.dll" modules, and inserts them into the loaded module list under 583 * the name "kdcom.dll". [...]" 584 */ 585 586 /* 587 * This loop replaces a dumb call to strstr(..., "DEBUGPORT="). 588 * Indeed I want it to be case-insensitive to allow "debugport=" 589 * or "DeBuGpOrT=" or... , and I don't want it to match malformed 590 * command-line options, such as: 591 * 592 * "...foo DEBUGPORT=xxx bar..." 593 * "...foo/DEBUGPORT=xxx bar..." 594 * "...foo/DEBUGPORT=bar..." 595 * 596 * i.e. the "DEBUGPORT=" switch must start with a slash and be separated 597 * from the rest by whitespace, unless it begins the command-line, e.g.: 598 * 599 * "/DEBUGPORT=COM1 foo...bar..." 600 * "...foo /DEBUGPORT=USB bar..." 601 * or: 602 * "...foo /DEBUGPORT= bar..." 603 * (in that case, we default the port to COM). 604 */ 605 Options = BootOptions; 606 while (Options) 607 { 608 /* Skip possible initial whitespace */ 609 Options += strspn(Options, " \t"); 610 611 /* Check whether a new option starts and it is the DEBUGPORT one */ 612 if (*Options != '/' || _strnicmp(++Options, "DEBUGPORT=", 10) != 0) 613 { 614 /* Search for another whitespace */ 615 Options = strpbrk(Options, " \t"); 616 continue; 617 } 618 else 619 { 620 /* We found the DEBUGPORT option. Move to the port name. */ 621 Options += 10; 622 break; 623 } 624 } 625 626 if (Options) 627 { 628 /* 629 * We have found the DEBUGPORT option. Parse the port name. 630 * Format: /DEBUGPORT=COM1 or /DEBUGPORT=FILE:\Device\HarddiskX\PartitionY\debug.log or /DEBUGPORT=FOO 631 * If we only have /DEBUGPORT= (i.e. without any port name), defaults it to "COM". 632 */ 633 RtlStringCbCopyA(KdTransportDllName, sizeof(KdTransportDllName), "KD"); 634 if (_strnicmp(Options, "COM", 3) == 0 && '0' <= Options[3] && Options[3] <= '9') 635 { 636 RtlStringCbCatNA(KdTransportDllName, sizeof(KdTransportDllName), Options, 3); 637 } 638 else 639 { 640 size_t i = strcspn(Options, " \t:"); /* Skip valid separators: whitespace or colon */ 641 if (i == 0) 642 RtlStringCbCatA(KdTransportDllName, sizeof(KdTransportDllName), "COM"); 643 else 644 RtlStringCbCatNA(KdTransportDllName, sizeof(KdTransportDllName), Options, i); 645 } 646 RtlStringCbCatA(KdTransportDllName, sizeof(KdTransportDllName), ".DLL"); 647 _strupr(KdTransportDllName); 648 649 /* 650 * Load the transport DLL. Override the base DLL name of the 651 * loaded transport DLL to the default "KDCOM.DLL" name. 652 */ 653 LoadModule(LoaderBlock, DirPath, KdTransportDllName, "kdcom.dll", LoaderSystemCode, &KdComDTE, 60); 654 } 655 } 656 657 /* Parse the boot options */ 658 Options = BootOptions; 659 TRACE("LoadWindowsCore: BootOptions '%s'\n", BootOptions); 660 while (Options) 661 { 662 /* Skip possible initial whitespace */ 663 Options += strspn(Options, " \t"); 664 665 /* Check whether a new option starts */ 666 if (*Options == '/') 667 { 668 Options++; 669 670 if (_strnicmp(Options, "3GB", 3) == 0) 671 { 672 /* We found the 3GB option. */ 673 FIXME("LoadWindowsCore: 3GB - TRUE (not implemented)\n"); 674 VirtualBias = TRUE; 675 } 676 if (_strnicmp(Options, "SOS", 3) == 0) 677 { 678 /* We found the SOS option. */ 679 FIXME("LoadWindowsCore: SOS - TRUE (not implemented)\n"); 680 SosEnabled = TRUE; 681 } 682 if (OperatingSystemVersion > _WIN32_WINNT_NT4) 683 { 684 if (_strnicmp(Options, "SAFEBOOT", 8) == 0) 685 { 686 /* We found the SAFEBOOT option. */ 687 FIXME("LoadWindowsCore: SAFEBOOT - TRUE (not implemented)\n"); 688 SafeBoot = TRUE; 689 } 690 if (_strnicmp(Options, "PAE", 3) == 0) 691 { 692 /* We found the PAE option. */ 693 FIXME("LoadWindowsCore: PAE - TRUE (not implemented)\n"); 694 PaeEnabled = TRUE; 695 } 696 } 697 if (OperatingSystemVersion > _WIN32_WINNT_WIN2K) 698 { 699 if (_strnicmp(Options, "NOPAE", 5) == 0) 700 { 701 /* We found the NOPAE option. */ 702 FIXME("LoadWindowsCore: NOPAE - TRUE (not implemented)\n"); 703 PaeDisabled = TRUE; 704 } 705 if (_strnicmp(Options, "BOOTLOGO", 8) == 0) 706 { 707 /* We found the BOOTLOGO option. */ 708 FIXME("LoadWindowsCore: BOOTLOGO - TRUE (not implemented)\n"); 709 BootLogo = TRUE; 710 } 711 if (!LoaderBlock->SetupLdrBlock) 712 { 713 if (_strnicmp(Options, "NOEXECUTE=ALWAYSOFF", 19) == 0) 714 { 715 /* We found the NOEXECUTE=ALWAYSOFF option. */ 716 FIXME("LoadWindowsCore: NOEXECUTE=ALWAYSOFF - TRUE (not implemented)\n"); 717 NoexecuteDisabled = TRUE; 718 } 719 else if (_strnicmp(Options, "NOEXECUTE", 9) == 0) 720 { 721 /* We found the NOEXECUTE option. */ 722 FIXME("LoadWindowsCore: NOEXECUTE - TRUE (not implemented)\n"); 723 NoexecuteEnabled = TRUE; 724 } 725 726 if (_strnicmp(Options, "EXECUTE", 7) == 0) 727 { 728 /* We found the EXECUTE option. */ 729 FIXME("LoadWindowsCore: EXECUTE - TRUE (not implemented)\n"); 730 NoexecuteDisabled = TRUE; 731 } 732 } 733 } 734 } 735 736 /* Search for another whitespace */ 737 Options = strpbrk(Options, " \t"); 738 } 739 740 if (SafeBoot) 741 { 742 PaeDisabled = TRUE; 743 NoexecuteDisabled = TRUE; 744 } 745 746 /* Load all referenced DLLs for Kernel, HAL and Kernel Debugger Transport DLL */ 747 Success = PeLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, *KernelDTE); 748 Success &= PeLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, HalDTE); 749 if (KdComDTE) 750 { 751 Success &= PeLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, KdComDTE); 752 } 753 754 return Success; 755 } 756 757 static 758 BOOLEAN 759 WinLdrInitErrataInf( 760 IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock, 761 IN USHORT OperatingSystemVersion, 762 IN PCSTR SystemRoot) 763 { 764 LONG rc; 765 HKEY hKey; 766 ULONG BufferSize; 767 ULONG FileSize; 768 PVOID PhysicalBase; 769 WCHAR szFileName[80]; 770 CHAR ErrataFilePath[MAX_PATH]; 771 772 /* Open either the 'BiosInfo' (Windows <= 2003) or the 'Errata' (Vista+) key */ 773 if (OperatingSystemVersion >= _WIN32_WINNT_VISTA) 774 { 775 rc = RegOpenKey(NULL, 776 L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Errata", 777 &hKey); 778 } 779 else // (OperatingSystemVersion <= _WIN32_WINNT_WS03) 780 { 781 rc = RegOpenKey(NULL, 782 L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\BiosInfo", 783 &hKey); 784 } 785 if (rc != ERROR_SUCCESS) 786 { 787 WARN("Could not open the BiosInfo/Errata registry key (Error %u)\n", (int)rc); 788 return FALSE; 789 } 790 791 /* Retrieve the INF file name value */ 792 BufferSize = sizeof(szFileName); 793 rc = RegQueryValue(hKey, L"InfName", NULL, (PUCHAR)szFileName, &BufferSize); 794 if (rc != ERROR_SUCCESS) 795 { 796 WARN("Could not retrieve the InfName value (Error %u)\n", (int)rc); 797 return FALSE; 798 } 799 800 // TODO: "SystemBiosDate" 801 802 RtlStringCbPrintfA(ErrataFilePath, sizeof(ErrataFilePath), "%s%s%S", 803 SystemRoot, "inf\\", szFileName); 804 805 /* Load the INF file */ 806 PhysicalBase = WinLdrLoadModule(ErrataFilePath, &FileSize, LoaderRegistryData); 807 if (!PhysicalBase) 808 { 809 WARN("Could not load '%s'\n", ErrataFilePath); 810 return FALSE; 811 } 812 813 LoaderBlock->Extension->EmInfFileImage = PaToVa(PhysicalBase); 814 LoaderBlock->Extension->EmInfFileSize = FileSize; 815 816 return TRUE; 817 } 818 819 ARC_STATUS 820 LoadAndBootWindows( 821 IN ULONG Argc, 822 IN PCHAR Argv[], 823 IN PCHAR Envp[]) 824 { 825 ARC_STATUS Status; 826 PCSTR ArgValue; 827 PCSTR SystemPartition; 828 PCHAR File; 829 BOOLEAN Success; 830 USHORT OperatingSystemVersion; 831 PLOADER_PARAMETER_BLOCK LoaderBlock; 832 CHAR BootPath[MAX_PATH]; 833 CHAR FileName[MAX_PATH]; 834 CHAR BootOptions[256]; 835 836 /* Retrieve the (mandatory) boot type */ 837 ArgValue = GetArgumentValue(Argc, Argv, "BootType"); 838 if (!ArgValue || !*ArgValue) 839 { 840 ERR("No 'BootType' value, aborting!\n"); 841 return EINVAL; 842 } 843 844 /* Convert it to an OS version */ 845 if (_stricmp(ArgValue, "Windows") == 0 || 846 _stricmp(ArgValue, "Windows2003") == 0) 847 { 848 OperatingSystemVersion = _WIN32_WINNT_WS03; 849 } 850 else if (_stricmp(ArgValue, "WindowsNT40") == 0) 851 { 852 OperatingSystemVersion = _WIN32_WINNT_NT4; 853 } 854 else 855 { 856 ERR("Unknown 'BootType' value '%s', aborting!\n", ArgValue); 857 return EINVAL; 858 } 859 860 /* Retrieve the (mandatory) system partition */ 861 SystemPartition = GetArgumentValue(Argc, Argv, "SystemPartition"); 862 if (!SystemPartition || !*SystemPartition) 863 { 864 ERR("No 'SystemPartition' specified, aborting!\n"); 865 return EINVAL; 866 } 867 868 UiDrawBackdrop(); 869 UiDrawProgressBarCenter(1, 100, "Loading NT..."); 870 871 /* Retrieve the system path */ 872 *BootPath = ANSI_NULL; 873 ArgValue = GetArgumentValue(Argc, Argv, "SystemPath"); 874 if (ArgValue) 875 RtlStringCbCopyA(BootPath, sizeof(BootPath), ArgValue); 876 877 /* 878 * Check whether BootPath is a full path 879 * and if not, create a full boot path. 880 * 881 * See FsOpenFile for the technique used. 882 */ 883 if (strrchr(BootPath, ')') == NULL) 884 { 885 /* Temporarily save the boot path */ 886 RtlStringCbCopyA(FileName, sizeof(FileName), BootPath); 887 888 /* This is not a full path: prepend the SystemPartition */ 889 RtlStringCbCopyA(BootPath, sizeof(BootPath), SystemPartition); 890 891 /* Append a path separator if needed */ 892 if (*FileName != '\\' && *FileName != '/') 893 RtlStringCbCatA(BootPath, sizeof(BootPath), "\\"); 894 895 /* Append the remaining path */ 896 RtlStringCbCatA(BootPath, sizeof(BootPath), FileName); 897 } 898 899 /* Append a path separator if needed */ 900 if (!*BootPath || BootPath[strlen(BootPath) - 1] != '\\') 901 RtlStringCbCatA(BootPath, sizeof(BootPath), "\\"); 902 903 TRACE("BootPath: '%s'\n", BootPath); 904 905 /* Retrieve the boot options */ 906 *BootOptions = ANSI_NULL; 907 ArgValue = GetArgumentValue(Argc, Argv, "Options"); 908 if (ArgValue && *ArgValue) 909 RtlStringCbCopyA(BootOptions, sizeof(BootOptions), ArgValue); 910 911 /* Append boot-time options */ 912 AppendBootTimeOptions(BootOptions); 913 914 /* 915 * Set "/HAL=" and "/KERNEL=" options if needed. 916 * If already present on the standard "Options=" option line, they take 917 * precedence over those passed via the separate "Hal=" and "Kernel=" 918 * options. 919 */ 920 if (strstr(BootOptions, "/HAL=") != 0) 921 { 922 /* 923 * Not found in the options, try to retrieve the 924 * separate value and append it to the options. 925 */ 926 ArgValue = GetArgumentValue(Argc, Argv, "Hal"); 927 if (ArgValue && *ArgValue) 928 { 929 RtlStringCbCatA(BootOptions, sizeof(BootOptions), " /HAL="); 930 RtlStringCbCatA(BootOptions, sizeof(BootOptions), ArgValue); 931 } 932 } 933 if (strstr(BootOptions, "/KERNEL=") != 0) 934 { 935 /* 936 * Not found in the options, try to retrieve the 937 * separate value and append it to the options. 938 */ 939 ArgValue = GetArgumentValue(Argc, Argv, "Kernel"); 940 if (ArgValue && *ArgValue) 941 { 942 RtlStringCbCatA(BootOptions, sizeof(BootOptions), " /KERNEL="); 943 RtlStringCbCatA(BootOptions, sizeof(BootOptions), ArgValue); 944 } 945 } 946 947 TRACE("BootOptions: '%s'\n", BootOptions); 948 949 /* Check if a ramdisk file was given */ 950 File = strstr(BootOptions, "/RDPATH="); 951 if (File) 952 { 953 /* Load the ramdisk */ 954 Status = RamDiskInitialize(FALSE, BootOptions, SystemPartition); 955 if (Status != ESUCCESS) 956 { 957 File += 8; 958 UiMessageBox("Failed to load RAM disk file '%.*s'", 959 strcspn(File, " \t"), File); 960 return Status; 961 } 962 } 963 964 /* Let user know we started loading */ 965 //UiDrawStatusText("Loading..."); 966 967 /* Allocate and minimally-initialize the Loader Parameter Block */ 968 AllocateAndInitLPB(OperatingSystemVersion, &LoaderBlock); 969 970 /* Load the system hive */ 971 UiDrawBackdrop(); 972 UiDrawProgressBarCenter(15, 100, "Loading system hive..."); 973 Success = WinLdrInitSystemHive(LoaderBlock, BootPath, FALSE); 974 TRACE("SYSTEM hive %s\n", (Success ? "loaded" : "not loaded")); 975 /* Bail out if failure */ 976 if (!Success) 977 return ENOEXEC; 978 979 /* Fixup the version number using data from the registry */ 980 if (OperatingSystemVersion == 0) 981 OperatingSystemVersion = WinLdrDetectVersion(); 982 LoaderBlock->Extension->MajorVersion = (OperatingSystemVersion & 0xFF00) >> 8; 983 LoaderBlock->Extension->MinorVersion = (OperatingSystemVersion & 0xFF); 984 985 /* Load NLS data, OEM font, and prepare boot drivers list */ 986 Success = WinLdrScanSystemHive(LoaderBlock, BootPath); 987 TRACE("SYSTEM hive %s\n", (Success ? "scanned" : "not scanned")); 988 /* Bail out if failure */ 989 if (!Success) 990 return ENOEXEC; 991 992 /* Load the Firmware Errata file */ 993 Success = WinLdrInitErrataInf(LoaderBlock, OperatingSystemVersion, BootPath); 994 TRACE("Firmware Errata file %s\n", (Success ? "loaded" : "not loaded")); 995 /* Not necessarily fatal if not found - carry on going */ 996 997 /* Finish loading */ 998 return LoadAndBootWindowsCommon(OperatingSystemVersion, 999 LoaderBlock, 1000 BootOptions, 1001 BootPath, 1002 FALSE); 1003 } 1004 1005 ARC_STATUS 1006 LoadAndBootWindowsCommon( 1007 USHORT OperatingSystemVersion, 1008 PLOADER_PARAMETER_BLOCK LoaderBlock, 1009 PCSTR BootOptions, 1010 PCSTR BootPath, 1011 BOOLEAN Setup) 1012 { 1013 PLOADER_PARAMETER_BLOCK LoaderBlockVA; 1014 BOOLEAN Success; 1015 PLDR_DATA_TABLE_ENTRY KernelDTE; 1016 KERNEL_ENTRY_POINT KiSystemStartup; 1017 PCSTR SystemRoot; 1018 1019 TRACE("LoadAndBootWindowsCommon()\n"); 1020 1021 ASSERT(OperatingSystemVersion != 0); 1022 1023 #ifdef _M_IX86 1024 /* Setup redirection support */ 1025 WinLdrSetupEms((PCHAR)BootOptions); 1026 #endif 1027 1028 /* Convert BootPath to SystemRoot */ 1029 SystemRoot = strstr(BootPath, "\\"); 1030 1031 /* Detect hardware */ 1032 UiDrawBackdrop(); 1033 UiDrawProgressBarCenter(20, 100, "Detecting hardware..."); 1034 LoaderBlock->ConfigurationRoot = MachHwDetect(); 1035 1036 /* Load the operating system core: the Kernel, the HAL and the Kernel Debugger Transport DLL */ 1037 Success = LoadWindowsCore(OperatingSystemVersion, 1038 LoaderBlock, 1039 BootOptions, 1040 BootPath, 1041 &KernelDTE); 1042 if (!Success) 1043 { 1044 UiMessageBox("Error loading NTOS core."); 1045 return ENOEXEC; 1046 } 1047 1048 /* Load boot drivers */ 1049 UiDrawBackdrop(); 1050 UiDrawProgressBarCenter(100, 100, "Loading boot drivers..."); 1051 Success = WinLdrLoadBootDrivers(LoaderBlock, BootPath); 1052 TRACE("Boot drivers loading %s\n", Success ? "successful" : "failed"); 1053 1054 /* Cleanup ini file */ 1055 IniCleanup(); 1056 1057 /* Initialize Phase 1 - no drivers loading anymore */ 1058 WinLdrInitializePhase1(LoaderBlock, 1059 BootOptions, 1060 SystemRoot, 1061 BootPath, 1062 OperatingSystemVersion); 1063 1064 /* Save entry-point pointer and Loader block VAs */ 1065 KiSystemStartup = (KERNEL_ENTRY_POINT)KernelDTE->EntryPoint; 1066 LoaderBlockVA = PaToVa(LoaderBlock); 1067 1068 /* "Stop all motors", change videomode */ 1069 MachPrepareForReactOS(); 1070 1071 /* Debugging... */ 1072 //DumpMemoryAllocMap(); 1073 1074 /* Do the machine specific initialization */ 1075 WinLdrSetupMachineDependent(LoaderBlock); 1076 1077 /* Map pages and create memory descriptors */ 1078 WinLdrSetupMemoryLayout(LoaderBlock); 1079 1080 /* Set processor context */ 1081 WinLdrSetProcessorContext(); 1082 1083 /* Save final value of LoaderPagesSpanned */ 1084 LoaderBlock->Extension->LoaderPagesSpanned = LoaderPagesSpanned; 1085 1086 TRACE("Hello from paged mode, KiSystemStartup %p, LoaderBlockVA %p!\n", 1087 KiSystemStartup, LoaderBlockVA); 1088 1089 /* Zero KI_USER_SHARED_DATA page */ 1090 RtlZeroMemory((PVOID)KI_USER_SHARED_DATA, MM_PAGE_SIZE); 1091 1092 WinLdrpDumpMemoryDescriptors(LoaderBlockVA); 1093 WinLdrpDumpBootDriver(LoaderBlockVA); 1094 #ifndef _M_AMD64 1095 WinLdrpDumpArcDisks(LoaderBlockVA); 1096 #endif 1097 1098 /* Pass control */ 1099 (*KiSystemStartup)(LoaderBlockVA); 1100 return ESUCCESS; 1101 } 1102 1103 VOID 1104 WinLdrpDumpMemoryDescriptors(PLOADER_PARAMETER_BLOCK LoaderBlock) 1105 { 1106 PLIST_ENTRY NextMd; 1107 PMEMORY_ALLOCATION_DESCRIPTOR MemoryDescriptor; 1108 1109 NextMd = LoaderBlock->MemoryDescriptorListHead.Flink; 1110 1111 while (NextMd != &LoaderBlock->MemoryDescriptorListHead) 1112 { 1113 MemoryDescriptor = CONTAINING_RECORD(NextMd, MEMORY_ALLOCATION_DESCRIPTOR, ListEntry); 1114 1115 TRACE("BP %08X PC %04X MT %d\n", MemoryDescriptor->BasePage, 1116 MemoryDescriptor->PageCount, MemoryDescriptor->MemoryType); 1117 1118 NextMd = MemoryDescriptor->ListEntry.Flink; 1119 } 1120 } 1121 1122 VOID 1123 WinLdrpDumpBootDriver(PLOADER_PARAMETER_BLOCK LoaderBlock) 1124 { 1125 PLIST_ENTRY NextBd; 1126 PBOOT_DRIVER_LIST_ENTRY BootDriver; 1127 1128 NextBd = LoaderBlock->BootDriverListHead.Flink; 1129 1130 while (NextBd != &LoaderBlock->BootDriverListHead) 1131 { 1132 BootDriver = CONTAINING_RECORD(NextBd, BOOT_DRIVER_LIST_ENTRY, Link); 1133 1134 TRACE("BootDriver %wZ DTE %08X RegPath: %wZ\n", &BootDriver->FilePath, 1135 BootDriver->LdrEntry, &BootDriver->RegistryPath); 1136 1137 NextBd = BootDriver->Link.Flink; 1138 } 1139 } 1140 1141 VOID 1142 WinLdrpDumpArcDisks(PLOADER_PARAMETER_BLOCK LoaderBlock) 1143 { 1144 PLIST_ENTRY NextBd; 1145 PARC_DISK_SIGNATURE ArcDisk; 1146 1147 NextBd = LoaderBlock->ArcDiskInformation->DiskSignatureListHead.Flink; 1148 1149 while (NextBd != &LoaderBlock->ArcDiskInformation->DiskSignatureListHead) 1150 { 1151 ArcDisk = CONTAINING_RECORD(NextBd, ARC_DISK_SIGNATURE, ListEntry); 1152 1153 TRACE("ArcDisk %s checksum: 0x%X, signature: 0x%X\n", 1154 ArcDisk->ArcName, ArcDisk->CheckSum, ArcDisk->Signature); 1155 1156 NextBd = ArcDisk->ListEntry.Flink; 1157 } 1158 } 1159