]> code.delx.au - refind/blobdiff - refind/main.c
Fix to hang when booting from Mac Firewire devices.
[refind] / refind / main.c
index 250ff1ba63dba933fc6a1134b4bf6019c93adfba..392bed2c53f0ef8a0acd2e4abc25fd50886844b0 100644 (file)
@@ -66,7 +66,7 @@
 #endif
 
 //
-// variables
+// constants
 
 #define MACOSX_LOADER_PATH      L"System\\Library\\CoreServices\\boot.efi"
 #if defined (EFIX64)
 #define SUBSCREEN_HINT2            L"Insert or F2 to edit options; Esc to return to main menu"
 #define SUBSCREEN_HINT2_NO_EDITOR  L"Esc to return to main menu"
 
+// Load types
+#define TYPE_EFI    1
+#define TYPE_LEGACY 2
+
 static REFIT_MENU_ENTRY MenuEntryAbout    = { L"About rEFInd", TAG_ABOUT, 1, 0, 'A', NULL, NULL, NULL };
 static REFIT_MENU_ENTRY MenuEntryReset    = { L"Reboot Computer", TAG_REBOOT, 1, 0, 'R', NULL, NULL, NULL };
 static REFIT_MENU_ENTRY MenuEntryShutdown = { L"Shut Down Computer", TAG_SHUTDOWN, 1, 0, 'U', NULL, NULL, NULL };
@@ -147,7 +151,7 @@ static VOID AboutrEFInd(VOID)
 
     if (AboutMenu.EntryCount == 0) {
         AboutMenu.TitleImage = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
-        AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.7.1.2");
+        AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.7.3.4");
         AddMenuInfoLine(&AboutMenu, L"");
         AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2006-2010 Christoph Pfisterer");
         AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2012-2013 Roderick W. Smith");
@@ -210,9 +214,44 @@ 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);
+
+    if ((RootDir == NULL) || (FileName == NULL)) {
+       // Assume valid here, because Macs produce NULL RootDir (& maybe FileName)
+       // when launching from a Firewire drive. This should be handled better, but
+       // fix would have to be in StartEFIImageList() and/or in FindVolumeAndFilename().
+       return TRUE;
+    } // if
+
+    Status = refit_call5_wrapper(RootDir->Open, RootDir, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
+    if (EFI_ERROR(Status))
+       return FALSE;
+
+    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,
+                                    IN CHAR16 *LoadOptions, IN UINTN LoaderType,
                                     IN CHAR16 *ImageTitle, IN CHAR8 OSType,
                                     OUT UINTN *ErrorInStep,
                                     IN BOOLEAN Verbose)
