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