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