]> code.delx.au - refind/blobdiff - refind/main.c
Protection against loading invalid EFI drivers; bug fix for using
[refind] / refind / main.c
index ca7bb1319e2cfa1ac3b467327dcaedea5213c345..f7e7616f41caee7e5991aad7951004b657338236 100644 (file)
@@ -147,7 +147,7 @@ static VOID AboutrEFInd(VOID)
 
     if (AboutMenu.EntryCount == 0) {
         AboutMenu.TitleImage = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
-        AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.7.0");
+        AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.7.1.2");
         AddMenuInfoLine(&AboutMenu, L"");
         AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2006-2010 Christoph Pfisterer");
         AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2012-2013 Roderick W. Smith");
@@ -210,6 +210,34 @@ static VOID WarnSecureBootError(CHAR16 *Name, BOOLEAN Verbose) {
    } // if
 } // VOID WarnSecureBootError()
 
+// Returns TRUE if this file is a valid EFI loader file, and is proper ARCH
+static BOOLEAN IsValidLoader(EFI_FILE *RootDir, CHAR16 *FileName) {
+    BOOLEAN         IsValid = TRUE;
+#if defined (EFIX64) | defined (EFI32)
+    EFI_STATUS      Status;
+    EFI_FILE_HANDLE FileHandle;
+    CHAR8           Header[512];
+    UINTN           Size = sizeof(Header);
+
+    Status = refit_call5_wrapper(RootDir->Open, RootDir, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
+    if (EFI_ERROR(Status))
+       return 0;
+
+    Status = refit_call3_wrapper(FileHandle->Read, FileHandle, &Size, Header);
+    refit_call1_wrapper(FileHandle->Close, FileHandle);
+
+    IsValid = !EFI_ERROR(Status) &&
+              Size == sizeof(Header) &&
+              ((Header[0] == 'M' && Header[1] == 'Z' &&
+               (Size = *(UINT32 *)&Header[0x3c]) < 0x180 &&
+               Header[Size] == 'P' && Header[Size+1] == 'E' &&
+               Header[Size+2] == 0 && Header[Size+3] == 0 &&
+               *(UINT16 *)&Header[Size+4] == EFI_STUB_ARCH) ||
+              (*(UINT32 *)&Header == FAT_ARCH));
+#endif
+    return IsValid;
+} // BOOLEAN IsValidLoader()
+
 // Launch an EFI binary.
 static EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths,
                                     IN CHAR16 *LoadOptions, IN CHAR16 *LoadOptionsPrefix,
@@ -220,9 +248,11 @@ static EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths,
     EFI_STATUS              Status, ReturnStatus;
     EFI_HANDLE              ChildImageHandle;
     EFI_LOADED_IMAGE        *ChildLoadedImage = NULL;
+    REFIT_VOLUME            *Volume = NULL;
     UINTN                   DevicePathIndex;
     CHAR16                  ErrorInfo[256];
     CHAR16                  *FullLoadOptions = NULL;
+    CHAR16                  *Filename = NULL;
 
     if (ErrorInStep != NULL)
         *ErrorInStep = 0;
@@ -251,15 +281,23 @@ static EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths,
     // load the image into memory (and execute it, in the case of a shim/MOK image).
     ReturnStatus = Status = EFI_NOT_FOUND;  // in case the list is empty
     for (DevicePathIndex = 0; DevicePaths[DevicePathIndex] != NULL; DevicePathIndex++) {
-       // NOTE: Below commented-out line could be more efficient if file were read ahead of
-       // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
-       // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
-       // kernel returns a "Failed to handle fs_proto" error message.
-       // TODO: Track down the cause of this error and fix it, if possible.
-       // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
-       //                                            ImageData, ImageSize, &ChildImageHandle);
-       ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
-                                                   NULL, 0, &ChildImageHandle);
+       FindVolumeAndFilename(DevicePaths[DevicePathIndex], &Volume, &Filename);
+       // Some EFIs crash if attempting to load driver for invalid architecture, so
+       // protect for this condition....
+       if (IsValidLoader(Volume->RootDir, Filename)) {
+          // NOTE: Below commented-out line could be more efficient if file were read ahead of
+          // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
+          // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
+          // kernel returns a "Failed to handle fs_proto" error message.
+          // TODO: Track down the cause of this error and fix it, if possible.
+          // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
+          //                                            ImageData, ImageSize, &ChildImageHandle);
+          ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
+                                                      NULL, 0, &ChildImageHandle);
+       } else {
+          Print(L"Invalid loader file!\n");
+          ReturnStatus = EFI_LOAD_ERROR;
+       }
        if (ReturnStatus != EFI_NOT_FOUND) {
           break;
        }
@@ -377,7 +415,6 @@ static EFI_STATUS RebootIntoFirmware(VOID) {
    refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
    Print(L"Error calling ResetSystem: %r", err);
    PauseForKey();
-//   uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
    return err;
 }
 