@@ -220,16 +259,18 @@ 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;
 
     // set load options
     if (LoadOptions != NULL) {
-        if (LoadOptionsPrefix != NULL) {
+        if (LoaderType == TYPE_EFI) {
             MergeStrings(&FullLoadOptions, LoadOptions, L' ');
             if (OSType == 'M') {
                MergeStrings(&FullLoadOptions, L" ", 0);
@@ -251,15 +292,24 @@ 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; but sometimes Volume comes back NULL, so provide
+       // an exception. (TODO: Handle this special condition better.)
+       if ((LoaderType == TYPE_LEGACY) || (Volume == NULL) || 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;
        }
@@ -311,7 +361,7 @@ bailout:
 } /* static EFI_STATUS StartEFIImageList() */
 
 static EFI_STATUS StartEFIImage(IN EFI_DEVICE_PATH *DevicePath,
-                                IN CHAR16 *LoadOptions, IN CHAR16 *LoadOptionsPrefix,
+                                IN CHAR16 *LoadOptions, IN UINTN LoaderType,
                                 IN CHAR16 *ImageTitle, IN CHAR8 OSType,
                                 OUT UINTN *ErrorInStep,
                                 IN BOOLEAN Verbose)
@@ -320,7 +370,7 @@ static EFI_STATUS StartEFIImage(IN EFI_DEVICE_PATH *DevicePath,
 
     DevicePaths[0] = DevicePath;
     DevicePaths[1] = NULL;
-    return StartEFIImageList(DevicePaths, LoadOptions, LoadOptionsPrefix, ImageTitle, OSType, ErrorInStep, Verbose);
+    return StartEFIImageList(DevicePaths, LoadOptions, LoaderType, ImageTitle, OSType, ErrorInStep, Verbose);
 } /* static EFI_STATUS StartEFIImage() */
 
 // From gummiboot: Retrieve a raw EFI variable.
@@ -390,7 +440,7 @@ static VOID StartLoader(LOADER_ENTRY *Entry)
     UINTN ErrorInStep = 0;
 
     BeginExternalScreen(Entry->UseGraphicsMode, L"Booting OS");
-    StartEFIImage(Entry->DevicePath, Entry->LoadOptions, Basename(Entry->LoaderPath),
+    StartEFIImage(Entry->DevicePath, Entry->LoadOptions, TYPE_EFI,
                   Basename(Entry->LoaderPath), Entry->OSType, &ErrorInStep, !Entry->UseGraphicsMode);
     FinishExternalScreen();
 }
@@ -1185,34 +1235,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
@@ -1249,7 +1271,7 @@ static BOOLEAN ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16
              SPrint(FileName, 255, L"\\%s", DirEntry->FileName);
           CleanUpPathNameSlashes(FileName);
 
-          if(!IsValidLoader(Volume, FileName))
+          if(!IsValidLoader(Volume->RootDir, FileName))
              continue;
 
           NewLoader = AllocateZeroPool(sizeof(struct LOADER_LIST));
@@ -1365,9 +1387,9 @@ static VOID ScanEfiFiles(REFIT_VOLUME *Volume) {
             Length = StrLen(Directory);
             if ((Length > 0) && ScanLoaderDir(Volume, Directory, MatchPatterns))
                ScanFallbackLoader = FALSE;
-         } // if
+            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....
@@ -1596,7 +1618,7 @@ static VOID StartLegacy(IN LEGACY_ENTRY *Entry)
 
     ExtractLegacyLoaderPaths(DiscoveredPathList, MAX_DISCOVERED_PATHS, LegacyLoaderList);
 
-    Status = StartEFIImageList(DiscoveredPathList, Entry->LoadOptions, NULL, L"legacy loader", 0, &ErrorInStep, TRUE);
+    Status = StartEFIImageList(DiscoveredPathList, Entry->LoadOptions, TYPE_LEGACY, L"legacy loader", 0, &ErrorInStep, TRUE);
     if (Status == EFI_NOT_FOUND) {
         if (ErrorInStep == 1) {
             Print(L"\nPlease make sure that you have the latest firmware update installed.\n");
@@ -1907,7 +1929,7 @@ static VOID ScanLegacyExternal(VOID)
 static VOID StartTool(IN LOADER_ENTRY *Entry)
 {
    BeginExternalScreen(Entry->UseGraphicsMode, Entry->me.Title + 6);  // assumes "Start <title>" as assigned below
-   StartEFIImage(Entry->DevicePath, Entry->LoadOptions, Basename(Entry->LoaderPath),
+   StartEFIImage(Entry->DevicePath, Entry->LoadOptions, TYPE_EFI,
                  Basename(Entry->LoaderPath), Entry->OSType, NULL, TRUE);
    FinishExternalScreen();
 } /* static VOID StartTool() */
@@ -1957,7 +1979,7 @@ static UINTN ScanDriverDir(IN CHAR16 *Path)
         SPrint(FileName, 255, L"%s\\%s", Path, DirEntry->FileName);
         NumFound++;
         Status = StartEFIImage(FileDevicePath(SelfLoadedImage->DeviceHandle, FileName),
-                               L"", DirEntry->FileName, DirEntry->FileName, 0, NULL, FALSE);
+                               L"", TYPE_EFI, DirEntry->FileName, 0, NULL, FALSE);
     }
     Status = DirIterClose(&DirIter);
     if (Status != EFI_NOT_FOUND) {