[FREELDR] Add parsing boot options from .ini files. (#2511)
[reactos.git] / boot / freeldr / freeldr / ntldr / winldr.c
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 }