]> code.delx.au - refind/blobdiff - refind/main.c
New memory tester icon; refinement of progam-launch code.
[refind] / refind / main.c
index 8cfc5d6bc0ac182b471c46beac5ae8eedeb73cb3..c35ab1fa604d7beac79e60a43255b9a36e5f2b94 100644 (file)
 #endif
 
 //
-// variables
+// constants
 
 #define MACOSX_LOADER_PATH      L"System\\Library\\CoreServices\\boot.efi"
+#define MEMTEST_LOCATIONS       L"EFI\\tools,EFI\\tools\\memtest86,EFI\\tools\\memtest,EFI\\memtest86,EFI\\memtest"
 #if defined (EFIX64)
 #define SHELL_NAMES             L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shell.efi,\\shellx64.efi"
 #define GPTSYNC_NAMES           L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_x64.efi"
+#define MEMTEST_NAMES           L"memtest86.efi,memtest86_x64.efi,memtest86x64.efi,bootx64.efi"
 #define DRIVER_DIRS             L"drivers,drivers_x64"
 #define FALLBACK_FULLNAME       L"EFI\\BOOT\\bootx64.efi"
 #define FALLBACK_BASENAME       L"bootx64.efi"
@@ -79,6 +81,7 @@
 #elif defined (EFI32)
 #define SHELL_NAMES             L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shell.efi,\\shellia32.efi"
 #define GPTSYNC_NAMES           L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_ia32.efi"
+#define MEMTEST_NAMES           L"memtest86.efi,memtest86_ia32.efi,memtest86ia32.efi,bootia32.efi"
 #define DRIVER_DIRS             L"drivers,drivers_ia32"
 #define FALLBACK_FULLNAME       L"EFI\\BOOT\\bootia32.efi"
 #define FALLBACK_BASENAME       L"bootia32.efi"
@@ -86,6 +89,7 @@
 #else
 #define SHELL_NAMES             L"\\EFI\\tools\\shell.efi,\\shell.efi"
 #define GPTSYNC_NAMES           L"\\EFI\\tools\\gptsync.efi"
+#define MEMTEST_NAMES           L"memtest86.efi"
 #define DRIVER_DIRS             L"drivers"
 #define FALLBACK_FULLNAME       L"EFI\\BOOT\\boot.efi" /* Not really correct */
 #define FALLBACK_BASENAME       L"boot.efi"            /* Not really correct */
 #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 };
