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