@@ -926,7 +963,8 @@ VOID SetLoaderDefaults(LOADER_ENTRY *Entry, CHAR16 *LoaderPath, REFIT_VOLUME *Vo
       Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_GRUB;
    } else if (StriCmp(FileName, L"cdboot.efi") == 0 ||
               StriCmp(FileName, L"bootmgr.efi") == 0 ||
-              StriCmp(FileName, L"bootmgfw.efi") == 0) {
+              StriCmp(FileName, L"bootmgfw.efi") == 0 ||
+              StriCmp(FileName, L"bkpbootmgfw.efi") == 0) {
       MergeStrings(&OSIconName, L"win", L',');
       Entry->OSType = 'W';
       ShortcutLetter = 'W';
@@ -1038,7 +1076,7 @@ static VOID CleanUpLoaderList(struct LOADER_LIST *LoaderList) {
 // Returns TRUE if none of these conditions is met -- that is, if the path is
 // eligible for scanning.
 static BOOLEAN ShouldScan(REFIT_VOLUME *Volume, CHAR16 *Path) {
-   CHAR16   *VolName = NULL, *DontScanDir;
+   CHAR16   *VolName = NULL, *DontScanDir, *PathCopy = NULL;
    UINTN    i = 0, VolNum;
    BOOLEAN  ScanIt = TRUE;
 
@@ -1048,6 +1086,25 @@ static BOOLEAN ShouldScan(REFIT_VOLUME *Volume, CHAR16 *Path) {
    if ((StriCmp(Path, SelfDirPath) == 0) && (Volume->DeviceHandle == SelfVolume->DeviceHandle))
       return FALSE;
 
+   // See if Path includes an explicit volume declaration that's NOT Volume....
+   PathCopy = StrDuplicate(Path);
+   if (SplitVolumeAndFilename(&PathCopy, &VolName)) {
+      if (StriCmp(VolName, Volume->VolName) != 0) {
+         if ((StrLen(VolName) > 2) && (VolName[0] == L'f') && (VolName[1] == L's') && (VolName[2] >= L'0') && (VolName[2] <= L'9')) {
+            VolNum = Atoi(VolName + 2);
+            if (VolNum != Volume->VolNumber) {
+               ScanIt = FALSE;
+            }
+         } else {
+            ScanIt = FALSE;
+         }
+      } // if
+   } // if Path includes volume specification
+   MyFreePool(PathCopy);
+   MyFreePool(VolName);
+   VolName = NULL;
+
+   // See if Volume is in GlobalConfig.DontScanDirs....
    while ((DontScanDir = FindCommaDelimited(GlobalConfig.DontScanDirs, i++)) && ScanIt) {
       SplitVolumeAndFilename(&DontScanDir, &VolName);
       CleanUpPathNameSlashes(DontScanDir);
@@ -1064,8 +1121,10 @@ static BOOLEAN ShouldScan(REFIT_VOLUME *Volume, CHAR16 *Path) {
             ScanIt = FALSE;
       }
       MyFreePool(DontScanDir);
+      MyFreePool(VolName);
       DontScanDir = NULL;
-   }
+   } // while()
+
    return ScanIt;
 } // BOOLEAN ShouldScan()
 
@@ -1164,34 +1223,6 @@ static BOOLEAN IsSymbolicLink(REFIT_VOLUME *Volume, CHAR16 *Path, EFI_FILE_INFO
    return (DirEntry->FileSize != FileSize2);
 } // BOOLEAN IsSymbolicLink()
 
-// Returns TRUE if this file is a valid EFI loader file, and is proper ARCH
-static BOOLEAN IsValidLoader(REFIT_VOLUME *Volume, CHAR16 *FileName) {
-    BOOLEAN         IsValid = TRUE;
-#if defined (EFIX64) | defined (EFI32)
-    EFI_STATUS      Status;
-    EFI_FILE_HANDLE FileHandle;
-    CHAR8           Header[512];
-    UINTN           Size = sizeof(Header);
-
-    Status = refit_call5_wrapper(Volume->RootDir->Open, Volume->RootDir, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
-    if (EFI_ERROR(Status))
-       return 0;
-
-    Status = refit_call3_wrapper(FileHandle->Read, FileHandle, &Size, Header);
-    refit_call1_wrapper(FileHandle->Close, FileHandle);
-
-    IsValid = !EFI_ERROR(Status) &&
-              Size == sizeof(Header) &&
-              ((Header[0] == 'M' && Header[1] == 'Z' &&
-               (Size = *(UINT32 *)&Header[0x3c]) < 0x180 &&
-               Header[Size] == 'P' && Header[Size+1] == 'E' &&
-               Header[Size+2] == 0 && Header[Size+3] == 0 &&
-               *(UINT16 *)&Header[Size+4] == EFI_STUB_ARCH) ||
-              (*(UINT32 *)&Header == FAT_ARCH));
-#endif
-    return IsValid;
-} // BOOLEAN IsValidLoader()
-
 // Scan an individual directory for EFI boot loader files and, if found,
 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
@@ -1228,9 +1259,7 @@ static BOOLEAN ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16
              SPrint(FileName, 255, L"\\%s", DirEntry->FileName);
           CleanUpPathNameSlashes(FileName);
 
-          if( /* (!StriSubCmp(L"vmlinuz", DirEntry->FileName) ||
-              !StriSubCmp(L"bzImage", DirEntry->FileName)) && */
-              !IsValidLoader(Volume, FileName))
+          if(!IsValidLoader(Volume->RootDir, FileName))
              continue;
 
           NewLoader = AllocateZeroPool(sizeof(struct LOADER_LIST));
@@ -1275,6 +1304,7 @@ static VOID ScanEfiFiles(REFIT_VOLUME *Volume) {
    CHAR16                  FileName[256], *Directory, *MatchPatterns, *VolName = NULL, *SelfPath;
    UINTN                   i, Length;
    BOOLEAN                 ScanFallbackLoader = TRUE;
+   BOOLEAN                 FoundBRBackup = FALSE;
 
    MatchPatterns = StrDuplicate(LOADER_MATCH_PATTERNS);
    if (GlobalConfig.ScanAllLinux)
@@ -1300,13 +1330,24 @@ static VOID ScanEfiFiles(REFIT_VOLUME *Volume) {
       } // if should scan Mac directory
 
       // check for Microsoft boot loader/menu
-      StrCpy(FileName, L"EFI\\Microsoft\\Boot\\bootmgfw.efi");
-      if (FileExists(Volume->RootDir, FileName) && ShouldScan(Volume, L"EFI\\Microsoft\\Boot") &&
-          !IsIn(L"bootmgfw.efi", GlobalConfig.DontScanFiles)) {
-         AddLoaderEntry(FileName, L"Microsoft EFI boot", Volume);
-         if (DuplicatesFallback(Volume, FileName))
-            ScanFallbackLoader = FALSE;
-      }
+      if (ShouldScan(Volume, L"EFI\\Microsoft\\Boot")) {
+         StrCpy(FileName, L"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi");
+         if (FileExists(Volume->RootDir, FileName) &&  !IsIn(L"bkpbootmgfw.efi", GlobalConfig.DontScanFiles)) {
+            AddLoaderEntry(FileName, L"Microsoft EFI boot (Boot Repair backup)", Volume);
+            FoundBRBackup = TRUE;
+            if (DuplicatesFallback(Volume, FileName))
+               ScanFallbackLoader = FALSE;
+         }
+         StrCpy(FileName, L"EFI\\Microsoft\\Boot\\bootmgfw.efi");
+         if (FileExists(Volume->RootDir, FileName) &&  !IsIn(L"bootmgfw.efi", GlobalConfig.DontScanFiles)) {
+            if (FoundBRBackup)
+               AddLoaderEntry(FileName, L"Supposed Microsoft EFI boot (probably GRUB)", Volume);
+            else
+               AddLoaderEntry(FileName, L"Microsoft EFI boot", Volume);
+            if (DuplicatesFallback(Volume, FileName))
+               ScanFallbackLoader = FALSE;
+         }
+      } // if
 
       // scan the root directory for EFI executables
       if (ScanLoaderDir(Volume, L"\\", MatchPatterns))
@@ -1328,13 +1369,15 @@ static VOID ScanEfiFiles(REFIT_VOLUME *Volume) {
       // Scan user-specified (or additional default) directories....
       i = 0;
       while ((Directory = FindCommaDelimited(GlobalConfig.AlsoScan, i++)) != NULL) {
-         SplitVolumeAndFilename(&Directory, &VolName);
-         CleanUpPathNameSlashes(Directory);
-         Length = StrLen(Directory);
-         if ((Length > 0) && ScanLoaderDir(Volume, Directory, MatchPatterns))
-            ScanFallbackLoader = FALSE;
+         if (ShouldScan(Volume, Directory)) {
+            SplitVolumeAndFilename(&Directory, &VolName);
+            CleanUpPathNameSlashes(Directory);
+            Length = StrLen(Directory);
+            if ((Length > 0) && ScanLoaderDir(Volume, Directory, MatchPatterns))
+               ScanFallbackLoader = FALSE;
+            MyFreePool(VolName);
+         } // if should scan dir
          MyFreePool(Directory);
-         MyFreePool(VolName);
       } // while
 
       // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
@@ -2101,7 +2144,7 @@ static VOID WarnIfLegacyProblems() {
 
 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
 static VOID ScanForBootloaders(VOID) {
-   UINTN                     i;
+   UINTN  i;
 
    ScanVolumes();
 
@@ -2342,8 +2385,8 @@ efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
     FindLegacyBootType();
     if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC)
        CopyMem(GlobalConfig.ScanFor, "ihebocm   ", NUM_SCAN_OPTIONS);
-    ScanVolumes();
     ReadConfig(CONFIG_FILE_NAME);
+    ScanVolumes();
 
     InitScreen();
     WarnIfLegacyProblems();