[FREELDR] Load the Firmware Errata file specified in the registry. (#1951)
[reactos.git] / boot / freeldr / freeldr / ntldr / winldr.c
index e68a602..e8a7d38 100644 (file)
@@ -1,30 +1,15 @@
 /*
- *  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 "registry.h"
 
-#include <ndk/ldrtypes.h>
-
 #include <debug.h>
 DBG_DEFAULT_CHANNEL(WINDOWS);
 
@@ -80,11 +65,13 @@ WinLdrInitializePhase1(PLOADER_PARAMETER_BLOCK LoaderBlock,
                        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)";
+     */
 
     PSTR  LoadOptions, NewLoadOptions;
     CHAR  HalPath[] = "\\";
@@ -271,7 +258,7 @@ WinLdrLoadDeviceDriver(PLIST_ENTRY LoadOrderListHead,
     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
@@ -280,15 +267,15 @@ WinLdrLoadDeviceDriver(PLIST_ENTRY LoadOrderListHead,
 
     // It's not loaded, we have to load it
     RtlStringCbPrintfA(FullPath, sizeof(FullPath), "%s%wZ", BootPath, FilePath);
-    Success = WinLdrLoadImage(FullPath, LoaderBootDriver, &DriverBase);
+    Success = PeLdrLoadImage(FullPath, LoaderBootDriver, &DriverBase);
     if (!Success)
         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");
+        ERR("PeLdrAllocateDataTableEntry() failed\n");
         return FALSE;
     }
 
@@ -297,10 +284,10 @@ WinLdrLoadDeviceDriver(PLIST_ENTRY LoadOrderListHead,
 
     // Look for any dependencies it may have, and load them too
     RtlStringCbPrintfA(FullPath, sizeof(FullPath), "%s%s", BootPath, DriverPath);
-    Success = WinLdrScanImportDescriptorTable(LoadOrderListHead, FullPath, *DriverDTE);
+    Success = PeLdrScanImportDescriptorTable(LoadOrderListHead, FullPath, *DriverDTE);
     if (!Success)
     {
-        ERR("WinLdrScanImportDescriptorTable() failed for %s\n", FullPath);
+        ERR("PeLdrScanImportDescriptorTable() failed for %s\n", FullPath);
         return FALSE;
     }
 
@@ -382,7 +369,7 @@ WinLdrLoadModule(PCSTR ModuleName,
     *Size = 0;
 
     /* Open the image file */
-    Status = ArcOpen((PCHAR)ModuleName, OpenReadOnly, &FileId);
+    Status = ArcOpen((PSTR)ModuleName, OpenReadOnly, &FileId);
     if (Status != ESUCCESS)
     {
         /* In case of errors, we just return, without complaining to the user */
@@ -390,7 +377,7 @@ WinLdrLoadModule(PCSTR ModuleName,
         return NULL;
     }
 
-    /* Get this file's size */
+    /* Retrieve its size */
     Status = ArcGetFileInformation(FileId, &FileInfo);
     if (Status != ESUCCESS)
     {
@@ -404,11 +391,12 @@ 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)
@@ -428,17 +416,16 @@ WinLdrDetectVersion(VOID)
     LONG rc;
     HKEY hKey;
 
-    rc = RegOpenKey(
-        NULL,
-        L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server",
-        &hKey);
+    rc = RegOpenKey(NULL,
+                    L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\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;
     }
 
-    // We may here want to read the value of ProductVersion
+    /* We may here want to read the value of ProductVersion */
     return _WIN32_WINNT_WS03;
 }
 
@@ -465,7 +452,7 @@ LoadModule(
     RtlStringCbCopyA(FullFileName, sizeof(FullFileName), Path);
     RtlStringCbCatA(FullFileName, sizeof(FullFileName), File);
 
-    Success = WinLdrLoadImage(FullFileName, MemoryType, &BaseAddress);
+    Success = PeLdrLoadImage(FullFileName, MemoryType, &BaseAddress);
     if (!Success)
     {
         TRACE("Loading %s failed\n", File);
@@ -478,11 +465,11 @@ LoadModule(
      * 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);
 
     return Success;
 }
@@ -498,8 +485,8 @@ LoadWindowsCore(IN USHORT OperatingSystemVersion,
     BOOLEAN Success;
     PCSTR Options;
     CHAR DirPath[MAX_PATH];
-    CHAR KernelFileName[MAX_PATH];
     CHAR HalFileName[MAX_PATH];
+    CHAR KernelFileName[MAX_PATH];
     CHAR KdTransportDllName[MAX_PATH];
     PLDR_DATA_TABLE_ENTRY HalDTE, KdComDTE = NULL;
 
@@ -509,25 +496,27 @@ LoadWindowsCore(IN USHORT OperatingSystemVersion,
     RtlStringCbCopyA(DirPath, sizeof(DirPath), BootPath);
     RtlStringCbCatA(DirPath, sizeof(DirPath), "system32\\");
 
-    //
-    // TODO: Parse also the separate INI values "Kernel=" and "Hal="
-    //
-
-    /* Default KERNEL and HAL file names */
-    RtlStringCbCopyA(KernelFileName, sizeof(KernelFileName), "ntoskrnl.exe");
+    /*
+     * Default HAL and KERNEL file names.
+     * 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
+     */
     RtlStringCbCopyA(HalFileName   , sizeof(HalFileName)   , "hal.dll");
+    RtlStringCbCopyA(KernelFileName, sizeof(KernelFileName), "ntoskrnl.exe");
 
-    /* Find any /KERNEL= or /HAL= switch in the boot options */
+    /* Find any "/HAL=" or "/KERNEL=" switch in the boot options */
     Options = BootOptions;
     while (Options)
     {
         /* Skip possible initial whitespace */
         Options += strspn(Options, " \t");
 
-        /* Check whether a new option starts and it is either KERNEL or HAL */
+        /* Check whether a new option starts and it is either HAL or KERNEL */
         if (*Options != '/' || (++Options,
-            !(_strnicmp(Options, "KERNEL=", 7) == 0 ||
-              _strnicmp(Options, "HAL=",    4) == 0)) )
+            !(_strnicmp(Options, "HAL=",    4) == 0 ||
+              _strnicmp(Options, "KERNEL=", 7) == 0)) )
         {
             /* Search for another whitespace */
             Options = strpbrk(Options, " \t");
@@ -542,23 +531,23 @@ LoadWindowsCore(IN USHORT OperatingSystemVersion,
                 break;
             }
 
-            /* We have found either KERNEL or HAL options */
-            if (_strnicmp(Options, "KERNEL=", 7) == 0)
-            {
-                Options += 7; i -= 7;
-                RtlStringCbCopyNA(KernelFileName, sizeof(KernelFileName), Options, i);
-                _strupr(KernelFileName);
-            }
-            else if (_strnicmp(Options, "HAL=", 4) == 0)
+            /* We have found either HAL or KERNEL options */
+            if (_strnicmp(Options, "HAL=", 4) == 0)
             {
                 Options += 4; i -= 4;
                 RtlStringCbCopyNA(HalFileName, sizeof(HalFileName), Options, i);
                 _strupr(HalFileName);
             }
+            else if (_strnicmp(Options, "KERNEL=", 7) == 0)
+            {
+                Options += 7; i -= 7;
+                RtlStringCbCopyNA(KernelFileName, sizeof(KernelFileName), Options, i);
+                _strupr(KernelFileName);
+            }
         }
     }
 
-    TRACE("Kernel file = '%s' ; HAL file = '%s'\n", KernelFileName, HalFileName);
+    TRACE("HAL file = '%s' ; Kernel file = '%s'\n", HalFileName, KernelFileName);
 
     /* Load the Kernel */
     LoadModule(LoaderBlock, DirPath, KernelFileName, "ntoskrnl.exe", LoaderSystemCode, KernelDTE, 30);
@@ -652,23 +641,86 @@ LoadWindowsCore(IN USHORT OperatingSystemVersion,
     }
 
     /* Load all referenced DLLs for Kernel, HAL and Kernel Debugger Transport DLL */
-    Success  = WinLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, *KernelDTE);
-    Success &= WinLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, HalDTE);
+    Success  = PeLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, *KernelDTE);
+    Success &= PeLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, HalDTE);
     if (KdComDTE)
     {
-        Success &= WinLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, KdComDTE);
+        Success &= PeLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, KdComDTE);
     }
 
     return Success;
 }
 
