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