[COMCTL32] Pt.rc: Place accelerator FIXMEs
[reactos.git] / boot / freeldr / freeldr / ntldr / winldr.c
index 6c8da87..7e02fcf 100644 (file)
@@ -1,29 +1,16 @@
 /*
- *  FreeLoader
- *
- *  Copyright (C) 1998-2003  Brian Palmer    <brianp@sginet.com>
- *  Copyright (C) 2006       Aleksey Bragin  <aleksey@reactos.org>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * PROJECT:     FreeLoader
+ * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE:     Windows-compatible NT OS Loader.
+ * COPYRIGHT:   Copyright 2006-2019 Aleksey Bragin <aleksey@reactos.org>
  */
 
 #include <freeldr.h>
+#include <ndk/ldrtypes.h>
 #include "winldr.h"
+#include "ntldropts.h"
 #include "registry.h"
-
-#include <ndk/ldrtypes.h>
+#include <internal/cmboot.h>
 
 #include <debug.h>
 DBG_DEFAULT_CHANNEL(WINDOWS);
@@ -37,20 +24,66 @@ extern BOOLEAN AcpiPresent;
 
 extern HEADLESS_LOADER_BLOCK LoaderRedirectionInformation;
 extern BOOLEAN WinLdrTerminalConnected;
-extern void WinLdrSetupEms(IN PCHAR BootOptions);
+extern VOID WinLdrSetupEms(IN PCSTR BootOptions);
 
 PLOADER_SYSTEM_BLOCK WinLdrSystemBlock;
+/**/PCWSTR BootFileSystem = NULL;/**/
+
+BOOLEAN VirtualBias = FALSE;
+BOOLEAN SosEnabled = FALSE;
+BOOLEAN SafeBoot = FALSE;
+BOOLEAN BootLogo = FALSE;
+#ifdef _M_IX86
+BOOLEAN PaeModeOn = FALSE;
+#endif
+BOOLEAN NoExecuteEnabled = FALSE;
 
 // debug stuff
 VOID DumpMemoryAllocMap(VOID);
 
+/* PE loader import-DLL loading callback */
+static VOID
+NTAPI
+NtLdrImportDllLoadCallback(
+    _In_ PCSTR FileName)
+{
+    NtLdrOutputLoadMsg(FileName, NULL);
+}
+
+VOID
+NtLdrOutputLoadMsg(
+    _In_ PCSTR FileName,
+    _In_opt_ PCSTR Description)
+{
+    if (SosEnabled)
+    {
+        printf("  %s\n", FileName);
+        TRACE("Loading: %s\n", FileName);
+    }
+    else
+    {
+        /* Inform the user we load a file */
+        CHAR ProgressString[256];
+
+        RtlStringCbPrintfA(ProgressString, sizeof(ProgressString),
+                           "Loading %s...",
+                           (Description ? Description : FileName));
+        // UiSetProgressBarText(ProgressString);
+        // UiIndicateProgress();
+        UiDrawStatusText(ProgressString);
+    }
+}
+
 // Init "phase 0"
 VOID