+static
+BOOLEAN
+WinLdrInitErrataInf(
+    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(NULL,
+                        L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Errata",
+                        &hKey);
+    }
+    else // (OperatingSystemVersion <= _WIN32_WINNT_WS03)
+    {
+        rc = RegOpenKey(NULL,
+                        L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\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);
+        return FALSE;
+    }
+
+    // TODO: "SystemBiosDate"
+
+    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;
+    }
+
+    WinLdrSystemBlock->Extension.EmInfFileImage = PaToVa(PhysicalBase);
+    WinLdrSystemBlock->Extension.EmInfFileSize  = FileSize;
+
+    return TRUE;
+}
+
 ARC_STATUS
 LoadAndBootWindows(
     IN ULONG Argc,
     IN PCHAR Argv[],
     IN PCHAR Envp[])
 {
+    ARC_STATUS Status;
     PCSTR ArgValue;
+    PCSTR SystemPartition;
     PCHAR File;
     BOOLEAN Success;
     USHORT OperatingSystemVersion;
@@ -677,13 +729,15 @@ LoadAndBootWindows(
     CHAR  FileName[MAX_PATH];
     CHAR  BootOptions[256];
 
+    /* Retrieve the (mandatory) boot type */
     ArgValue = GetArgumentValue(Argc, Argv, "BootType");
-    if (!ArgValue)
+    if (!ArgValue || !*ArgValue)
     {
         ERR("No 'BootType' value, aborting!\n");
         return EINVAL;
     }
 
+    /* Convert it to an OS version */
     if (_stricmp(ArgValue, "Windows") == 0 ||
         _stricmp(ArgValue, "Windows2003") == 0)
     {
@@ -699,6 +753,14 @@ LoadAndBootWindows(
         return EINVAL;
     }
 
+    /* Retrieve the (mandatory) system partition */
+    SystemPartition = GetArgumentValue(Argc, Argv, "SystemPartition");
+    if (!SystemPartition || !*SystemPartition)
+    {
+        ERR("No 'SystemPartition' specified, aborting!\n");
+        return EINVAL;
+    }
+
     UiDrawBackdrop();
     UiDrawProgressBarCenter(1, 100, "Loading NT...");
 
@@ -719,8 +781,8 @@ LoadAndBootWindows(
         /* Temporarily save the boot path */
         RtlStringCbCopyA(FileName, sizeof(FileName), 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 != '\\' && *FileName != '/')
@@ -730,7 +792,7 @@ LoadAndBootWindows(
         RtlStringCbCatA(BootPath, sizeof(BootPath), FileName);
     }
 
-    /* Append a backslash if needed */
+    /* Append a path separator if needed */
     if (!*BootPath || BootPath[strlen(BootPath) - 1] != '\\')
         RtlStringCbCatA(BootPath, sizeof(BootPath), "\\");
 
@@ -739,12 +801,45 @@ LoadAndBootWindows(
     /* Retrieve the boot options */
     *BootOptions = ANSI_NULL;
     ArgValue = GetArgumentValue(Argc, Argv, "Options");
-    if (ArgValue)
+    if (ArgValue && *ArgValue)
         RtlStringCbCopyA(BootOptions, sizeof(BootOptions), ArgValue);
 
     /* Append boot-time options */
     AppendBootTimeOptions(BootOptions);
 
+    /*
+     * Set "/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 (strstr(BootOptions, "/HAL=") != 0)
+    {
+        /*
+         * 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 (strstr(BootOptions, "/KERNEL=") != 0)
+    {
+        /*
+         * 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);
+        }
+    }
+
     TRACE("BootOptions: '%s'\n", BootOptions);
 
     /* Check if a ramdisk file was given */
@@ -758,10 +853,11 @@ LoadAndBootWindows(
         *strstr(FileName, " ") = ANSI_NULL;
 
         /* Load the ramdisk */
-        if (!RamDiskLoadVirtualFile(FileName))
+        Status = RamDiskLoadVirtualFile(FileName, SystemPartition);
+        if (Status != ESUCCESS)
         {
             UiMessageBox("Failed to load RAM disk file %s", FileName);
-            return ENOENT;
+            return Status;
         }
     }
 
@@ -787,6 +883,11 @@ LoadAndBootWindows(
     if (!Success)
         return ENOEXEC;
 
+    /* Load the Firmware Errata file */
+    Success = WinLdrInitErrataInf(OperatingSystemVersion, BootPath);
+    TRACE("Firmware Errata file %s\n", (Success ? "loaded" : "not loaded"));
+    /* Not necessarily fatal if not found - carry on going */
+
     /* Finish loading */
     return LoadAndBootWindowsCommon(OperatingSystemVersion,
                                     LoaderBlock,
@@ -880,8 +981,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);