xref: /reactos/boot/freeldr/freeldr/ntldr/winldr.c (revision ad827eaa)
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,
346                                           DllName,
347                                           DllName,
348                                           PaToVa(DriverBase),
349                                           DriverDTE);
350     if (!Success)
351     {
352         /* Cleanup and bail out */
353         ERR("PeLdrAllocateDataTableEntry('%s') failed\n", DllName);
354         MmFreeMemory(DriverBase);
355         return FALSE;
356     }
357 
358     /* Init security cookie */
359     PeLdrInitSecurityCookie(*DriverDTE);
360 
361     // Modify any flags, if needed
362     (*DriverDTE)->Flags |= Flags;
363 
364     // Look for any dependencies it may have, and load them too
365     RtlStringCbPrintfA(FullPath, sizeof(FullPath), "%s%s", BootPath, DriverPath);
366     Success = PeLdrScanImportDescriptorTable(LoadOrderListHead, FullPath, *DriverDTE);
367     if (!Success)
368     {
369         /* Cleanup and bail out */
370         ERR("PeLdrScanImportDescriptorTable('%s') failed\n", FullPath);
371         PeLdrFreeDataTableEntry(*DriverDTE);
372         MmFreeMemory(DriverBase);
373         return FALSE;
374     }
375 
376     return TRUE;
377 }
378 
379 BOOLEAN
380 WinLdrLoadBootDrivers(PLOADER_PARAMETER_BLOCK LoaderBlock,
381                       PCSTR BootPath)
382 {
383     PLIST_ENTRY NextBd;
384     PBOOT_DRIVER_NODE DriverNode;
385     PBOOT_DRIVER_LIST_ENTRY BootDriver;
386     BOOLEAN Success;
387     BOOLEAN ret = TRUE;
388 
389     /* Walk through the boot drivers list */
390     NextBd = LoaderBlock->BootDriverListHead.Flink;
391     while (NextBd != &LoaderBlock->BootDriverListHead)
392     {
393         DriverNode = CONTAINING_RECORD(NextBd,
394                                        BOOT_DRIVER_NODE,
395                                        ListEntry.Link);
396         BootDriver = &DriverNode->ListEntry;
397 
398         /* Get the next list entry as we may remove the current one on failure */
399         NextBd = BootDriver->Link.Flink;
400 
401         TRACE("BootDriver %wZ DTE %08X RegPath: %wZ\n",
402               &BootDriver->FilePath, BootDriver->LdrEntry,
403               &BootDriver->RegistryPath);
404 
405         // Paths are relative (FIXME: Are they always relative?)
406 
407         /* Load it */
408         UiIndicateProgress();
409         Success = WinLdrLoadDeviceDriver(&LoaderBlock->LoadOrderListHead,
410                                          BootPath,
411                                          &BootDriver->FilePath,
412                                          0,
413                                          &BootDriver->LdrEntry);
414         if (Success)
415         {
416             /* Convert the addresses to VA since we are not going to use them anymore */
417             BootDriver->RegistryPath.Buffer = PaToVa(BootDriver->RegistryPath.Buffer);
418             BootDriver->FilePath.Buffer = PaToVa(BootDriver->FilePath.Buffer);
419             BootDriver->LdrEntry = PaToVa(BootDriver->LdrEntry);
420 
421             if (DriverNode->Group.Buffer)
422                 DriverNode->Group.Buffer = PaToVa(DriverNode->Group.Buffer);
423             DriverNode->Name.Buffer = PaToVa(DriverNode->Name.Buffer);
424         }
425         else
426         {
427             /* Loading failed: cry loudly */
428             ERR("Cannot load boot driver '%wZ'!\n", &BootDriver->FilePath);
429             UiMessageBox("Cannot load boot driver '%wZ'!", &BootDriver->FilePath);
430             ret = FALSE;
431 
432             /* Remove it from the list and try to continue */
433             RemoveEntryList(&BootDriver->Link);
434         }
435     }
436 
437     return ret;
438 }
439 
440 PVOID
441 WinLdrLoadModule(PCSTR ModuleName,
442                  PULONG Size,
443                  TYPE_OF_MEMORY MemoryType)
444 {
445     ULONG FileId;
446     PVOID PhysicalBase;
447     FILEINFORMATION FileInfo;
448     ULONG FileSize;
449     ARC_STATUS Status;
450     ULONG BytesRead;
451 
452     *Size = 0;
453 
454     /* Open the image file */
455     NtLdrOutputLoadMsg(ModuleName, NULL);
456     Status = ArcOpen((PSTR)ModuleName, OpenReadOnly, &FileId);
457     if (Status != ESUCCESS)
458     {
459         /* In case of errors, we just return, without complaining to the user */
460         WARN("Error while opening '%s', Status: %u\n", ModuleName, Status);
461         return NULL;
462     }
463 
464     /* Retrieve its size */
465     Status = ArcGetFileInformation(FileId, &FileInfo);
466     if (Status != ESUCCESS)
467     {
468         ArcClose(FileId);
469         return NULL;
470     }
471     FileSize = FileInfo.EndingAddress.LowPart;
472     *Size = FileSize;
473 
474     /* Allocate memory */
475     PhysicalBase = MmAllocateMemoryWithType(FileSize, MemoryType);
476     if (PhysicalBase == NULL)
477     {
478         ERR("Could not allocate memory for '%s'\n", ModuleName);
479         ArcClose(FileId);
480         return NULL;
481     }
482 
483     /* Load the whole file */
484     Status = ArcRead(FileId, PhysicalBase, FileSize, &BytesRead);
485     ArcClose(FileId);
486     if (Status != ESUCCESS)
487     {
488         WARN("Error while reading '%s', Status: %u\n", ModuleName, Status);
489         return NULL;
490     }
491 
492     TRACE("Loaded %s at 0x%x with size 0x%x\n", ModuleName, PhysicalBase, FileSize);
493 
494     return PhysicalBase;
495 }
496 
497 USHORT
498 WinLdrDetectVersion(VOID)
499 {
500     LONG rc;
501     HKEY hKey;
502 
503     rc = RegOpenKey(CurrentControlSetKey, L"Control\\Terminal Server", &hKey);
504     if (rc != ERROR_SUCCESS)
505     {
506         /* Key doesn't exist; assume NT 4.0 */
507         return _WIN32_WINNT_NT4;
508     }
509     RegCloseKey(hKey);
510 
511     /* We may here want to read the value of ProductVersion */
512     return _WIN32_WINNT_WS03;
513 }
514 
515 static
516 PVOID
517 LoadModule(
518     IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,
519     IN PCCH Path,
520     IN PCCH File,
521     IN PCCH ImportName, // BaseDllName
522     IN TYPE_OF_MEMORY MemoryType,
523     OUT PLDR_DATA_TABLE_ENTRY *Dte,
524     IN ULONG Percentage)
525 {
526     BOOLEAN Success;
527     CHAR FullFileName[MAX_PATH];
528     CHAR ProgressString[256];
529     PVOID BaseAddress;
530 
531     RtlStringCbPrintfA(ProgressString, sizeof(ProgressString), "Loading %s...", File);
532     UiUpdateProgressBar(Percentage, ProgressString);
533 
534     RtlStringCbCopyA(FullFileName, sizeof(FullFileName), Path);
535     RtlStringCbCatA(FullFileName, sizeof(FullFileName), File);
536 
537     NtLdrOutputLoadMsg(FullFileName, NULL);
538     Success = PeLdrLoadImage(FullFileName, MemoryType, &BaseAddress);
539     if (!Success)
540     {
541         ERR("PeLdrLoadImage('%s') failed\n", File);
542         return NULL;
543     }
544     TRACE("%s loaded successfully at %p\n", File, BaseAddress);
545 
546     Success = PeLdrAllocateDataTableEntry(&LoaderBlock->LoadOrderListHead,
547                                           ImportName,
548                                           FullFileName,
549                                           PaToVa(BaseAddress),
550                                           Dte);
551     if (!Success)
552     {
553         /* Cleanup and bail out */
554         ERR("PeLdrAllocateDataTableEntry('%s') failed\n", FullFileName);
555         MmFreeMemory(BaseAddress);
556         return NULL;
557     }
558 
559     /* Init security cookie */
560     PeLdrInitSecurityCookie(*Dte);
561 
562     return BaseAddress;
563 }
564 
565 #ifdef _M_IX86
566 static
567 BOOLEAN
568 WinLdrIsPaeSupported(
569     _In_ USHORT OperatingSystemVersion,
570     _In_ PLOADER_PARAMETER_BLOCK LoaderBlock,
571     _In_ PCSTR BootOptions,
572     _In_ PCSTR HalFileName,
573     _Inout_updates_bytes_(KernelFileNameSize) _Always_(_Post_z_)
574          PSTR KernelFileName,
575     _In_ SIZE_T KernelFileNameSize)
576 {
577     BOOLEAN PaeEnabled = FALSE;
578     BOOLEAN PaeDisabled = FALSE;
579     BOOLEAN Result;
580 
581     if ((OperatingSystemVersion > _WIN32_WINNT_NT4) &&
582         NtLdrGetOption(BootOptions, "PAE"))
583     {
584         /* We found the PAE option */
585         PaeEnabled = TRUE;
586     }
587 
588     Result = PaeEnabled;
589 
590     if ((OperatingSystemVersion > _WIN32_WINNT_WIN2K) &&
591         NtLdrGetOption(BootOptions, "NOPAE"))
592     {
593         PaeDisabled = TRUE;
594     }
595 
596     if (SafeBoot)
597         PaeDisabled = TRUE;
598 
599     TRACE("PaeEnabled %X, PaeDisabled %X\n", PaeEnabled, PaeDisabled);
600 
601     if (PaeDisabled)
602         Result = FALSE;
603 
604     /* Enable PAE if DEP is enabled */
605     if (NoExecuteEnabled)
606         Result = TRUE;
607 
608     // TODO: checks for CPU support, hotplug memory support ... other tests
609     // TODO: select kernel name ("ntkrnlpa.exe" or "ntoskrnl.exe"), or,
610     // if KernelFileName is a user-specified kernel file, check whether it
611     // has, if PAE needs to be enabled, the IMAGE_FILE_LARGE_ADDRESS_AWARE
612     // Characteristics bit set, and that the HAL image has a similar support.
613 
614     if (Result) UNIMPLEMENTED;
615 
616     return Result;
617 }
618 #endif /* _M_IX86 */
619 
620 static
621 BOOLEAN
622 LoadWindowsCore(IN USHORT OperatingSystemVersion,
623                 IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,
624                 IN PCSTR BootOptions,
625                 IN PCSTR BootPath,
626                 IN OUT PLDR_DATA_TABLE_ENTRY* KernelDTE)
627 {
628     BOOLEAN Success;
629     PCSTR Option;
630     ULONG OptionLength;
631     PVOID KernelBase, HalBase, KdDllBase = NULL;
632     PLDR_DATA_TABLE_ENTRY HalDTE, KdDllDTE = NULL;
633     CHAR DirPath[MAX_PATH];
634     CHAR HalFileName[MAX_PATH];
635     CHAR KernelFileName[MAX_PATH];
636     CHAR KdDllName[MAX_PATH];
637 
638     if (!KernelDTE) return FALSE;
639 
640     /* Initialize SystemRoot\System32 path */
641     RtlStringCbCopyA(DirPath, sizeof(DirPath), BootPath);
642     RtlStringCbCatA(DirPath, sizeof(DirPath), "system32\\");
643 
644     /* Parse the boot options */
645     TRACE("LoadWindowsCore: BootOptions '%s'\n", BootOptions);
646 
647 #ifdef _M_IX86
648     if (NtLdrGetOption(BootOptions, "3GB"))
649     {
650         /* We found the 3GB option. */
651         FIXME("LoadWindowsCore: 3GB - TRUE (not implemented)\n");
652         VirtualBias = TRUE;
653     }
654     // TODO: "USERVA=" for XP/2k3
655 #endif
656 
657     if ((OperatingSystemVersion > _WIN32_WINNT_NT4) &&
658         (NtLdrGetOption(BootOptions, "SAFEBOOT") ||
659          NtLdrGetOption(BootOptions, "SAFEBOOT:")))
660     {
661         /* We found the SAFEBOOT option. */
662         FIXME("LoadWindowsCore: SAFEBOOT - TRUE (not implemented)\n");
663         SafeBoot = TRUE;
664     }
665 
666     if ((OperatingSystemVersion > _WIN32_WINNT_WIN2K) &&
667         NtLdrGetOption(BootOptions, "BOOTLOGO"))
668     {
669         /* We found the BOOTLOGO option. */
670         FIXME("LoadWindowsCore: BOOTLOGO - TRUE (not implemented)\n");
671         BootLogo = TRUE;
672     }
673 
674     /* Check the (NO)EXECUTE options */
675     if ((OperatingSystemVersion > _WIN32_WINNT_WIN2K) &&
676         !LoaderBlock->SetupLdrBlock)
677     {
678         /* Disable NX by default on x86, otherwise enable it */
679 #ifdef _M_IX86
680         NoExecuteEnabled = FALSE;
681 #else
682         NoExecuteEnabled = TRUE;
683 #endif
684 
685 #ifdef _M_IX86
686         /* Check the options in decreasing order of precedence */
687         if (NtLdrGetOption(BootOptions, "NOEXECUTE=OPTIN")  ||
688             NtLdrGetOption(BootOptions, "NOEXECUTE=OPTOUT") ||
689             NtLdrGetOption(BootOptions, "NOEXECUTE=ALWAYSON"))
690         {
691             NoExecuteEnabled = TRUE;
692         }
693         else if (NtLdrGetOption(BootOptions, "NOEXECUTE=ALWAYSOFF"))
694             NoExecuteEnabled = FALSE;
695         else
696 #else
697         /* Only the following two options really apply for x64 and other platforms */
698 #endif
699         if (NtLdrGetOption(BootOptions, "NOEXECUTE"))
700             NoExecuteEnabled = TRUE;
701         else if (NtLdrGetOption(BootOptions, "EXECUTE"))
702             NoExecuteEnabled = FALSE;
703 
704 #ifdef _M_IX86
705         /* Disable DEP in SafeBoot mode for x86 only */
706         if (SafeBoot)
707             NoExecuteEnabled = FALSE;
708 #endif
709     }
710     TRACE("NoExecuteEnabled %X\n", NoExecuteEnabled);
711 
712     /*
713      * Select the HAL and KERNEL file names.
714      * Check for any "/HAL=" or "/KERNEL=" override option.
715      *
716      * See the following links to know how the file names are actually chosen:
717      * https://www.geoffchappell.com/notes/windows/boot/bcd/osloader/detecthal.htm
718      * https://www.geoffchappell.com/notes/windows/boot/bcd/osloader/hal.htm
719      * https://www.geoffchappell.com/notes/windows/boot/bcd/osloader/kernel.htm
720      */
721     /* Default HAL and KERNEL file names */
722     RtlStringCbCopyA(HalFileName   , sizeof(HalFileName)   , "hal.dll");
723     RtlStringCbCopyA(KernelFileName, sizeof(KernelFileName), "ntoskrnl.exe");
724 
725     Option = NtLdrGetOptionEx(BootOptions, "HAL=", &OptionLength);
726     if (Option && (OptionLength > 4))
727     {
728         /* Retrieve the HAL file name */
729         Option += 4; OptionLength -= 4;
730         RtlStringCbCopyNA(HalFileName, sizeof(HalFileName), Option, OptionLength);
731         _strlwr(HalFileName);
732     }
733 
734     Option = NtLdrGetOptionEx(BootOptions, "KERNEL=", &OptionLength);
735     if (Option && (OptionLength > 7))
736     {
737         /* Retrieve the KERNEL file name */
738         Option += 7; OptionLength -= 7;
739         RtlStringCbCopyNA(KernelFileName, sizeof(KernelFileName), Option, OptionLength);
740         _strlwr(KernelFileName);
741     }
742 
743 #ifdef _M_IX86
744     /* Check for PAE support and select the adequate kernel image */
745     PaeModeOn = WinLdrIsPaeSupported(OperatingSystemVersion,
746                                      LoaderBlock,
747                                      BootOptions,
748                                      HalFileName,
749                                      KernelFileName,
750                                      sizeof(KernelFileName));
751     if (PaeModeOn) FIXME("WinLdrIsPaeSupported: PaeModeOn\n");
752 #endif
753 
754     TRACE("HAL file = '%s' ; Kernel file = '%s'\n", HalFileName, KernelFileName);
755 
756     /*
757      * Load the core NT files: Kernel, HAL and KD transport DLL.
758      * Cheat about their base DLL name so as to satisfy the imports/exports,
759      * even if the corresponding underlying files do not have the same names
760      * -- this happens e.g. with UP vs. MP kernel, standard vs. ACPI hal, or
761      * different KD transport DLLs.
762      */
763 
764     /* Load the Kernel */
765     KernelBase = LoadModule(LoaderBlock, DirPath, KernelFileName,
766                             "ntoskrnl.exe", LoaderSystemCode, KernelDTE, 30);
767     if (!KernelBase)
768     {
769         ERR("LoadModule('%s') failed\n", KernelFileName);
770         UiMessageBox("Could not load %s", KernelFileName);
771         return FALSE;
772     }
773 
774     /* Load the HAL */
775     HalBase = LoadModule(LoaderBlock, DirPath, HalFileName,
776                          "hal.dll", LoaderHalCode, &HalDTE, 35);
777     if (!HalBase)
778     {
779         ERR("LoadModule('%s') failed\n", HalFileName);
780         UiMessageBox("Could not load %s", HalFileName);
781         PeLdrFreeDataTableEntry(*KernelDTE);
782         MmFreeMemory(KernelBase);
783         return FALSE;
784     }
785 
786     /* Load the Kernel Debugger Transport DLL */
787     if (OperatingSystemVersion > _WIN32_WINNT_WIN2K)
788     {
789         /*
790          * According to http://www.nynaeve.net/?p=173 :
791          * "[...] Another enhancement that could be done Microsoft-side would be
792          * a better interface for replacing KD transport modules. Right now, due
793          * to the fact that ntoskrnl is static linked to KDCOM.DLL, the OS loader
794          * has a hardcoded hack that interprets the KD type in the OS loader options,
795          * loads one of the (hardcoded filenames) "kdcom.dll", "kd1394.dll", or
796          * "kdusb2.dll" modules, and inserts them into the loaded module list under
797          * the name "kdcom.dll". [...]"
798          */
799 
800         /*
801          * A Kernel Debugger Transport DLL is always loaded for Windows XP+ :
802          * either the standard KDCOM.DLL (by default): IsCustomKdDll == FALSE
803          * or an alternative user-provided one via the /DEBUGPORT= option:
804          * IsCustomKdDll == TRUE if it does not specify the default KDCOM.
805          */
806         BOOLEAN IsCustomKdDll = FALSE;
807 
808         /* Check whether there is a DEBUGPORT option */
809         Option = NtLdrGetOptionEx(BootOptions, "DEBUGPORT=", &OptionLength);
810         if (Option && (OptionLength > 10))
811         {
812             /* Move to the debug port name */
813             Option += 10; OptionLength -= 10;
814 
815             /*
816              * Parse the port name.
817              * Format: /DEBUGPORT=COM[0-9]
818              * or: /DEBUGPORT=FILE:\Device\HarddiskX\PartitionY\debug.log
819              * or: /DEBUGPORT=FOO
820              * If we only have /DEBUGPORT= (i.e. without any port name),
821              * default to "COM".
822              */
823 
824             /* Get the actual length of the debug port
825              * until the next whitespace or colon. */
826             OptionLength = (ULONG)strcspn(Option, " \t:");
827 
828             if ((OptionLength == 0) ||
829                 ( (OptionLength >= 3) && (_strnicmp(Option, "COM", 3) == 0) &&
830                  ((OptionLength == 3) || ('0' <= Option[3] && Option[3] <= '9')) ))
831             {
832                 /* The standard KDCOM.DLL is used */
833             }
834             else
835             {
836                 /* A custom KD DLL is used */
837                 IsCustomKdDll = TRUE;
838             }
839         }
840         if (!IsCustomKdDll)
841         {
842             Option = "COM"; OptionLength = 3;
843         }
844 
845         RtlStringCbPrintfA(KdDllName, sizeof(KdDllName), "kd%.*s.dll",
846                            OptionLength, Option);
847         _strlwr(KdDllName);
848 
849         /* Load the KD DLL. Override its base DLL name to the default "KDCOM.DLL". */
850         KdDllBase = LoadModule(LoaderBlock, DirPath, KdDllName,
851                                "kdcom.dll", LoaderSystemCode, &KdDllDTE, 40);
852         if (!KdDllBase)
853         {
854             /* If we failed to load a custom KD DLL, fall back to the standard one */
855             if (IsCustomKdDll)
856             {
857                 /* The custom KD DLL being optional, just ignore the failure */
858                 WARN("LoadModule('%s') failed\n", KdDllName);
859 
860                 IsCustomKdDll = FALSE;
861                 RtlStringCbCopyA(KdDllName, sizeof(KdDllName), "kdcom.dll");
862 
863                 KdDllBase = LoadModule(LoaderBlock, DirPath, KdDllName,
864                                        "kdcom.dll", LoaderSystemCode, &KdDllDTE, 40);
865             }
866 
867             if (!KdDllBase)
868             {
869                 /* Ignore the failure; we will fail later when scanning the
870                  * kernel import tables, if it really needs the KD DLL. */
871                 ERR("LoadModule('%s') failed\n", KdDllName);
872             }
873         }
874     }
875 
876     /* Load all referenced DLLs for Kernel, HAL and Kernel Debugger Transport DLL */
877     Success = PeLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, *KernelDTE);
878     if (!Success)
879     {
880         UiMessageBox("Could not load %s", KernelFileName);
881         goto Quit;
882     }
883     Success = PeLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, HalDTE);
884     if (!Success)
885     {
886         UiMessageBox("Could not load %s", HalFileName);
887         goto Quit;
888     }
889     if (KdDllDTE)
890     {
891         Success = PeLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, KdDllDTE);
892         if (!Success)
893         {
894             UiMessageBox("Could not load %s", KdDllName);
895             goto Quit;
896         }
897     }
898 
899 Quit:
900     if (!Success)
901     {
902         /* Cleanup and bail out */
903         if (KdDllDTE)
904             PeLdrFreeDataTableEntry(KdDllDTE);
905         if (KdDllBase) // Optional
906             MmFreeMemory(KdDllBase);
907 
908         PeLdrFreeDataTableEntry(HalDTE);
909         MmFreeMemory(HalBase);
910 
911         PeLdrFreeDataTableEntry(*KernelDTE);
912         MmFreeMemory(KernelBase);
913     }
914 
915     return Success;
916 }
917 
918 static
919 BOOLEAN
920 WinLdrInitErrataInf(
921     IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,
922     IN USHORT OperatingSystemVersion,
923     IN PCSTR SystemRoot)
924 {
925     LONG rc;
926     HKEY hKey;
927     ULONG BufferSize;
928     ULONG FileSize;
929     PVOID PhysicalBase;
930     WCHAR szFileName[80];
931     CHAR ErrataFilePath[MAX_PATH];
932 
933     /* Open either the 'BiosInfo' (Windows <= 2003) or the 'Errata' (Vista+) key */
934     if (OperatingSystemVersion >= _WIN32_WINNT_VISTA)
935     {
936         rc = RegOpenKey(CurrentControlSetKey, L"Control\\Errata", &hKey);
937     }
938     else // (OperatingSystemVersion <= _WIN32_WINNT_WS03)
939     {
940         rc = RegOpenKey(CurrentControlSetKey, L"Control\\BiosInfo", &hKey);
941     }
942     if (rc != ERROR_SUCCESS)
943     {
944         WARN("Could not open the BiosInfo/Errata registry key (Error %u)\n", (int)rc);
945         return FALSE;
946     }
947 
948     /* Retrieve the INF file name value */
949     BufferSize = sizeof(szFileName);
950     rc = RegQueryValue(hKey, L"InfName", NULL, (PUCHAR)szFileName, &BufferSize);
951     if (rc != ERROR_SUCCESS)
952     {
953         WARN("Could not retrieve the InfName value (Error %u)\n", (int)rc);
954         RegCloseKey(hKey);
955         return FALSE;
956     }
957 
958     // TODO: "SystemBiosDate"
959 
960     RegCloseKey(hKey);
961 
962     RtlStringCbPrintfA(ErrataFilePath, sizeof(ErrataFilePath), "%s%s%S",
963                        SystemRoot, "inf\\", szFileName);
964 
965     /* Load the INF file */
966     PhysicalBase = WinLdrLoadModule(ErrataFilePath, &FileSize, LoaderRegistryData);
967     if (!PhysicalBase)
968     {
969         WARN("Could not load '%s'\n", ErrataFilePath);
970         return FALSE;
971     }
972 
973     LoaderBlock->Extension->EmInfFileImage = PaToVa(PhysicalBase);
974     LoaderBlock->Extension->EmInfFileSize  = FileSize;
975 
976     return TRUE;
977 }
978 
979 ARC_STATUS
980 LoadAndBootWindows(
981     IN ULONG Argc,
982     IN PCHAR Argv[],
983     IN PCHAR Envp[])
984 {
985     ARC_STATUS Status;
986     PCSTR ArgValue;
987     PCSTR SystemPartition;
988     PCSTR FileName;
989     ULONG FileNameLength;
990     BOOLEAN Success;
991     USHORT OperatingSystemVersion;
992     PLOADER_PARAMETER_BLOCK LoaderBlock;
993     CHAR BootPath[MAX_PATH];
994     CHAR FilePath[MAX_PATH];
995     CHAR BootOptions[256];
996 
997     /* Retrieve the (mandatory) boot type */
998     ArgValue = GetArgumentValue(Argc, Argv, "BootType");
999     if (!ArgValue || !*ArgValue)
1000     {
1001         ERR("No 'BootType' value, aborting!\n");
1002         return EINVAL;
1003     }
1004 
1005     /* Convert it to an OS version */
1006     if (_stricmp(ArgValue, "Windows") == 0 ||
1007         _stricmp(ArgValue, "Windows2003") == 0)
1008     {
1009         OperatingSystemVersion = _WIN32_WINNT_WS03;
1010     }
1011     else if (_stricmp(ArgValue, "WindowsNT40") == 0)
1012     {
1013         OperatingSystemVersion = _WIN32_WINNT_NT4;
1014     }
1015     else if (_stricmp(ArgValue, "WindowsVista") == 0)
1016     {
1017         OperatingSystemVersion = _WIN32_WINNT_VISTA;
1018     }
1019     else
1020     {
1021         ERR("Unknown 'BootType' value '%s', aborting!\n", ArgValue);
1022         return EINVAL;
1023     }
1024 
1025     /* Retrieve the (mandatory) system partition */
1026     SystemPartition = GetArgumentValue(Argc, Argv, "SystemPartition");
1027     if (!SystemPartition || !*SystemPartition)
1028     {
1029         ERR("No 'SystemPartition' specified, aborting!\n");
1030         return EINVAL;
1031     }
1032 
1033     /* Let the user know we started loading */
1034     UiDrawBackdrop();
1035     UiDrawStatusText("Loading...");
1036     UiDrawProgressBarCenter("Loading NT...");
1037 
1038     /* Retrieve the system path */
1039     *BootPath = ANSI_NULL;
1040     ArgValue = GetArgumentValue(Argc, Argv, "SystemPath");
1041     if (ArgValue)
1042         RtlStringCbCopyA(BootPath, sizeof(BootPath), ArgValue);
1043 
1044     /*
1045      * Check whether BootPath is a full path
1046      * and if not, create a full boot path.
1047      *
1048      * See FsOpenFile for the technique used.
1049      */
1050     if (strrchr(BootPath, ')') == NULL)
1051     {
1052         /* Temporarily save the boot path */
1053         RtlStringCbCopyA(FilePath, sizeof(FilePath), BootPath);
1054 
1055         /* This is not a full path: prepend the SystemPartition */
1056         RtlStringCbCopyA(BootPath, sizeof(BootPath), SystemPartition);
1057 
1058         /* Append a path separator if needed */
1059         if (*FilePath != '\\' && *FilePath != '/')
1060             RtlStringCbCatA(BootPath, sizeof(BootPath), "\\");
1061 
1062         /* Append the remaining path */
1063         RtlStringCbCatA(BootPath, sizeof(BootPath), FilePath);
1064     }
1065 
1066     /* Append a path separator if needed */
1067     if (!*BootPath || BootPath[strlen(BootPath) - 1] != '\\')
1068         RtlStringCbCatA(BootPath, sizeof(BootPath), "\\");
1069 
1070     TRACE("BootPath: '%s'\n", BootPath);
1071 
1072     /* Retrieve the boot options */
1073     *BootOptions = ANSI_NULL;
1074     ArgValue = GetArgumentValue(Argc, Argv, "Options");
1075     if (ArgValue && *ArgValue)
1076         RtlStringCbCopyA(BootOptions, sizeof(BootOptions), ArgValue);
1077 
1078     /* Append boot-time options */
1079     AppendBootTimeOptions(BootOptions);
1080 
1081     /*
1082      * Set the "/HAL=" and "/KERNEL=" options if needed.
1083      * If already present on the standard "Options=" option line, they take
1084      * precedence over those passed via the separate "Hal=" and "Kernel="
1085      * options.
1086      */
1087     if (!NtLdrGetOption(BootOptions, "HAL="))
1088     {
1089         /*
1090          * Not found in the options, try to retrieve the
1091          * separate value and append it to the options.
1092          */
1093         ArgValue = GetArgumentValue(Argc, Argv, "Hal");
1094         if (ArgValue && *ArgValue)
1095         {
1096             RtlStringCbCatA(BootOptions, sizeof(BootOptions), " /HAL=");
1097             RtlStringCbCatA(BootOptions, sizeof(BootOptions), ArgValue);
1098         }
1099     }
1100     if (!NtLdrGetOption(BootOptions, "KERNEL="))
1101     {
1102         /*
1103          * Not found in the options, try to retrieve the
1104          * separate value and append it to the options.
1105          */
1106         ArgValue = GetArgumentValue(Argc, Argv, "Kernel");
1107         if (ArgValue && *ArgValue)
1108         {
1109             RtlStringCbCatA(BootOptions, sizeof(BootOptions), " /KERNEL=");
1110             RtlStringCbCatA(BootOptions, sizeof(BootOptions), ArgValue);
1111         }
1112     }
1113 
1114     TRACE("BootOptions: '%s'\n", BootOptions);
1115 
1116     /* Check if a RAM disk file was given */
1117     FileName = NtLdrGetOptionEx(BootOptions, "RDPATH=", &FileNameLength);
1118     if (FileName && (FileNameLength > 7))
1119     {
1120         /* Load the RAM disk */
1121         Status = RamDiskInitialize(FALSE, BootOptions, SystemPartition);
1122         if (Status != ESUCCESS)
1123         {
1124             FileName += 7; FileNameLength -= 7;
1125             UiMessageBox("Failed to load RAM disk file '%.*s'",
1126                          FileNameLength, FileName);
1127             return Status;
1128         }
1129     }
1130 
1131     /* Handle the SOS option */
1132     SosEnabled = !!NtLdrGetOption(BootOptions, "SOS");
1133     if (SosEnabled)
1134         UiResetForSOS();
1135 
1136     /* Allocate and minimally-initialize the Loader Parameter Block */
1137     AllocateAndInitLPB(OperatingSystemVersion, &LoaderBlock);
1138 
1139     /* Load the system hive */
1140     UiUpdateProgressBar(15, "Loading system hive...");
1141     Success = WinLdrInitSystemHive(LoaderBlock, BootPath, FALSE);
1142     TRACE("SYSTEM hive %s\n", (Success ? "loaded" : "not loaded"));
1143     /* Bail out if failure */
1144     if (!Success)
1145         return ENOEXEC;
1146 
1147     /* Fixup the version number using data from the registry */
1148     if (OperatingSystemVersion == 0)
1149         OperatingSystemVersion = WinLdrDetectVersion();
1150     LoaderBlock->Extension->MajorVersion = (OperatingSystemVersion & 0xFF00) >> 8;
1151     LoaderBlock->Extension->MinorVersion = (OperatingSystemVersion & 0xFF);
1152 
1153     /* Load NLS data, OEM font, and prepare boot drivers list */
1154     Success = WinLdrScanSystemHive(LoaderBlock, BootPath);
1155     TRACE("SYSTEM hive %s\n", (Success ? "scanned" : "not scanned"));
1156     /* Bail out if failure */
1157     if (!Success)
1158         return ENOEXEC;
1159 
1160     /* Load the Firmware Errata file */
1161     Success = WinLdrInitErrataInf(LoaderBlock, OperatingSystemVersion, BootPath);
1162     TRACE("Firmware Errata file %s\n", (Success ? "loaded" : "not loaded"));
1163     /* Not necessarily fatal if not found - carry on going */
1164 
1165     /* Finish loading */
1166     return LoadAndBootWindowsCommon(OperatingSystemVersion,
1167                                     LoaderBlock,
1168                                     BootOptions,
1169                                     BootPath);
1170 }
1171 
1172 ARC_STATUS
1173 LoadAndBootWindowsCommon(
1174     IN USHORT OperatingSystemVersion,
1175     IN PLOADER_PARAMETER_BLOCK LoaderBlock,
1176     IN PCSTR BootOptions,
1177     IN PCSTR BootPath)
1178 {
1179     PLOADER_PARAMETER_BLOCK LoaderBlockVA;
1180     BOOLEAN Success;
1181     PLDR_DATA_TABLE_ENTRY KernelDTE;
1182     KERNEL_ENTRY_POINT KiSystemStartup;
1183     PCSTR SystemRoot;
1184 
1185     TRACE("LoadAndBootWindowsCommon()\n");
1186 
1187     ASSERT(OperatingSystemVersion != 0);
1188 
1189 #ifdef _M_IX86
1190     /* Setup redirection support */
1191     WinLdrSetupEms(BootOptions);
1192 #endif
1193 
1194     /* Convert BootPath to SystemRoot */
1195     SystemRoot = strstr(BootPath, "\\");
1196 
1197     /* Detect hardware */
1198     UiUpdateProgressBar(20, "Detecting hardware...");
1199     LoaderBlock->ConfigurationRoot = MachHwDetect(BootOptions);
1200 
1201     /* Initialize the PE loader import-DLL callback, so that we can obtain
1202      * feedback (for example during SOS) on the PE images that get loaded. */
1203     PeLdrImportDllLoadCallback = NtLdrImportDllLoadCallback;
1204 
1205     /* Load the operating system core: the Kernel, the HAL and the Kernel Debugger Transport DLL */
1206     Success = LoadWindowsCore(OperatingSystemVersion,
1207                               LoaderBlock,
1208                               BootOptions,
1209                               BootPath,
1210                               &KernelDTE);
1211     if (!Success)
1212     {
1213         /* Reset the PE loader import-DLL callback */
1214         PeLdrImportDllLoadCallback = NULL;
1215 
1216         UiMessageBox("Error loading NTOS core.");
1217         return ENOEXEC;
1218     }
1219 
1220     /* Cleanup INI file */
1221     IniCleanup();
1222 
1223 /****
1224  **** WE HAVE NOW REACHED THE POINT OF NO RETURN !!
1225  ****/
1226 
1227     UiSetProgressBarSubset(40, 90); // NTOS goes from 25 to 75%
1228 
1229     /* Load boot drivers */
1230     UiSetProgressBarText("Loading boot drivers...");
1231     Success = WinLdrLoadBootDrivers(LoaderBlock, BootPath);
1232     TRACE("Boot drivers loading %s\n", Success ? "successful" : "failed");
1233 
1234     UiSetProgressBarSubset(0, 100);
1235 
1236     /* Reset the PE loader import-DLL callback */
1237     PeLdrImportDllLoadCallback = NULL;
1238 
1239     /* Initialize Phase 1 - no drivers loading anymore */
1240     WinLdrInitializePhase1(LoaderBlock,
1241                            BootOptions,
1242                            SystemRoot,
1243                            BootPath,
1244                            OperatingSystemVersion);
1245 
1246     UiUpdateProgressBar(100, NULL);
1247 
1248     /* Save entry-point pointer and Loader block VAs */
1249     KiSystemStartup = (KERNEL_ENTRY_POINT)KernelDTE->EntryPoint;
1250     LoaderBlockVA = PaToVa(LoaderBlock);
1251 
1252     /* "Stop all motors", change videomode */
1253     MachPrepareForReactOS();
1254 
1255     /* Debugging... */
1256     //DumpMemoryAllocMap();
1257 
1258     /* Do the machine specific initialization */
1259     WinLdrSetupMachineDependent(LoaderBlock);
1260 
1261     /* Map pages and create memory descriptors */
1262     WinLdrSetupMemoryLayout(LoaderBlock);
1263 
1264     /* Set processor context */
1265     WinLdrSetProcessorContext(OperatingSystemVersion);
1266 
1267     /* Save final value of LoaderPagesSpanned */
1268     LoaderBlock->Extension->LoaderPagesSpanned = LoaderPagesSpanned;
1269 
1270     TRACE("Hello from paged mode, KiSystemStartup %p, LoaderBlockVA %p!\n",
1271           KiSystemStartup, LoaderBlockVA);
1272 
1273     /* Zero KI_USER_SHARED_DATA page */
1274     RtlZeroMemory((PVOID)KI_USER_SHARED_DATA, MM_PAGE_SIZE);
1275 
1276     WinLdrpDumpMemoryDescriptors(LoaderBlockVA);
1277     WinLdrpDumpBootDriver(LoaderBlockVA);
1278 #ifndef _M_AMD64
1279     WinLdrpDumpArcDisks(LoaderBlockVA);
1280 #endif
1281 
1282     /* Pass control */
1283     (*KiSystemStartup)(LoaderBlockVA);
1284 
1285     UNREACHABLE; // return ESUCCESS;
1286 }
1287 
1288 VOID
1289 WinLdrpDumpMemoryDescriptors(PLOADER_PARAMETER_BLOCK LoaderBlock)
1290 {
1291     PLIST_ENTRY NextMd;
1292     PMEMORY_ALLOCATION_DESCRIPTOR MemoryDescriptor;
1293 
1294     NextMd = LoaderBlock->MemoryDescriptorListHead.Flink;
1295 
1296     while (NextMd != &LoaderBlock->MemoryDescriptorListHead)
1297     {
1298         MemoryDescriptor = CONTAINING_RECORD(NextMd, MEMORY_ALLOCATION_DESCRIPTOR, ListEntry);
1299 
1300         TRACE("BP %08X PC %04X MT %d\n", MemoryDescriptor->BasePage,
1301             MemoryDescriptor->PageCount, MemoryDescriptor->MemoryType);
1302 
1303         NextMd = MemoryDescriptor->ListEntry.Flink;
1304     }
1305 }
1306 
1307 VOID
1308 WinLdrpDumpBootDriver(PLOADER_PARAMETER_BLOCK LoaderBlock)
1309 {
1310     PLIST_ENTRY NextBd;
1311     PBOOT_DRIVER_LIST_ENTRY BootDriver;
1312 
1313     NextBd = LoaderBlock->BootDriverListHead.Flink;
1314 
1315     while (NextBd != &LoaderBlock->BootDriverListHead)
1316     {
1317         BootDriver = CONTAINING_RECORD(NextBd, BOOT_DRIVER_LIST_ENTRY, Link);
1318 
1319         TRACE("BootDriver %wZ DTE %08X RegPath: %wZ\n", &BootDriver->FilePath,
1320             BootDriver->LdrEntry, &BootDriver->RegistryPath);
1321 
1322         NextBd = BootDriver->Link.Flink;
1323     }
1324 }
1325 
1326 VOID
1327 WinLdrpDumpArcDisks(PLOADER_PARAMETER_BLOCK LoaderBlock)
1328 {
1329     PLIST_ENTRY NextBd;
1330     PARC_DISK_SIGNATURE ArcDisk;
1331 
1332     NextBd = LoaderBlock->ArcDiskInformation->DiskSignatureListHead.Flink;
1333 
1334     while (NextBd != &LoaderBlock->ArcDiskInformation->DiskSignatureListHead)
1335     {
1336         ArcDisk = CONTAINING_RECORD(NextBd, ARC_DISK_SIGNATURE, ListEntry);
1337 
1338         TRACE("ArcDisk %s checksum: 0x%X, signature: 0x%X\n",
1339             ArcDisk->ArcName, ArcDisk->CheckSum, ArcDisk->Signature);
1340 
1341         NextBd = ArcDisk->ListEntry.Flink;
1342     }
1343 }
1344