-AllocateAndInitLPB(PLOADER_PARAMETER_BLOCK *OutLoaderBlock)
+AllocateAndInitLPB(
+    IN USHORT VersionToBoot,
+    OUT PLOADER_PARAMETER_BLOCK* OutLoaderBlock)
 {
     PLOADER_PARAMETER_BLOCK LoaderBlock;
+    PLOADER_PARAMETER_EXTENSION Extension;
 
-    /* Allocate and zero-init the LPB */
+    /* Allocate and zero-init the Loader Parameter Block */
     WinLdrSystemBlock = MmAllocateMemoryWithType(sizeof(LOADER_SYSTEM_BLOCK),
                                                  LoaderSystemBlock);
     if (WinLdrSystemBlock == NULL)
@@ -64,6 +97,13 @@ AllocateAndInitLPB(PLOADER_PARAMETER_BLOCK *OutLoaderBlock)
     LoaderBlock = &WinLdrSystemBlock->LoaderBlock;
     LoaderBlock->NlsData = &WinLdrSystemBlock->NlsDataBlock;
 
+    /* Initialize the Loader Block Extension */
+    Extension = &WinLdrSystemBlock->Extension;
+    LoaderBlock->Extension = Extension;
+    Extension->Size = sizeof(LOADER_PARAMETER_EXTENSION);
+    Extension->MajorVersion = (VersionToBoot & 0xFF00) >> 8;
+    Extension->MinorVersion = (VersionToBoot & 0xFF);
+
     /* Init three critical lists, used right away */
     InitializeListHead(&LoaderBlock->LoadOrderListHead);
     InitializeListHead(&LoaderBlock->MemoryDescriptorListHead);
@@ -75,56 +115,74 @@ AllocateAndInitLPB(PLOADER_PARAMETER_BLOCK *OutLoaderBlock)
 // Init "phase 1"
 VOID
 WinLdrInitializePhase1(PLOADER_PARAMETER_BLOCK LoaderBlock,
-                       LPCSTR Options,
-                       LPCSTR SystemRoot,
-                       LPCSTR BootPath,
+                       PCSTR Options,
+                       PCSTR SystemRoot,
+                       PCSTR BootPath,
                        USHORT VersionToBoot)
 {
-    /* Examples of correct options and paths */
-    //CHAR    Options[] = "/DEBUGPORT=COM1 /BAUDRATE=115200";
-    //CHAR    Options[] = "/NODEBUG";
-    //CHAR    SystemRoot[] = "\\WINNT\\";
-    //CHAR    ArcBoot[] = "multi(0)disk(0)rdisk(0)partition(1)";
+    /*
+     * Examples of correct options and paths:
+     * CHAR Options[] = "/DEBUGPORT=COM1 /BAUDRATE=115200";
+     * CHAR Options[] = "/NODEBUG";
+     * CHAR SystemRoot[] = "\\WINNT\\";
+     * CHAR ArcBoot[] = "multi(0)disk(0)rdisk(0)partition(1)";
+     */
 
-    LPSTR LoadOptions, NewLoadOptions;
+    PSTR  LoadOptions, NewLoadOptions;
     CHAR  HalPath[] = "\\";
-    CHAR  ArcBoot[256];
-    CHAR  MiscFiles[256];
+    CHAR  ArcBoot[MAX_PATH+1];
+    CHAR  MiscFiles[MAX_PATH+1];
     ULONG i;
     ULONG_PTR PathSeparator;
     PLOADER_PARAMETER_EXTENSION Extension;
 
     /* Construct SystemRoot and ArcBoot from SystemPath */
     PathSeparator = strstr(BootPath, "\\") - BootPath;
-    strncpy(ArcBoot, BootPath, PathSeparator);
-    ArcBoot[PathSeparator] = ANSI_NULL;
+    RtlStringCbCopyNA(ArcBoot, sizeof(ArcBoot), BootPath, PathSeparator);
 
-    TRACE("ArcBoot: %s\n", ArcBoot);
-    TRACE("SystemRoot: %s\n", SystemRoot);
-    TRACE("Options: %s\n", Options);
+    TRACE("ArcBoot: '%s'\n", ArcBoot);
+    TRACE("SystemRoot: '%s'\n", SystemRoot);
+    TRACE("Options: '%s'\n", Options);
 
-    /* Fill Arc BootDevice */
+    /* Fill ARC BootDevice */
     LoaderBlock->ArcBootDeviceName = WinLdrSystemBlock->ArcBootDeviceName;
-    strncpy(LoaderBlock->ArcBootDeviceName, ArcBoot, MAX_PATH);
+    RtlStringCbCopyA(LoaderBlock->ArcBootDeviceName, sizeof(WinLdrSystemBlock->ArcBootDeviceName), ArcBoot);
     LoaderBlock->ArcBootDeviceName = PaToVa(LoaderBlock->ArcBootDeviceName);
 
-    /* Fill Arc HalDevice, it matches ArcBoot path */
+//
+// IMPROVE!!
+// SetupBlock->ArcSetupDeviceName must be the path to the setup **SOURCE**,
+// and not the setup boot path. Indeed they may differ!!
+//
+    if (LoaderBlock->SetupLdrBlock)
+    {
+        PSETUP_LOADER_BLOCK SetupBlock = LoaderBlock->SetupLdrBlock;
+
+        /* Adjust the ARC path in the setup block - Matches ArcBoot path */
+        SetupBlock->ArcSetupDeviceName = WinLdrSystemBlock->ArcBootDeviceName;
+        SetupBlock->ArcSetupDeviceName = PaToVa(SetupBlock->ArcSetupDeviceName);
+
+        /* Convert the setup block pointer */
+        LoaderBlock->SetupLdrBlock = PaToVa(LoaderBlock->SetupLdrBlock);
+    }
+
+    /* Fill ARC HalDevice, it matches ArcBoot path */
     LoaderBlock->ArcHalDeviceName = WinLdrSystemBlock->ArcBootDeviceName;
     LoaderBlock->ArcHalDeviceName = PaToVa(LoaderBlock->ArcHalDeviceName);
 
     /* Fill SystemRoot */
     LoaderBlock->NtBootPathName = WinLdrSystemBlock->NtBootPathName;
-    strncpy(LoaderBlock->NtBootPathName, SystemRoot, MAX_PATH);
+    RtlStringCbCopyA(LoaderBlock->NtBootPathName, sizeof(WinLdrSystemBlock->NtBootPathName), SystemRoot);
     LoaderBlock->NtBootPathName = PaToVa(LoaderBlock->NtBootPathName);
 
     /* Fill NtHalPathName */
     LoaderBlock->NtHalPathName = WinLdrSystemBlock->NtHalPathName;
-    strncpy(LoaderBlock->NtHalPathName, HalPath, MAX_PATH);
+    RtlStringCbCopyA(LoaderBlock->NtHalPathName, sizeof(WinLdrSystemBlock->NtHalPathName), HalPath);
     LoaderBlock->NtHalPathName = PaToVa(LoaderBlock->NtHalPathName);
 
-    /* Fill LoadOptions and strip the '/' commutator symbol in front of each option */
+    /* Fill LoadOptions and strip the '/' switch symbol in front of each option */
     NewLoadOptions = LoadOptions = LoaderBlock->LoadOptions = WinLdrSystemBlock->LoadOptions;
-    strncpy(LoaderBlock->LoadOptions, Options, MAX_OPTIONS_LENGTH);
+    RtlStringCbCopyA(LoaderBlock->LoadOptions, sizeof(WinLdrSystemBlock->LoadOptions), Options);
 
     do
     {
@@ -136,7 +194,7 @@ WinLdrInitializePhase1(PLOADER_PARAMETER_BLOCK LoaderBlock,
 
     LoaderBlock->LoadOptions = PaToVa(LoaderBlock->LoadOptions);
 
-    /* Arc devices */
+    /* ARC devices */
     LoaderBlock->ArcDiskInformation = &WinLdrSystemBlock->ArcDiskInformation;
     InitializeListHead(&LoaderBlock->ArcDiskInformation->DiskSignatureListHead);
 
@@ -147,20 +205,25 @@ WinLdrInitializePhase1(PLOADER_PARAMETER_BLOCK LoaderBlock,
 
         /* Allocate the ARC structure */
         ArcDiskSig = FrLdrHeapAlloc(sizeof(ARC_DISK_SIGNATURE_EX), 'giSD');
+        if (!ArcDiskSig)
+        {
+            ERR("Failed to allocate ARC structure! Ignoring remaining ARC disks. (i = %lu, DiskCount = %lu)\n",
+                i, reactos_disk_count);
+            break;
+        }
 
         /* Copy the data over */
         RtlCopyMemory(ArcDiskSig, &reactos_arc_disk_info[i], sizeof(ARC_DISK_SIGNATURE_EX));
 
-        /* Set the ARC Name pointer and mark the partition table as valid */
+        /* Set the ARC Name pointer */
         ArcDiskSig->DiskSignature.ArcName = PaToVa(ArcDiskSig->ArcName);
-        ArcDiskSig->DiskSignature.ValidPartitionTable = TRUE;
 
         /* Insert into the list */
         InsertTailList(&LoaderBlock->ArcDiskInformation->DiskSignatureListHead,
                        &ArcDiskSig->DiskSignature.ListEntry);
     }
 
-    /* Convert all list's to Virtual address */
+    /* Convert all lists to Virtual address */
 
     /* Convert the ArcDisks list to virtual address */
     List_PaToVa(&LoaderBlock->ArcDiskInformation->DiskSignatureListHead);
@@ -179,11 +242,9 @@ WinLdrInitializePhase1(PLOADER_PARAMETER_BLOCK LoaderBlock,
     /* Convert list of boot drivers */
     List_PaToVa(&LoaderBlock->BootDriverListHead);
 
-    /* Initialize Extension now */
-    Extension = &WinLdrSystemBlock->Extension;
-    Extension->Size = sizeof(LOADER_PARAMETER_EXTENSION);
-    Extension->MajorVersion = (VersionToBoot & 0xFF00) >> 8;
-    Extension->MinorVersion = VersionToBoot & 0xFF;
+    Extension = LoaderBlock->Extension;
+
+    /* FIXME! HACK value for docking profile */
     Extension->Profile.Status = 2;
 
     /* Check if FreeLdr detected a ACPI table */
@@ -206,24 +267,21 @@ WinLdrInitializePhase1(PLOADER_PARAMETER_BLOCK LoaderBlock,
     }
 #endif
     /* Load drivers database */
-    strcpy(MiscFiles, BootPath);
-    strcat(MiscFiles, "AppPatch\\drvmain.sdb");
+    RtlStringCbCopyA(MiscFiles, sizeof(MiscFiles), BootPath);
+    RtlStringCbCatA(MiscFiles, sizeof(MiscFiles), "AppPatch\\drvmain.sdb");
     Extension->DrvDBImage = PaToVa(WinLdrLoadModule(MiscFiles,
                                                     &Extension->DrvDBSize,
                                                     LoaderRegistryData));
 
-    /* Convert extension and setup block pointers */
-    LoaderBlock->Extension = PaToVa(Extension);
-
-    if (LoaderBlock->SetupLdrBlock)
-        LoaderBlock->SetupLdrBlock = PaToVa(LoaderBlock->SetupLdrBlock);
+    /* Convert the extension block pointer */
+    LoaderBlock->Extension = PaToVa(LoaderBlock->Extension);
 
     TRACE("WinLdrInitializePhase1() completed\n");
 }
 
 static BOOLEAN
 WinLdrLoadDeviceDriver(PLIST_ENTRY LoadOrderListHead,
-                       LPCSTR BootPath,
+                       PCSTR BootPath,
                        PUNICODE_STRING FilePath,
                        ULONG Flags,
                        PLDR_DATA_TABLE_ENTRY *DriverDTE)
@@ -236,27 +294,27 @@ WinLdrLoadDeviceDriver(PLIST_ENTRY LoadOrderListHead,
     PVOID DriverBase = NULL;
 
     // Separate the path to file name and directory path
-    _snprintf(DriverPath, sizeof(DriverPath), "%wZ", FilePath);
+    RtlStringCbPrintfA(DriverPath, sizeof(DriverPath), "%wZ", FilePath);
     DriverNamePos = strrchr(DriverPath, '\\');
     if (DriverNamePos != NULL)
     {
         // Copy the name
-        strcpy(DllName, DriverNamePos+1);
+        RtlStringCbCopyA(DllName, sizeof(DllName), DriverNamePos+1);
 
         // Cut out the name from the path
-        *(DriverNamePos+1) = 0;
+        *(DriverNamePos+1) = ANSI_NULL;
     }
     else
     {
         // There is no directory in the path
-        strcpy(DllName, DriverPath);
-        DriverPath[0] = ANSI_NULL;
+        RtlStringCbCopyA(DllName, sizeof(DllName), DriverPath);
+        *DriverPath = ANSI_NULL;
     }
 
-    TRACE("DriverPath: %s, DllName: %s, LPB\n", DriverPath, DllName);
+    TRACE("DriverPath: '%s', DllName: '%s', LPB\n", DriverPath, DllName);
 
     // Check if driver is already loaded
-    Success = WinLdrCheckForLoadedDll(LoadOrderListHead, DllName, DriverDTE);
+    Success = PeLdrCheckForLoadedDll(LoadOrderListHead, DllName, DriverDTE);
     if (Success)
     {
         // We've got the pointer to its DTE, just return success
@@ -264,28 +322,41 @@ WinLdrLoadDeviceDriver(PLIST_ENTRY LoadOrderListHead,
     }
 
     // It's not loaded, we have to load it
-    _snprintf(FullPath, sizeof(FullPath), "%s%wZ", BootPath, FilePath);
-    Success = WinLdrLoadImage(FullPath, LoaderBootDriver, &DriverBase);
+    RtlStringCbPrintfA(FullPath, sizeof(FullPath), "%s%wZ", BootPath, FilePath);
+
+    NtLdrOutputLoadMsg(FullPath, NULL);
+    Success = PeLdrLoadImage(FullPath, LoaderBootDriver, &DriverBase);
     if (!Success)
+    {
+        ERR("PeLdrLoadImage('%s') failed\n", DllName);
         return FALSE;
+    }
 
     // Allocate a DTE for it
-    Success = WinLdrAllocateDataTableEntry(LoadOrderListHead, DllName, DllName, DriverBase, DriverDTE);
+    Success = PeLdrAllocateDataTableEntry(LoadOrderListHead, DllName, DllName, DriverBase, DriverDTE);
     if (!Success)
     {
-        ERR("WinLdrAllocateDataTableEntry() failed\n");
+        /* Cleanup and bail out */
+        ERR("PeLdrAllocateDataTableEntry('%s') failed\n", DllName);
+        MmFreeMemory(DriverBase);
         return FALSE;
     }
 
+    /* Init security cookie */
+    PeLdrInitSecurityCookie(*DriverDTE);
+
     // Modify any flags, if needed
     (*DriverDTE)->Flags |= Flags;
 
     // Look for any dependencies it may have, and load them too
-    sprintf(FullPath,"%s%s", BootPath, DriverPath);
-    Success = WinLdrScanImportDescriptorTable(LoadOrderListHead, FullPath, *DriverDTE);
+    RtlStringCbPrintfA(FullPath, sizeof(FullPath), "%s%s", BootPath, DriverPath);
+    Success = PeLdrScanImportDescriptorTable(LoadOrderListHead, FullPath, *DriverDTE);
     if (!Success)
     {
-        ERR("WinLdrScanImportDescriptorTable() failed for %s\n", FullPath);
+        /* Cleanup and bail out */
+        ERR("PeLdrScanImportDescriptorTable('%s') failed\n", FullPath);
+        PeLdrFreeDataTableEntry(*DriverDTE);
+        MmFreeMemory(DriverBase);
         return FALSE;
     }
 
@@ -294,51 +365,60 @@ WinLdrLoadDeviceDriver(PLIST_ENTRY LoadOrderListHead,
 
 BOOLEAN
 WinLdrLoadBootDrivers(PLOADER_PARAMETER_BLOCK LoaderBlock,
-                      LPCSTR BootPath)
+                      PCSTR BootPath)
 {
     PLIST_ENTRY NextBd;
+    PBOOT_DRIVER_NODE DriverNode;
     PBOOT_DRIVER_LIST_ENTRY BootDriver;
     BOOLEAN Success;
     BOOLEAN ret = TRUE;
 
-    // Walk through the boot drivers list
+    /* Walk through the boot drivers list */
     NextBd = LoaderBlock->BootDriverListHead.Flink;
-
     while (NextBd != &LoaderBlock->BootDriverListHead)
     {
-        BootDriver = CONTAINING_RECORD(NextBd, BOOT_DRIVER_LIST_ENTRY, Link);
+        DriverNode = CONTAINING_RECORD(NextBd,
+                                       BOOT_DRIVER_NODE,
+                                       ListEntry.Link);
+        BootDriver = &DriverNode->ListEntry;
 
-        TRACE("BootDriver %wZ DTE %08X RegPath: %wZ\n", &BootDriver->FilePath,
-            BootDriver->LdrEntry, &BootDriver->RegistryPath);
+        /* Get the next list entry as we may remove the current one on failure */
+        NextBd = BootDriver->Link.Flink;
+
+        TRACE("BootDriver %wZ DTE %08X RegPath: %wZ\n",
+              &BootDriver->FilePath, BootDriver->LdrEntry,
+              &BootDriver->RegistryPath);
 
         // Paths are relative (FIXME: Are they always relative?)
 
-        // Load it
+        /* Load it */
+        UiIndicateProgress();
         Success = WinLdrLoadDeviceDriver(&LoaderBlock->LoadOrderListHead,
                                          BootPath,
                                          &BootDriver->FilePath,
                                          0,
                                          &BootDriver->LdrEntry);
-
         if (Success)
         {
-            // Convert the RegistryPath and DTE addresses to VA since we are not going to use it anymore
+            /* Convert the addresses to VA since we are not going to use them anymore */
             BootDriver->RegistryPath.Buffer = PaToVa(BootDriver->RegistryPath.Buffer);
             BootDriver->FilePath.Buffer = PaToVa(BootDriver->FilePath.Buffer);
             BootDriver->LdrEntry = PaToVa(BootDriver->LdrEntry);
+
+            if (DriverNode->Group.Buffer)
+                DriverNode->Group.Buffer = PaToVa(DriverNode->Group.Buffer);
+            DriverNode->Name.Buffer = PaToVa(DriverNode->Name.Buffer);
         }
         else
         {
-            // Loading failed - cry loudly
-            ERR("Can't load boot driver '%wZ'!\n", &BootDriver->FilePath);
-            UiMessageBox("Can't load boot driver '%wZ'!", &BootDriver->FilePath);
+            /* Loading failed: cry loudly */
+            ERR("Cannot load boot driver '%wZ'!\n", &BootDriver->FilePath);
+            UiMessageBox("Cannot load boot driver '%wZ'!", &BootDriver->FilePath);
             ret = FALSE;
 
-            // Remove it from the list and try to continue
-            RemoveEntryList(NextBd);
+            /* Remove it from the list and try to continue */
+            RemoveEntryList(&BootDriver->Link);
         }
-
-        NextBd = BootDriver->Link.Flink;
     }
 
     return ret;
@@ -346,7 +426,7 @@ WinLdrLoadBootDrivers(PLOADER_PARAMETER_BLOCK LoaderBlock,
 
 PVOID
 WinLdrLoadModule(PCSTR ModuleName,
-                 ULONG *Size,
+                 PULONG Size,
                  TYPE_OF_MEMORY MemoryType)
 {
     ULONG FileId;
@@ -356,24 +436,19 @@ WinLdrLoadModule(PCSTR ModuleName,
     ARC_STATUS Status;
     ULONG BytesRead;
 
-    //CHAR ProgressString[256];
-
-    /* Inform user we are loading files */
-    //sprintf(ProgressString, "Loading %s...", FileName);
-    //UiDrawProgressBarCenter(1, 100, ProgressString);
-
-    TRACE("Loading module %s\n", ModuleName);
     *Size = 0;
 
     /* Open the image file */
-    Status = ArcOpen((PCHAR)ModuleName, OpenReadOnly, &FileId);
+    NtLdrOutputLoadMsg(ModuleName, NULL);
+    Status = ArcOpen((PSTR)ModuleName, OpenReadOnly, &FileId);
     if (Status != ESUCCESS)
     {
         /* In case of errors, we just return, without complaining to the user */
+        WARN("Error while opening '%s', Status: %u\n", ModuleName, Status);
         return NULL;
     }
 
-    /* Get this file's size */
+    /* Retrieve its size */
     Status = ArcGetFileInformation(FileId, &FileInfo);
     if (Status != ESUCCESS)
     {
@@ -387,15 +462,17 @@ WinLdrLoadModule(PCSTR ModuleName,
     PhysicalBase = MmAllocateMemoryWithType(FileSize, MemoryType);
     if (PhysicalBase == NULL)
     {
+        ERR("Could not allocate memory for '%s'\n", ModuleName);
         ArcClose(FileId);
         return NULL;
     }
 
-    /* Load whole file */
+    /* Load the whole file */
     Status = ArcRead(FileId, PhysicalBase, FileSize, &BytesRead);
     ArcClose(FileId);
     if (Status != ESUCCESS)
     {
+        WARN("Error while reading '%s', Status: %u\n", ModuleName, Status);
         return NULL;
     }
 
@@ -410,22 +487,20 @@ WinLdrDetectVersion(VOID)
     LONG rc;
     HKEY hKey;
 
-    rc = RegOpenKey(
-        NULL,
-        L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server",
-        &hKey);
+    rc = RegOpenKey(CurrentControlSetKey, L"Control\\Terminal Server", &hKey);
     if (rc != ERROR_SUCCESS)
     {
-        // Key doesn't exist; assume NT 4.0
+        /* Key doesn't exist; assume NT 4.0 */
         return _WIN32_WINNT_NT4;
     }
+    RegCloseKey(hKey);
 
-    // We may here want to read the value of ProductVersion
+    /* We may here want to read the value of ProductVersion */
     return _WIN32_WINNT_WS03;
 }
 
 static
-BOOLEAN
+PVOID
 LoadModule(
     IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,
     IN PCCH Path,
@@ -438,37 +513,97 @@ LoadModule(
     BOOLEAN Success;
     CHAR FullFileName[MAX_PATH];
     CHAR ProgressString[256];
-    PVOID BaseAddress = NULL;
+    PVOID BaseAddress;
 
-    UiDrawBackdrop();
-    sprintf(ProgressString, "Loading %s...", File);
-    UiDrawProgressBarCenter(Percentage, 100, ProgressString);
+    RtlStringCbPrintfA(ProgressString, sizeof(ProgressString), "Loading %s...", File);
+    UiUpdateProgressBar(Percentage, ProgressString);
 
-    strcpy(FullFileName, Path);
-    strcat(FullFileName, File);
+    RtlStringCbCopyA(FullFileName, sizeof(FullFileName), Path);
+    RtlStringCbCatA(FullFileName, sizeof(FullFileName), File);
 
-    Success = WinLdrLoadImage(FullFileName, MemoryType, &BaseAddress);
+    NtLdrOutputLoadMsg(FullFileName, NULL);
+    Success = PeLdrLoadImage(FullFileName, MemoryType, &BaseAddress);
     if (!Success)
     {
-        TRACE("Loading %s failed\n", File);
-        return FALSE;
+        ERR("PeLdrLoadImage('%s') failed\n", File);
+        return NULL;
     }
     TRACE("%s loaded successfully at %p\n", File, BaseAddress);
 
-    /*
-     * Cheat about the base DLL name if we are loading
-     * the Kernel Debugger Transport DLL, to make the
-     * PE loader happy.
-     */
-    Success = WinLdrAllocateDataTableEntry(&LoaderBlock->LoadOrderListHead,
-                                           ImportName,
-                                           FullFileName,
-                                           BaseAddress,
-                                           Dte);
+    Success = PeLdrAllocateDataTableEntry(&LoaderBlock->LoadOrderListHead,
+                                          ImportName,
+                                          FullFileName,
+                                          BaseAddress,
+                                          Dte);
+    if (!Success)
+    {
+        /* Cleanup and bail out */
+        ERR("PeLdrAllocateDataTableEntry('%s') failed\n", FullFileName);
+        MmFreeMemory(BaseAddress);
+        return NULL;
+    }
 
-    return Success;
+    /* Init security cookie */
+    PeLdrInitSecurityCookie(*Dte);
+
+    return BaseAddress;
 }
 
+#ifdef _M_IX86
+static
+BOOLEAN
+WinLdrIsPaeSupported(
+    _In_ USHORT OperatingSystemVersion,
+    _In_ PLOADER_PARAMETER_BLOCK LoaderBlock,
+    _In_ PCSTR BootOptions,
+    _In_ PCSTR HalFileName,
+    _Inout_updates_bytes_(KernelFileNameSize) _Always_(_Post_z_)
+         PSTR KernelFileName,
+    _In_ SIZE_T KernelFileNameSize)
+{
+    BOOLEAN PaeEnabled = FALSE;
+    BOOLEAN PaeDisabled = FALSE;
+    BOOLEAN Result;
+
+    if ((OperatingSystemVersion > _WIN32_WINNT_NT4) &&
+        NtLdrGetOption(BootOptions, "PAE"))
+    {
+        /* We found the PAE option */
+        PaeEnabled = TRUE;
+    }
+
+    Result = PaeEnabled;
+
+    if ((OperatingSystemVersion > _WIN32_WINNT_WIN2K) &&
+        NtLdrGetOption(BootOptions, "NOPAE"))
+    {
+        PaeDisabled = TRUE;
+    }
+
+    if (SafeBoot)
+        PaeDisabled = TRUE;
+
+    TRACE("PaeEnabled %X, PaeDisabled %X\n", PaeEnabled, PaeDisabled);
+
+    if (PaeDisabled)
+        Result = FALSE;
+
+    /* Enable PAE if DEP is enabled */
+    if (NoExecuteEnabled)
+        Result = TRUE;
+
+    // TODO: checks for CPU support, hotplug memory support ... other tests
+    // TODO: select kernel name ("ntkrnlpa.exe" or "ntoskrnl.exe"), or,
+    // if KernelFileName is a user-specified kernel file, check whether it
+    // has, if PAE needs to be enabled, the IMAGE_FILE_LARGE_ADDRESS_AWARE
+    // Characteristics bit set, and that the HAL image has a similar support.
+
+    if (Result) UNIMPLEMENTED;
+
+    return Result;
+}
+#endif /* _M_IX86 */
+
 static
 BOOLEAN
 LoadWindowsCore(IN USHORT OperatingSystemVersion,
@@ -478,77 +613,162 @@ LoadWindowsCore(IN USHORT OperatingSystemVersion,
                 IN OUT PLDR_DATA_TABLE_ENTRY* KernelDTE)
 {
     BOOLEAN Success;
-    PCSTR Options;
+    PCSTR Option;
+    ULONG OptionLength;
+    PVOID KernelBase, HalBase, KdDllBase = NULL;
+    PLDR_DATA_TABLE_ENTRY HalDTE, KdDllDTE = NULL;
     CHAR DirPath[MAX_PATH];
-    CHAR KernelFileName[MAX_PATH];
     CHAR HalFileName[MAX_PATH];
-    CHAR KdTransportDllName[MAX_PATH];
-    PLDR_DATA_TABLE_ENTRY HalDTE, KdComDTE = NULL;
+    CHAR KernelFileName[MAX_PATH];
+    CHAR KdDllName[MAX_PATH];
 
     if (!KernelDTE) return FALSE;
 
     /* Initialize SystemRoot\System32 path */
-    strcpy(DirPath, BootPath);
-    strcat(DirPath, "system32\\");
+    RtlStringCbCopyA(DirPath, sizeof(DirPath), BootPath);
+    RtlStringCbCatA(DirPath, sizeof(DirPath), "system32\\");
 
-    //
-    // TODO: Parse also the separate INI values "Kernel=" and "Hal="
-    //
+    /* Parse the boot options */
+    TRACE("LoadWindowsCore: BootOptions '%s'\n", BootOptions);
+
+#ifdef _M_IX86
+    if (NtLdrGetOption(BootOptions, "3GB"))
+    {
+        /* We found the 3GB option. */
+        FIXME("LoadWindowsCore: 3GB - TRUE (not implemented)\n");
+        VirtualBias = TRUE;
+    }
+    // TODO: "USERVA=" for XP/2k3
+#endif
+
+    if ((OperatingSystemVersion > _WIN32_WINNT_NT4) &&
+        (NtLdrGetOption(BootOptions, "SAFEBOOT") ||
+         NtLdrGetOption(BootOptions, "SAFEBOOT:")))
+    {
+        /* We found the SAFEBOOT option. */
+        FIXME("LoadWindowsCore: SAFEBOOT - TRUE (not implemented)\n");
+        SafeBoot = TRUE;
+    }
 
-    /* Default KERNEL and HAL file names */
-    strcpy(KernelFileName, "ntoskrnl.exe");
-    strcpy(HalFileName   , "hal.dll");
+    if ((OperatingSystemVersion > _WIN32_WINNT_WIN2K) &&
+        NtLdrGetOption(BootOptions, "BOOTLOGO"))
+    {
+        /* We found the BOOTLOGO option. */
+        FIXME("LoadWindowsCore: BOOTLOGO - TRUE (not implemented)\n");
+        BootLogo = TRUE;
+    }
 
-    /* Find any /KERNEL= or /HAL= switch in the boot options */
-    Options = BootOptions;
-    while (Options)
+    /* Check the (NO)EXECUTE options */
+    if ((OperatingSystemVersion > _WIN32_WINNT_WIN2K) &&
+        !LoaderBlock->SetupLdrBlock)
     {
-        /* Skip possible initial whitespace */
-        Options += strspn(Options, " \t");
+        /* Disable NX by default on x86, otherwise enable it */
+#ifdef _M_IX86
+        NoExecuteEnabled = FALSE;
+#else
+        NoExecuteEnabled = TRUE;
+#endif
 
-        /* Check whether a new commutator starts and it is either KERNEL or HAL */
-        if (*Options != '/' || (++Options,
-            !(_strnicmp(Options, "KERNEL=", 7) == 0 ||
-              _strnicmp(Options, "HAL=",    4) == 0)) )
+#ifdef _M_IX86
+        /* Check the options in decreasing order of precedence */
+        if (NtLdrGetOption(BootOptions, "NOEXECUTE=OPTIN")  ||
+            NtLdrGetOption(BootOptions, "NOEXECUTE=OPTOUT") ||
+            NtLdrGetOption(BootOptions, "NOEXECUTE=ALWAYSON"))
         {
-            /* Search for another whitespace */
-            Options = strpbrk(Options, " \t");
-            continue;
+            NoExecuteEnabled = TRUE;
         }
+        else if (NtLdrGetOption(BootOptions, "NOEXECUTE=ALWAYSOFF"))
+            NoExecuteEnabled = FALSE;
         else
-        {
-            size_t i = strcspn(Options, " \t"); /* Skip whitespace */
-            if (i == 0)
-            {
-                /* Use the default values */
-                break;
-            }
+#else
+        /* Only the following two options really apply for x64 and other platforms */
+#endif
+        if (NtLdrGetOption(BootOptions, "NOEXECUTE"))
+            NoExecuteEnabled = TRUE;
+        else if (NtLdrGetOption(BootOptions, "EXECUTE"))
+            NoExecuteEnabled = FALSE;
 
-            /* We have found either KERNEL or HAL commutator */
-            if (_strnicmp(Options, "KERNEL=", 7) == 0)
-            {
-                Options += 7; i -= 7;
-                strncpy(KernelFileName, Options, i);
-                KernelFileName[i] = ANSI_NULL;
-                _strupr(KernelFileName);
-            }
-            else if (_strnicmp(Options, "HAL=", 4) == 0)
-            {
-                Options += 4; i -= 4;
-                strncpy(HalFileName, Options, i);
-                HalFileName[i] = ANSI_NULL;
-                _strupr(HalFileName);
-            }
-        }
+#ifdef _M_IX86
+        /* Disable DEP in SafeBoot mode for x86 only */
+        if (SafeBoot)
+            NoExecuteEnabled = FALSE;
+#endif
     }
+    TRACE("NoExecuteEnabled %X\n", NoExecuteEnabled);
 
-    TRACE("Kernel file = '%s' ; HAL file = '%s'\n", KernelFileName, HalFileName);
+    /*
+     * Select the HAL and KERNEL file names.
+     * Check for any "/HAL=" or "/KERNEL=" override option.
+     *
+     * See the following links to know how the file names are actually chosen:
+     * https://www.geoffchappell.com/notes/windows/boot/bcd/osloader/detecthal.htm
+     * https://www.geoffchappell.com/notes/windows/boot/bcd/osloader/hal.htm
+     * https://www.geoffchappell.com/notes/windows/boot/bcd/osloader/kernel.htm
+     */
+    /* Default HAL and KERNEL file names */
+    RtlStringCbCopyA(HalFileName   , sizeof(HalFileName)   , "hal.dll");
+    RtlStringCbCopyA(KernelFileName, sizeof(KernelFileName), "ntoskrnl.exe");
+
+    Option = NtLdrGetOptionEx(BootOptions, "HAL=", &OptionLength);
+    if (Option && (OptionLength > 4))
+    {
+        /* Retrieve the HAL file name */
+        Option += 4; OptionLength -= 4;
+        RtlStringCbCopyNA(HalFileName, sizeof(HalFileName), Option, OptionLength);
+        _strlwr(HalFileName);
+    }
+
+    Option = NtLdrGetOptionEx(BootOptions, "KERNEL=", &OptionLength);
+    if (Option && (OptionLength > 7))
+    {
+        /* Retrieve the KERNEL file name */
+        Option += 7; OptionLength -= 7;
+        RtlStringCbCopyNA(KernelFileName, sizeof(KernelFileName), Option, OptionLength);
+        _strlwr(KernelFileName);
+    }
+
+#ifdef _M_IX86
+    /* Check for PAE support and select the adequate kernel image */
+    PaeModeOn = WinLdrIsPaeSupported(OperatingSystemVersion,
+                                     LoaderBlock,
+                                     BootOptions,
+                                     HalFileName,
+                                     KernelFileName,
+                                     sizeof(KernelFileName));
+    if (PaeModeOn) FIXME("WinLdrIsPaeSupported: PaeModeOn\n");
+#endif
+
+    TRACE("HAL file = '%s' ; Kernel file = '%s'\n", HalFileName, KernelFileName);
+
+    /*
+     * Load the core NT files: Kernel, HAL and KD transport DLL.
+     * Cheat about their base DLL name so as to satisfy the imports/exports,
+     * even if the corresponding underlying files do not have the same names
+     * -- this happens e.g. with UP vs. MP kernel, standard vs. ACPI hal, or
+     * different KD transport DLLs.
+     */
 
     /* Load the Kernel */
-    LoadModule(LoaderBlock, DirPath, KernelFileName, "ntoskrnl.exe", LoaderSystemCode, KernelDTE, 30);
+    KernelBase = LoadModule(LoaderBlock, DirPath, KernelFileName,
+                            "ntoskrnl.exe", LoaderSystemCode, KernelDTE, 30);
+    if (!KernelBase)
+    {
+        ERR("LoadModule('%s') failed\n", KernelFileName);
+        UiMessageBox("Could not load %s", KernelFileName);
+        return FALSE;
+    }
 
     /* Load the HAL */
-    LoadModule(LoaderBlock, DirPath, HalFileName, "hal.dll", LoaderHalCode, &HalDTE, 45);
+    HalBase = LoadModule(LoaderBlock, DirPath, HalFileName,
+                         "hal.dll", LoaderHalCode, &HalDTE, 35);
+    if (!HalBase)
+    {
+        ERR("LoadModule('%s') failed\n", HalFileName);
+        UiMessageBox("Could not load %s", HalFileName);
+        PeLdrFreeDataTableEntry(*KernelDTE);
+        MmFreeMemory(KernelBase);
+        return FALSE;
+    }
 
     /* Load the Kernel Debugger Transport DLL */
     if (OperatingSystemVersion > _WIN32_WINNT_WIN2K)
@@ -565,120 +785,245 @@ LoadWindowsCore(IN USHORT OperatingSystemVersion,
          */
 
         /*
-         * This loop replaces a dumb call to strstr(..., "DEBUGPORT=").
-         * Indeed I want it to be case-insensitive to allow "debugport="
-         * or "DeBuGpOrT=" or... , and I don't want it to match malformed
-         * command-line options, such as:
-         *
-         * "...foo DEBUGPORT=xxx bar..."
-         * "...foo/DEBUGPORT=xxx bar..."
-         * "...foo/DEBUGPORT=bar..."
-         *
-         * i.e. the "DEBUGPORT=" switch must start with a slash and be separated
-         * from the rest by whitespace, unless it begins the command-line, e.g.:
-         *
-         * "/DEBUGPORT=COM1 foo...bar..."
-         * "...foo /DEBUGPORT=USB bar..."
-         * or:
-         * "...foo /DEBUGPORT= bar..."
-         * (in that case, we default the port to COM).
+         * A Kernel Debugger Transport DLL is always loaded for Windows XP+ :
+         * either the standard KDCOM.DLL (by default): IsCustomKdDll == FALSE
+         * or an alternative user-provided one via the /DEBUGPORT= option:
+         * IsCustomKdDll == TRUE if it does not specify the default KDCOM.
          */
-        Options = BootOptions;
-        while (Options)
+        BOOLEAN IsCustomKdDll = FALSE;
+
+        /* Check whether there is a DEBUGPORT option */
+        Option = NtLdrGetOptionEx(BootOptions, "DEBUGPORT=", &OptionLength);
+        if (Option && (OptionLength > 10))
         {
-            /* Skip possible initial whitespace */
-            Options += strspn(Options, " \t");
+            /* Move to the debug port name */
+            Option += 10; OptionLength -= 10;
+
+            /*
+             * Parse the port name.
+             * Format: /DEBUGPORT=COM[0-9]
+             * or: /DEBUGPORT=FILE:\Device\HarddiskX\PartitionY\debug.log
+             * or: /DEBUGPORT=FOO
+             * If we only have /DEBUGPORT= (i.e. without any port name),
+             * default to "COM".
+             */
 
-            /* Check whether a new commutator starts and it is the DEBUGPORT one */
-            if (*Options != '/' || _strnicmp(++Options, "DEBUGPORT=", 10) != 0)
+            /* Get the actual length of the debug port
+             * until the next whitespace or colon. */
+            OptionLength = (ULONG)strcspn(Option, " \t:");
+
+            if ((OptionLength == 0) ||
+                ( (OptionLength >= 3) && (_strnicmp(Option, "COM", 3) == 0) &&
+                 ((OptionLength == 3) || ('0' <= Option[3] && Option[3] <= '9')) ))
             {
-                /* Search for another whitespace */
-                Options = strpbrk(Options, " \t");
-                continue;
+                /* The standard KDCOM.DLL is used */
             }
             else
             {
-                /* We found the DEBUGPORT commutator. Move to the port name. */
-                Options += 10;
-                break;
+                /* A custom KD DLL is used */
+                IsCustomKdDll = TRUE;
             }
         }
+        if (!IsCustomKdDll)
+        {
+            Option = "COM"; OptionLength = 3;
+        }
+
+        RtlStringCbPrintfA(KdDllName, sizeof(KdDllName), "kd%.*s.dll",
+                           OptionLength, Option);
+        _strlwr(KdDllName);
 
-        if (Options)
+        /* Load the KD DLL. Override its base DLL name to the default "KDCOM.DLL". */
+        KdDllBase = LoadModule(LoaderBlock, DirPath, KdDllName,
+                               "kdcom.dll", LoaderSystemCode, &KdDllDTE, 40);
+        if (!KdDllBase)
         {
-            /*
-             * We have found the DEBUGPORT commutator. Parse the port name.
-             * Format: /DEBUGPORT=COM1 or /DEBUGPORT=FILE:\Device\HarddiskX\PartitionY\debug.log or /DEBUGPORT=FOO
-             * If we only have /DEBUGPORT= (i.e. without any port name), defaults it to "COM".
-             */
-            strcpy(KdTransportDllName, "KD");
-            if (_strnicmp(Options, "COM", 3) == 0 && '0' <= Options[3] && Options[3] <= '9')
+            /* If we failed to load a custom KD DLL, fall back to the standard one */
+            if (IsCustomKdDll)
             {
-                strncat(KdTransportDllName, Options, 3);
+                /* The custom KD DLL being optional, just ignore the failure */
+                WARN("LoadModule('%s') failed\n", KdDllName);
+
+                IsCustomKdDll = FALSE;
+                RtlStringCbCopyA(KdDllName, sizeof(KdDllName), "kdcom.dll");
+
+                KdDllBase = LoadModule(LoaderBlock, DirPath, KdDllName,
+                                       "kdcom.dll", LoaderSystemCode, &KdDllDTE, 40);
             }
-            else
+
+            if (!KdDllBase)
             {
-                size_t i = strcspn(Options, " \t:"); /* Skip valid separators: whitespace or colon */
-                if (i == 0)
-                    strcat(KdTransportDllName, "COM");
-                else
-                    strncat(KdTransportDllName, Options, i);
+                /* Ignore the failure; we will fail later when scanning the
+                 * kernel import tables, if it really needs the KD DLL. */
+                ERR("LoadModule('%s') failed\n", KdDllName);
             }
-            strcat(KdTransportDllName, ".DLL");
-            _strupr(KdTransportDllName);
-
-            /*
-             * Load the transport DLL. Override the base DLL name of the
-             * loaded transport DLL to the default "KDCOM.DLL" name.
-             */
-            LoadModule(LoaderBlock, DirPath, KdTransportDllName, "kdcom.dll", LoaderSystemCode, &KdComDTE, 60);
         }
     }
 
     /* Load all referenced DLLs for Kernel, HAL and Kernel Debugger Transport DLL */
-    Success  = WinLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, *KernelDTE);
-    Success &= WinLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, HalDTE);
-    if (KdComDTE)
+    Success = PeLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, *KernelDTE);
+    if (!Success)
+    {
+        UiMessageBox("Could not load %s", KernelFileName);
+        goto Quit;
+    }
+    Success = PeLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, HalDTE);
+    if (!Success)
+    {
+        UiMessageBox("Could not load %s", HalFileName);
+        goto Quit;
+    }
+    if (KdDllDTE)
+    {
+        Success = PeLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, KdDllDTE);
+        if (!Success)
+        {
+            UiMessageBox("Could not load %s", KdDllName);
+            goto Quit;
+        }
+    }
+
+Quit:
+    if (!Success)
     {
-        Success &= WinLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, KdComDTE);
+        /* Cleanup and bail out */
+        if (KdDllDTE)
+            PeLdrFreeDataTableEntry(KdDllDTE);
+        if (KdDllBase) // Optional
+            MmFreeMemory(KdDllBase);
+
+        PeLdrFreeDataTableEntry(HalDTE);
+        MmFreeMemory(HalBase);
+
+        PeLdrFreeDataTableEntry(*KernelDTE);
+        MmFreeMemory(KernelBase);
     }
 
     return Success;
 }
 
-VOID
-LoadAndBootWindows(IN OperatingSystemItem* OperatingSystem,
-                   IN USHORT OperatingSystemVersion)
+static
+BOOLEAN
+WinLdrInitErrataInf(
+    IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,
+    IN USHORT OperatingSystemVersion,
+    IN PCSTR SystemRoot)
+{
+    LONG rc;
+    HKEY hKey;
+    ULONG BufferSize;
+    ULONG FileSize;
+    PVOID PhysicalBase;
+    WCHAR szFileName[80];
+    CHAR ErrataFilePath[MAX_PATH];
+
+    /* Open either the 'BiosInfo' (Windows <= 2003) or the 'Errata' (Vista+) key */
+    if (OperatingSystemVersion >= _WIN32_WINNT_VISTA)
+    {
+        rc = RegOpenKey(CurrentControlSetKey, L"Control\\Errata", &hKey);
+    }
+    else // (OperatingSystemVersion <= _WIN32_WINNT_WS03)
+    {
+        rc = RegOpenKey(CurrentControlSetKey, L"Control\\BiosInfo", &hKey);
+    }
+    if (rc != ERROR_SUCCESS)
+    {
+        WARN("Could not open the BiosInfo/Errata registry key (Error %u)\n", (int)rc);
+        return FALSE;
+    }
+
+    /* Retrieve the INF file name value */
+    BufferSize = sizeof(szFileName);
+    rc = RegQueryValue(hKey, L"InfName", NULL, (PUCHAR)szFileName, &BufferSize);
+    if (rc != ERROR_SUCCESS)
+    {
+        WARN("Could not retrieve the InfName value (Error %u)\n", (int)rc);
+        RegCloseKey(hKey);
+        return FALSE;
+    }
+
+    // TODO: "SystemBiosDate"
+
+    RegCloseKey(hKey);
+
+    RtlStringCbPrintfA(ErrataFilePath, sizeof(ErrataFilePath), "%s%s%S",
+                       SystemRoot, "inf\\", szFileName);
+
+    /* Load the INF file */
+    PhysicalBase = WinLdrLoadModule(ErrataFilePath, &FileSize, LoaderRegistryData);
+    if (!PhysicalBase)
+    {
+        WARN("Could not load '%s'\n", ErrataFilePath);
+        return FALSE;
+    }
+
+    LoaderBlock->Extension->EmInfFileImage = PaToVa(PhysicalBase);
+    LoaderBlock->Extension->EmInfFileSize  = FileSize;
+
+    return TRUE;
+}
+
+ARC_STATUS
+LoadAndBootWindows(
+    IN ULONG Argc,
+    IN PCHAR Argv[],
+    IN PCHAR Envp[])
 {
-    ULONG_PTR SectionId;
-    PCSTR SectionName = OperatingSystem->SystemPartition;
-    CHAR  SettingsValue[80];
-    BOOLEAN HasSection;
-    CHAR  BootPath[MAX_PATH];
-    CHAR  FileName[MAX_PATH];
-    CHAR  BootOptions[256];
-    PCHAR File;
+    ARC_STATUS Status;
+    PCSTR ArgValue;
+    PCSTR SystemPartition;
+    PCSTR FileName;
+    ULONG FileNameLength;
     BOOLEAN Success;
+    USHORT OperatingSystemVersion;
     PLOADER_PARAMETER_BLOCK LoaderBlock;
+    CHAR BootPath[MAX_PATH];
+    CHAR FilePath[MAX_PATH];
+    CHAR BootOptions[256];
 
-    /* Get OS setting value */
-    SettingsValue[0] = ANSI_NULL;
-    IniOpenSection("Operating Systems", &SectionId);
-    IniReadSettingByName(SectionId, SectionName, SettingsValue, sizeof(SettingsValue));
-
-    /* Open the operating system section specified in the .ini file */
-    HasSection = IniOpenSection(SectionName, &SectionId);
+    /* Retrieve the (mandatory) boot type */
+    ArgValue = GetArgumentValue(Argc, Argv, "BootType");
+    if (!ArgValue || !*ArgValue)
+    {
+        ERR("No 'BootType' value, aborting!\n");
+        return EINVAL;
+    }
 
-    UiDrawBackdrop();
-    UiDrawProgressBarCenter(1, 100, "Loading NT...");
+    /* Convert it to an OS version */
+    if (_stricmp(ArgValue, "Windows") == 0 ||
+        _stricmp(ArgValue, "Windows2003") == 0)
+    {
+        OperatingSystemVersion = _WIN32_WINNT_WS03;
+    }
+    else if (_stricmp(ArgValue, "WindowsNT40") == 0)
+    {
+        OperatingSystemVersion = _WIN32_WINNT_NT4;
+    }
+    else
+    {
+        ERR("Unknown 'BootType' value '%s', aborting!\n", ArgValue);
+        return EINVAL;
+    }
 
-    /* Read the system path is set in the .ini file */
-    if (!HasSection ||
-        !IniReadSettingByName(SectionId, "SystemPath", BootPath, sizeof(BootPath)))
+    /* Retrieve the (mandatory) system partition */
+    SystemPartition = GetArgumentValue(Argc, Argv, "SystemPartition");
+    if (!SystemPartition || !*SystemPartition)
     {
-        strcpy(BootPath, SectionName);
+        ERR("No 'SystemPartition' specified, aborting!\n");
+        return EINVAL;
     }
 
+    /* Let the user know we started loading */
+    UiDrawBackdrop();
+    UiDrawStatusText("Loading...");
+    UiDrawProgressBarCenter("Loading NT...");
+
+    /* Retrieve the system path */
+    *BootPath = ANSI_NULL;
+    ArgValue = GetArgumentValue(Argc, Argv, "SystemPath");
+    if (ArgValue)
+        RtlStringCbCopyA(BootPath, sizeof(BootPath), ArgValue);
+
     /*
      * Check whether BootPath is a full path
      * and if not, create a full boot path.
@@ -688,119 +1033,157 @@ LoadAndBootWindows(IN OperatingSystemItem* OperatingSystem,
     if (strrchr(BootPath, ')') == NULL)
     {
         /* Temporarily save the boot path */
-        strcpy(FileName, BootPath);
+        RtlStringCbCopyA(FilePath, sizeof(FilePath), BootPath);
 
-        /* This is not a full path. Use the current (i.e. boot) device. */
-        MachDiskGetBootPath(BootPath, sizeof(BootPath));
+        /* This is not a full path: prepend the SystemPartition */
+        RtlStringCbCopyA(BootPath, sizeof(BootPath), SystemPartition);
 
         /* Append a path separator if needed */
-        if (FileName[0] != '\\' && FileName[0] != '/')
-            strcat(BootPath, "\\");
+        if (*FilePath != '\\' && *FilePath != '/')
+            RtlStringCbCatA(BootPath, sizeof(BootPath), "\\");
 
         /* Append the remaining path */
-        strcat(BootPath, FileName);
+        RtlStringCbCatA(BootPath, sizeof(BootPath), FilePath);
     }
 
-    /* Append a backslash if needed */
-    if ((BootPath[0] == 0) || BootPath[strlen(BootPath) - 1] != '\\')
-        strcat(BootPath, "\\");
+    /* Append a path separator if needed */
+    if (!*BootPath || BootPath[strlen(BootPath) - 1] != '\\')
+        RtlStringCbCatA(BootPath, sizeof(BootPath), "\\");
 
-    /* Read booting options */
-    if (!HasSection || !IniReadSettingByName(SectionId, "Options", BootOptions, sizeof(BootOptions)))
-    {
-        /* Get options after the title */
-        PCSTR p = SettingsValue;
-        while (*p == ' ' || *p == '"')
-            p++;
-        while (*p != '\0' && *p != '"')
-            p++;
-        strcpy(BootOptions, p);
-        TRACE("BootOptions: '%s'\n", BootOptions);
-    }
+    TRACE("BootPath: '%s'\n", BootPath);
+
+    /* Retrieve the boot options */
+    *BootOptions = ANSI_NULL;
+    ArgValue = GetArgumentValue(Argc, Argv, "Options");
+    if (ArgValue && *ArgValue)
+        RtlStringCbCopyA(BootOptions, sizeof(BootOptions), ArgValue);
 
     /* Append boot-time options */
     AppendBootTimeOptions(BootOptions);
 
-    /* Check if a ramdisk file was given */
-    File = strstr(BootOptions, "/RDPATH=");
-    if (File)
+    /*
+     * Set the "/HAL=" and "/KERNEL=" options if needed.
+     * If already present on the standard "Options=" option line, they take
+     * precedence over those passed via the separate "Hal=" and "Kernel="
+     * options.
+     */
+    if (!NtLdrGetOption(BootOptions, "HAL="))
+    {
+        /*
+         * Not found in the options, try to retrieve the
+         * separate value and append it to the options.
+         */
+        ArgValue = GetArgumentValue(Argc, Argv, "Hal");
+        if (ArgValue && *ArgValue)
+        {
+            RtlStringCbCatA(BootOptions, sizeof(BootOptions), " /HAL=");
+            RtlStringCbCatA(BootOptions, sizeof(BootOptions), ArgValue);
+        }
+    }
+    if (!NtLdrGetOption(BootOptions, "KERNEL="))
     {
-        /* Copy the file name and everything else after it */
-        strcpy(FileName, File + 8);
+        /*
+         * Not found in the options, try to retrieve the
+         * separate value and append it to the options.
+         */
+        ArgValue = GetArgumentValue(Argc, Argv, "Kernel");
+        if (ArgValue && *ArgValue)
+        {
+            RtlStringCbCatA(BootOptions, sizeof(BootOptions), " /KERNEL=");
+            RtlStringCbCatA(BootOptions, sizeof(BootOptions), ArgValue);
+        }
+    }
 
-        /* Null-terminate */
-        *strstr(FileName, " ") = ANSI_NULL;
+    TRACE("BootOptions: '%s'\n", BootOptions);
 
-        /* Load the ramdisk */
-        if (!RamDiskLoadVirtualFile(FileName))
+    /* Check if a RAM disk file was given */
+    FileName = NtLdrGetOptionEx(BootOptions, "RDPATH=", &FileNameLength);
+    if (FileName && (FileNameLength > 7))
+    {
+        /* Load the RAM disk */
+        Status = RamDiskInitialize(FALSE, BootOptions, SystemPartition);
+        if (Status != ESUCCESS)
         {
-            UiMessageBox("Failed to load RAM disk file %s", FileName);
-            return;
+            FileName += 7; FileNameLength -= 7;
+            UiMessageBox("Failed to load RAM disk file '%.*s'",
+                         FileNameLength, FileName);
+            return Status;
         }
     }
 
-    /* Let user know we started loading */
-    //UiDrawStatusText("Loading...");
+    /* Handle the SOS option */
+    SosEnabled = !!NtLdrGetOption(BootOptions, "SOS");
+    if (SosEnabled)
+        UiResetForSOS();
 
-    TRACE("BootPath: '%s'\n", BootPath);
-
-    /* Allocate and minimalist-initialize LPB */
-    AllocateAndInitLPB(&LoaderBlock);
+    /* Allocate and minimally-initialize the Loader Parameter Block */
+    AllocateAndInitLPB(OperatingSystemVersion, &LoaderBlock);
 
     /* Load the system hive */
-    UiDrawBackdrop();
-    UiDrawProgressBarCenter(15, 100, "Loading system hive...");
-    Success = WinLdrInitSystemHive(LoaderBlock, BootPath);
+    UiUpdateProgressBar(15, "Loading system hive...");
+    Success = WinLdrInitSystemHive(LoaderBlock, BootPath, FALSE);
     TRACE("SYSTEM hive %s\n", (Success ? "loaded" : "not loaded"));
     /* Bail out if failure */
     if (!Success)
-        return;
+        return ENOEXEC;
+
+    /* Fixup the version number using data from the registry */
+    if (OperatingSystemVersion == 0)
+        OperatingSystemVersion = WinLdrDetectVersion();
+    LoaderBlock->Extension->MajorVersion = (OperatingSystemVersion & 0xFF00) >> 8;
+    LoaderBlock->Extension->MinorVersion = (OperatingSystemVersion & 0xFF);
 
     /* Load NLS data, OEM font, and prepare boot drivers list */
     Success = WinLdrScanSystemHive(LoaderBlock, BootPath);
     TRACE("SYSTEM hive %s\n", (Success ? "scanned" : "not scanned"));
     /* Bail out if failure */
     if (!Success)
-        return;
+        return ENOEXEC;
+
+    /* Load the Firmware Errata file */
+    Success = WinLdrInitErrataInf(LoaderBlock, OperatingSystemVersion, BootPath);
+    TRACE("Firmware Errata file %s\n", (Success ? "loaded" : "not loaded"));
+    /* Not necessarily fatal if not found - carry on going */
 
     /* Finish loading */
-    LoadAndBootWindowsCommon(OperatingSystemVersion,
-                             LoaderBlock,
-                             BootOptions,
-                             BootPath,
-                             FALSE);
+    return LoadAndBootWindowsCommon(OperatingSystemVersion,
+                                    LoaderBlock,
+                                    BootOptions,
+                                    BootPath);
 }
 
-VOID
+ARC_STATUS
 LoadAndBootWindowsCommon(
-    USHORT OperatingSystemVersion,
-    PLOADER_PARAMETER_BLOCK LoaderBlock,
-    LPCSTR BootOptions,
-    LPCSTR BootPath,
-    BOOLEAN Setup)
+    IN USHORT OperatingSystemVersion,
+    IN PLOADER_PARAMETER_BLOCK LoaderBlock,
+    IN PCSTR BootOptions,
+    IN PCSTR BootPath)
 {
     PLOADER_PARAMETER_BLOCK LoaderBlockVA;
     BOOLEAN Success;
     PLDR_DATA_TABLE_ENTRY KernelDTE;
     KERNEL_ENTRY_POINT KiSystemStartup;
-    LPCSTR SystemRoot;
+    PCSTR SystemRoot;
+
     TRACE("LoadAndBootWindowsCommon()\n");
 
+    ASSERT(OperatingSystemVersion != 0);
+
 #ifdef _M_IX86
     /* Setup redirection support */
-    WinLdrSetupEms((PCHAR)BootOptions);
+    WinLdrSetupEms(BootOptions);
 #endif
 
     /* Convert BootPath to SystemRoot */
     SystemRoot = strstr(BootPath, "\\");
 
     /* Detect hardware */
-    UiDrawBackdrop();
-    UiDrawProgressBarCenter(20, 100, "Detecting hardware...");
-    LoaderBlock->ConfigurationRoot = MachHwDetect();
+    UiUpdateProgressBar(20, "Detecting hardware...");
+    LoaderBlock->ConfigurationRoot = MachHwDetect(BootOptions);
 
-    if (OperatingSystemVersion == 0)
-        OperatingSystemVersion = WinLdrDetectVersion();
+    /* Initialize the PE loader import-DLL callback, so that we can obtain
+     * feedback (for example during SOS) on the PE images that get loaded. */
+    PeLdrImportDllLoadCallback = NtLdrImportDllLoadCallback;
 
     /* Load the operating system core: the Kernel, the HAL and the Kernel Debugger Transport DLL */
     Success = LoadWindowsCore(OperatingSystemVersion,
@@ -810,16 +1193,32 @@ LoadAndBootWindowsCommon(
                               &KernelDTE);
     if (!Success)
     {
+        /* Reset the PE loader import-DLL callback */
+        PeLdrImportDllLoadCallback = NULL;
+
         UiMessageBox("Error loading NTOS core.");
-        return;
+        return ENOEXEC;
     }
 
+    /* Cleanup INI file */
+    IniCleanup();
+
+/****
+ **** WE HAVE NOW REACHED THE POINT OF NO RETURN !!
+ ****/
+
+    UiSetProgressBarSubset(40, 90); // NTOS goes from 25 to 75%
+
     /* Load boot drivers */
-    UiDrawBackdrop();
-    UiDrawProgressBarCenter(100, 100, "Loading boot drivers...");
+    UiSetProgressBarText("Loading boot drivers...");
     Success = WinLdrLoadBootDrivers(LoaderBlock, BootPath);
     TRACE("Boot drivers loading %s\n", Success ? "successful" : "failed");
 
+    UiSetProgressBarSubset(0, 100);
+
+    /* Reset the PE loader import-DLL callback */
+    PeLdrImportDllLoadCallback = NULL;
+
     /* Initialize Phase 1 - no drivers loading anymore */
     WinLdrInitializePhase1(LoaderBlock,
                            BootOptions,
@@ -827,15 +1226,14 @@ LoadAndBootWindowsCommon(
                            BootPath,
                            OperatingSystemVersion);
 
+    UiUpdateProgressBar(100, NULL);
+
     /* Save entry-point pointer and Loader block VAs */
     KiSystemStartup = (KERNEL_ENTRY_POINT)KernelDTE->EntryPoint;
     LoaderBlockVA = PaToVa(LoaderBlock);
 
     /* "Stop all motors", change videomode */
-    MachPrepareForReactOS(Setup);
-
-    /* Cleanup ini file */
-    IniCleanup();
+    MachPrepareForReactOS();
 
     /* Debugging... */
     //DumpMemoryAllocMap();
@@ -855,8 +1253,8 @@ LoadAndBootWindowsCommon(
     TRACE("Hello from paged mode, KiSystemStartup %p, LoaderBlockVA %p!\n",
           KiSystemStartup, LoaderBlockVA);
 
-    // Zero KI_USER_SHARED_DATA page
-    memset((PVOID)KI_USER_SHARED_DATA, 0, MM_PAGE_SIZE);
+    /* Zero KI_USER_SHARED_DATA page */
+    RtlZeroMemory((PVOID)KI_USER_SHARED_DATA, MM_PAGE_SIZE);
 
     WinLdrpDumpMemoryDescriptors(LoaderBlockVA);
     WinLdrpDumpBootDriver(LoaderBlockVA);
@@ -866,6 +1264,8 @@ LoadAndBootWindowsCommon(
 
     /* Pass control */
     (*KiSystemStartup)(LoaderBlockVA);
+
+    UNREACHABLE; // return ESUCCESS;
 }
 
 VOID