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