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