@@ -123,8 +131,8 @@ static REFIT_MENU_SCREEN AboutMenu      = { L"About", NULL, 0, NULL, 0, NULL, 0,
 
 REFIT_CONFIG GlobalConfig = { FALSE, FALSE, 0, 0, 0, DONT_CHANGE_TEXT_MODE, 20, 0, 0, GRAPHICS_FOR_OSX, LEGACY_TYPE_MAC, 0, 0,
                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                              { TAG_SHELL, TAG_APPLE_RECOVERY, TAG_MOK_TOOL, TAG_ABOUT, TAG_SHUTDOWN, TAG_REBOOT, TAG_FIRMWARE,
-                                0, 0, 0, 0, 0, 0 }
+                              { TAG_SHELL, TAG_MEMTEST, TAG_APPLE_RECOVERY, TAG_MOK_TOOL, TAG_ABOUT, TAG_SHUTDOWN,
+                                TAG_REBOOT, TAG_FIRMWARE, 0, 0, 0, 0, 0, 0 }
                             };
 
 EFI_GUID GlobalGuid = EFI_GLOBAL_VARIABLE;
@@ -147,7 +155,7 @@ static VOID AboutrEFInd(VOID)
 
     if (AboutMenu.EntryCount == 0) {
         AboutMenu.TitleImage = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
-        AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.7.1.3");
+        AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.7.3.9");
         AddMenuInfoLine(&AboutMenu, L"");
         AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2006-2010 Christoph Pfisterer");
         AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2012-2013 Roderick W. Smith");
@@ -219,9 +227,16 @@ static BOOLEAN IsValidLoader(EFI_FILE *RootDir, CHAR16 *FileName) {
     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 0;
+       return FALSE;
 
     Status = refit_call3_wrapper(FileHandle->Read, FileHandle, &Size, Header);
     refit_call1_wrapper(FileHandle->Close, FileHandle);
@@ -240,7 +255,7 @@ static BOOLEAN IsValidLoader(EFI_FILE *RootDir, CHAR16 *FileName) {
 
 // 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)
@@ -259,21 +274,17 @@ static EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths,
 
     // set load options
     if (LoadOptions != NULL) {
-        if (LoadOptionsPrefix != NULL) {
-            MergeStrings(&FullLoadOptions, LoadOptions, L' ');
-            if (OSType == 'M') {
-               MergeStrings(&FullLoadOptions, L" ", 0);
-               // NOTE: That last space is also added by the EFI shell and seems to be significant
-               //  when passing options to Apple's boot.efi...
-            } // if
-        } else {
-            MergeStrings(&FullLoadOptions, LoadOptions, 0);
-        } // if/else
+       MergeStrings(&FullLoadOptions, LoadOptions, 0);
+       if ((LoaderType == TYPE_EFI) && (OSType == 'M')) {
+           MergeStrings(&FullLoadOptions, L" ", 0);
+           // NOTE: That last space is also added by the EFI shell and seems to be significant
+           // when passing options to Apple's boot.efi...
+        } // if
     } else { // LoadOptions == NULL
        // NOTE: We provide a non-null string when no options are specified for safety;
        // some systems (at least DUET) can hang when launching some programs (such as
        // an EFI shell) without this.
-       FullLoadOptions = StrDuplicate(L" ");
+       FullLoadOptions = StrDuplicate(L"");
     }
     if (Verbose)
        Print(L"Starting %s\nUsing load options '%s'\n", ImageTitle, FullLoadOptions);
@@ -283,8 +294,9 @@ static EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths,
     for (DevicePathIndex = 0; DevicePaths[DevicePathIndex] != NULL; DevicePathIndex++) {
        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)) {
+       // 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
@@ -349,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)
@@ -358,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.
@@ -428,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();
 }
@@ -995,7 +1007,7 @@ LOADER_ENTRY * AddLoaderEntry(IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN
    if (Entry != NULL) {
       Entry->Title = StrDuplicate((LoaderTitle != NULL) ? LoaderTitle : LoaderPath);
       Entry->me.Title = AllocateZeroPool(sizeof(CHAR16) * 256);
-      SPrint(Entry->me.Title, 255, L"Boot %s from %s", (LoaderTitle != NULL) ? LoaderTitle : LoaderPath, Volume->VolName);
+      SPrint(Entry->me.Title, 255, L"Boot %s from %s ", (LoaderTitle != NULL) ? LoaderTitle : LoaderPath, Volume->VolName);
       Entry->me.Row = 0;
       Entry->me.BadgeImage = Volume->VolBadgeImage;
       if ((LoaderPath != NULL) && (LoaderPath[0] != L'\\')) {
@@ -1356,7 +1368,8 @@ static VOID ScanEfiFiles(REFIT_VOLUME *Volume) {
       // scan subdirectories of the EFI directory (as per the standard)
       DirIterOpen(Volume->RootDir, L"EFI", &EfiDirIter);
       while (DirIterNext(&EfiDirIter, 1, NULL, &EfiDirEntry)) {
-         if (StriCmp(EfiDirEntry->FileName, L"tools") == 0 || EfiDirEntry->FileName[0] == '.')
+         if (StriCmp(EfiDirEntry->FileName, L"tools") == 0 || (StriCmp(EfiDirEntry->FileName, L"memtest") == 0) ||
+             (StriCmp(EfiDirEntry->FileName, L"memtest86") == 0) || EfiDirEntry->FileName[0] == '.')
             continue;   // skip this, doesn't contain boot loaders or is scanned later
          SPrint(FileName, 255, L"EFI\\%s", EfiDirEntry->FileName);
          if (ScanLoaderDir(Volume, FileName, MatchPatterns))
@@ -1606,7 +1619,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");
@@ -1917,7 +1930,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() */
@@ -1967,7 +1980,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) {
@@ -2186,7 +2199,7 @@ static VOID ScanForBootloaders(VOID) {
 // Add the second-row tags containing built-in and external tools (EFI shell,
 // reboot, etc.)
 static VOID ScanForTools(VOID) {
-   CHAR16 *FileName = NULL, *MokLocations, *MokName, *PathName, Description[256];
+   CHAR16 *FileName = NULL, *MokLocations, *MokName, *MemtestName, *PathName, Description[256];
    REFIT_MENU_ENTRY *TempMenuEntry;
    UINTN i, j, k, VolumeIndex;
    UINT64 osind;
@@ -2250,6 +2263,7 @@ static VOID ScanForTools(VOID) {
             } // while
             FileName = NULL;
             break;
+
          case TAG_APPLE_RECOVERY:
             FileName = StrDuplicate(L"\\com.apple.recovery.boot\\boot.efi");
             for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
@@ -2262,6 +2276,7 @@ static VOID ScanForTools(VOID) {
             MyFreePool(FileName);
             FileName = NULL;
             break;
+
          case TAG_MOK_TOOL:
             j = 0;
             while ((FileName = FindCommaDelimited(MokLocations, j++)) != NULL) {
@@ -2281,8 +2296,29 @@ static VOID ScanForTools(VOID) {
                } // while MOK_NAMES
                MyFreePool(FileName);
             } // while MokLocations
+            break;
 
+         case TAG_MEMTEST:
+            j = 0;
+            while ((FileName = FindCommaDelimited(MEMTEST_LOCATIONS, j++)) != NULL) {
+               k = 0;
+               while ((MemtestName = FindCommaDelimited(MEMTEST_NAMES, k++)) != NULL) {
+                  PathName = StrDuplicate(FileName);
+                  MergeStrings(&PathName, MemtestName, (StriCmp(PathName, L"\\") == 0) ? 0 : L'\\');
+                  for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
+                     if ((Volumes[VolumeIndex]->RootDir != NULL) && (FileExists(Volumes[VolumeIndex]->RootDir, PathName))) {
+                        SPrint(Description, 255, L"Memory test utility at %s on %s", PathName, Volumes[VolumeIndex]->VolName);
+                        AddToolEntry(Volumes[VolumeIndex]->DeviceHandle, PathName, Description,
+                                     BuiltinIcon(BUILTIN_ICON_TOOL_MEMTEST), 'S', FALSE);
+                     } // if
+                  } // for
+                  MyFreePool(PathName);
+                  MyFreePool(MemtestName);
+               } // while MEMTEST_NAMES
+               MyFreePool(FileName);
+            } // while MEMTEST_LOCATIONS
             break;
+
       } // switch()
    } // for
 } // static VOID ScanForTools