]> code.delx.au - refind/blobdiff - refind/main.c
Added "scan_all_linux_kernels" refind.conf option & improved case
[refind] / refind / main.c
index 5e8496e0156f4f1b72f8216b9f7a99ec2d849f88..7ce4bcc5b78411447c5df03967cab2de236b838f 100644 (file)
 #include "icns.h"
 #include "menu.h"
 #include "refit_call_wrapper.h"
+#include "driver_support.h"
 #include "../include/syslinux_mbr.h"
 
 // 
 // variables
 
-#define MACOSX_LOADER_PATH      L"\\System\\Library\\CoreServices\\boot.efi"
+#define MACOSX_LOADER_PATH      L"System\\Library\\CoreServices\\boot.efi"
 #if defined (EFIX64)
 #define SHELL_NAMES             L"\\EFI\\tools\\shell.efi,\\shellx64.efi"
 #elif defined (EFI32)
 #define SHELL_NAMES             L"\\EFI\\tools\\shell.efi"
 #endif
 
+// Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
+// L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
+// comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
+// no harm on other computers, AFAIK. In theory, every case variation should be done for
+// completeness, but that's ridiculous....
+#define LOADER_MATCH_PATTERNS   L"*.efi,*.EFI"
+
+// Patterns that identify Linux kernels. Added to the loader match pattern when the
+// scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
+// a ".efi" extension to be found when scanning for boot loaders.
+#define LINUX_MATCH_PATTERNS    L"vmlinuz*,bzImage*"
+
 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 };
@@ -72,7 +85,7 @@ static REFIT_MENU_ENTRY MenuEntryExit     = { L"Exit rEFInd", TAG_EXIT, 1, 0, 0,
 static REFIT_MENU_SCREEN MainMenu       = { L"Main Menu", NULL, 0, NULL, 0, NULL, 0, L"Automatic boot" };
 static REFIT_MENU_SCREEN AboutMenu      = { L"About", NULL, 0, NULL, 0, NULL, 0, NULL };
 
-REFIT_CONFIG GlobalConfig = { FALSE, 20, 0, 0, NULL, NULL, NULL, NULL,
+REFIT_CONFIG GlobalConfig = { FALSE, FALSE, 20, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL,
                               {TAG_SHELL, TAG_ABOUT, TAG_SHUTDOWN, TAG_REBOOT, 0, 0, 0, 0, 0 }};
 
 //
@@ -83,7 +96,7 @@ static VOID AboutrEFInd(VOID)
 {
     if (AboutMenu.EntryCount == 0) {
         AboutMenu.TitleImage = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
-        AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.2.6");
+        AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.2.7.2");
         AddMenuInfoLine(&AboutMenu, L"");
         AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2006-2010 Christoph Pfisterer");
         AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2012 Roderick W. Smith");
@@ -115,7 +128,8 @@ static VOID AboutrEFInd(VOID)
 static EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths,
                                     IN CHAR16 *LoadOptions, IN CHAR16 *LoadOptionsPrefix,
                                     IN CHAR16 *ImageTitle,
-                                    OUT UINTN *ErrorInStep)
+                                    OUT UINTN *ErrorInStep,
+                                    IN BOOLEAN Verbose)
 {
     EFI_STATUS              Status, ReturnStatus;
     EFI_HANDLE              ChildImageHandle;
@@ -124,7 +138,8 @@ static EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths,
     CHAR16                  ErrorInfo[256];
     CHAR16                  *FullLoadOptions = NULL;
 
-    Print(L"Starting %s\n", ImageTitle);
+    if (Verbose)
+        Print(L"Starting %s\n", ImageTitle);
     if (ErrorInStep != NULL)
         *ErrorInStep = 0;
 
@@ -160,7 +175,8 @@ static EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths,
         // NOTE: We also include the terminating null in the length for safety.
         ChildLoadedImage->LoadOptions = (VOID *)LoadOptions;
         ChildLoadedImage->LoadOptionsSize = ((UINT32)StrLen(LoadOptions) + 1) * sizeof(CHAR16);
-        Print(L"Using load options '%s'\n", LoadOptions);
+        if (Verbose)
+            Print(L"Using load options '%s'\n", LoadOptions);
     }
 
     // close open file handles
@@ -191,13 +207,14 @@ bailout:
 static EFI_STATUS StartEFIImage(IN EFI_DEVICE_PATH *DevicePath,
                                 IN CHAR16 *LoadOptions, IN CHAR16 *LoadOptionsPrefix,
                                 IN CHAR16 *ImageTitle,
-                                OUT UINTN *ErrorInStep)
+                                OUT UINTN *ErrorInStep,
+                                IN BOOLEAN Verbose)
 {
     EFI_DEVICE_PATH *DevicePaths[2];
 
     DevicePaths[0] = DevicePath;
     DevicePaths[1] = NULL;
-    return StartEFIImageList(DevicePaths, LoadOptions, LoadOptionsPrefix, ImageTitle, ErrorInStep);
+    return StartEFIImageList(DevicePaths, LoadOptions, LoadOptionsPrefix, ImageTitle, ErrorInStep, Verbose);
 } /* static EFI_STATUS StartEFIImage() */
 
 //
@@ -210,7 +227,7 @@ static VOID StartLoader(IN LOADER_ENTRY *Entry)
 
     BeginExternalScreen(Entry->UseGraphicsMode, L"Booting OS");
     StartEFIImage(Entry->DevicePath, Entry->LoadOptions,
-                  Basename(Entry->LoaderPath), Basename(Entry->LoaderPath), &ErrorInStep);
+                  Basename(Entry->LoaderPath), Basename(Entry->LoaderPath), &ErrorInStep, TRUE);
     FinishExternalScreen();
 }
 
@@ -395,7 +412,7 @@ VOID GenerateSubScreen(LOADER_ENTRY *Entry, IN REFIT_VOLUME *Volume) {
             AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
          }
 #endif
-      
+
          SubEntry = InitializeLoaderEntry(Entry);
          if (SubEntry != NULL) {
             SubEntry->me.Title        = L"Boot Mac OS X in single user mode";
@@ -606,10 +623,11 @@ VOID SetLoaderDefaults(LOADER_ENTRY *Entry, CHAR16 *LoaderPath, IN REFIT_VOLUME
 LOADER_ENTRY * AddLoaderEntry(IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume) {
    LOADER_ENTRY      *Entry;
 
+   CleanUpPathNameSlashes(LoaderPath);
    Entry = InitializeLoaderEntry(NULL);
    if (Entry != NULL) {
       Entry->Title = StrDuplicate(LoaderTitle);
-      Entry->me.Title = PoolPrint(L"Boot %s from %s", (LoaderTitle != NULL) ? LoaderTitle : LoaderPath + 1, Volume->VolName);
+      Entry->me.Title = PoolPrint(L"Boot %s from %s", (LoaderTitle != NULL) ? LoaderTitle : LoaderPath, Volume->VolName);
       Entry->me.Row = 0;
       Entry->me.BadgeImage = Volume->VolBadgeImage;
       Entry->LoaderPath = StrDuplicate(LoaderPath);
@@ -625,20 +643,18 @@ LOADER_ENTRY * AddLoaderEntry(IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN
 
 // Scan an individual directory for EFI boot loader files and, if found,
 // add them to the list.
-static VOID ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path)
+static VOID ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 *Pattern)
 {
     EFI_STATUS              Status;
     REFIT_DIR_ITER          DirIter;
     EFI_FILE_INFO           *DirEntry;
     CHAR16                  FileName[256];
 
-    // Note: SelfDirPath includes a leading backslash ('\'), but Path
-    // doesn't, so we rejigger the string to compensate....
-    if (!SelfDirPath || !Path || ((StriCmp(Path, &SelfDirPath[1]) == 0) && Volume != SelfVolume) ||
-        (StriCmp(Path, &SelfDirPath[1]) != 0)) {
+    if (!SelfDirPath || !Path || ((StriCmp(Path, SelfDirPath) == 0) && Volume != SelfVolume) ||
+        (StriCmp(Path, SelfDirPath) != 0)) {
        // look through contents of the directory
        DirIterOpen(Volume->RootDir, Path, &DirIter);
-       while (DirIterNext(&DirIter, 2, L"*.efi", &DirEntry)) {
+       while (DirIterNext(&DirIter, 2, Pattern, &DirEntry)) {
           if (DirEntry->FileName[0] == '.' ||
               StriCmp(DirEntry->FileName, L"TextMode.efi") == 0 ||
               StriCmp(DirEntry->FileName, L"ebounce.efi") == 0 ||
@@ -667,7 +683,12 @@ static VOID ScanEfiFiles(REFIT_VOLUME *Volume) {
    EFI_STATUS              Status;
    REFIT_DIR_ITER          EfiDirIter;
    EFI_FILE_INFO           *EfiDirEntry;
-   CHAR16                  FileName[256];
+   CHAR16                  FileName[256], *Directory, *MatchPatterns;
+   UINTN                   i, Length;
+
+    MatchPatterns = StrDuplicate(LOADER_MATCH_PATTERNS);
+    if (GlobalConfig.ScanAllLinux)
+       MergeStrings(&MatchPatterns, LINUX_MATCH_PATTERNS, L',');
 
    if ((Volume->RootDir != NULL) && (Volume->VolName != NULL)) {
       // check for Mac OS X boot loader
@@ -677,23 +698,19 @@ static VOID ScanEfiFiles(REFIT_VOLUME *Volume) {
       }
 
       // check for XOM
-      StrCpy(FileName, L"\\System\\Library\\CoreServices\\xom.efi");
+      StrCpy(FileName, L"System\\Library\\CoreServices\\xom.efi");
       if (FileExists(Volume->RootDir, FileName)) {
          AddLoaderEntry(FileName, L"Windows XP (XoM)", Volume);
       }
 
       // check for Microsoft boot loader/menu
-      StrCpy(FileName, L"\\EFI\\Microsoft\\Boot\\Bootmgfw.efi");
+      StrCpy(FileName, L"EFI\\Microsoft\\Boot\\Bootmgfw.efi");
       if (FileExists(Volume->RootDir, FileName)) {
          AddLoaderEntry(FileName, L"Microsoft EFI boot", Volume);
       }
 
       // scan the root directory for EFI executables
-      ScanLoaderDir(Volume, NULL);
-      // scan the elilo directory (as used on gimli's first Live CD)
-      ScanLoaderDir(Volume, L"elilo");
-      // scan the boot directory
-      ScanLoaderDir(Volume, L"boot");
+      ScanLoaderDir(Volume, NULL, MatchPatterns);
 
       // scan subdirectories of the EFI directory (as per the standard)
       DirIterOpen(Volume->RootDir, L"EFI", &EfiDirIter);
@@ -701,11 +718,25 @@ static VOID ScanEfiFiles(REFIT_VOLUME *Volume) {
          if (StriCmp(EfiDirEntry->FileName, L"tools") == 0 || EfiDirEntry->FileName[0] == '.')
             continue;   // skip this, doesn't contain boot loaders
          SPrint(FileName, 255, L"EFI\\%s", EfiDirEntry->FileName);
-         ScanLoaderDir(Volume, FileName);
+         ScanLoaderDir(Volume, FileName, MatchPatterns);
       } // while()
       Status = DirIterClose(&EfiDirIter);
       if (Status != EFI_NOT_FOUND)
          CheckError(Status, L"while scanning the EFI directory");
+
+      // Scan user-specified (or additional default) directories....
+      i = 0;
+      while ((Directory = FindCommaDelimited(GlobalConfig.AlsoScan, i++)) != NULL) {
+         Length = StrLen(Directory);
+         // Some EFI implementations won't read a directory if the path ends in
+         // a backslash, so eliminate this character, if it's present....
+         while ((Length > 0) && (Directory[Length - 1] == L'\\')) {
+            Directory[--Length] = 0;
+         } // while
+         if (Length > 0)
+            ScanLoaderDir(Volume, Directory, MatchPatterns);
+         FreePool(Directory);
+      } // while
    } // if
 } // static VOID ScanEfiFiles()
 
@@ -921,7 +952,7 @@ static VOID StartLegacy(IN LEGACY_ENTRY *Entry)
 
     ExtractLegacyLoaderPaths(DiscoveredPathList, MAX_DISCOVERED_PATHS, LegacyLoaderList);
 
-    Status = StartEFIImageList(DiscoveredPathList, Entry->LoadOptions, NULL, L"legacy loader", &ErrorInStep);
+    Status = StartEFIImageList(DiscoveredPathList, Entry->LoadOptions, NULL, L"legacy loader", &ErrorInStep, TRUE);
     if (Status == EFI_NOT_FOUND) {
         if (ErrorInStep == 1) {
             Print(L"\nPlease make sure that you have the latest firmware update installed.\n");
@@ -978,7 +1009,7 @@ static LEGACY_ENTRY * AddLegacyEntry(IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Vo
     SubEntry->Volume          = Entry->Volume;
     SubEntry->LoadOptions     = Entry->LoadOptions;
     AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
-    
+
     AddMenuEntry(SubScreen, &MenuEntryReturn);
     Entry->me.SubScreen = SubScreen;
     AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
@@ -1065,7 +1096,7 @@ 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),
-                  Basename(Entry->LoaderPath), NULL);
+                  Basename(Entry->LoaderPath), NULL, TRUE);
     FinishExternalScreen();
 } /* static VOID StartTool() */
 
@@ -1089,69 +1120,49 @@ static LOADER_ENTRY * AddToolEntry(IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle
     return Entry;
 } /* static LOADER_ENTRY * AddToolEntry() */
 
-#ifdef DEBIAN_ENABLE_EFI110
 //
 // pre-boot driver functions
 //
 
-static VOID ScanDriverDir(IN CHAR16 *Path)
+static UINTN ScanDriverDir(IN CHAR16 *Path)
 {
     EFI_STATUS              Status;
     REFIT_DIR_ITER          DirIter;
+    UINTN                   NumFound = 0;
     EFI_FILE_INFO           *DirEntry;
     CHAR16                  FileName[256];
 
     // look through contents of the directory
     DirIterOpen(SelfRootDir, Path, &DirIter);
-    while (DirIterNext(&DirIter, 2, L"*.EFI", &DirEntry)) {
+    while (DirIterNext(&DirIter, 2, L"*.efi", &DirEntry)) {
         if (DirEntry->FileName[0] == '.')
             continue;   // skip this
 
         SPrint(FileName, 255, L"%s\\%s", Path, DirEntry->FileName);
+        NumFound++;
         Status = StartEFIImage(FileDevicePath(SelfLoadedImage->DeviceHandle, FileName),
-                               L"", DirEntry->FileName, DirEntry->FileName, NULL);
+                               L"", DirEntry->FileName, DirEntry->FileName, NULL, FALSE);
     }
     Status = DirIterClose(&DirIter);
     if (Status != EFI_NOT_FOUND) {
         SPrint(FileName, 255, L"while scanning the %s directory", Path);
         CheckError(Status, FileName);
     }
+    return (NumFound);
 }
-/* EFI_STATUS
-LibScanHandleDatabase (
-     EFI_HANDLE  DriverBindingHandle, OPTIONAL
-     UINT32      *DriverBindingHandleIndex, OPTIONAL
-     EFI_HANDLE  ControllerHandle, OPTIONAL
-     UINT32      *ControllerHandleIndex, OPTIONAL
-     UINTN       *HandleCount,
-     EFI_HANDLE  **HandleBuffer,
-     UINT32      **HandleType
-     );
-#define EFI_HANDLE_TYPE_UNKNOWN                     0x000
-#define EFI_HANDLE_TYPE_IMAGE_HANDLE                0x001
-#define EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE       0x002
-#define EFI_HANDLE_TYPE_DEVICE_DRIVER               0x004
-#define EFI_HANDLE_TYPE_BUS_DRIVER                  0x008
-#define EFI_HANDLE_TYPE_DRIVER_CONFIGURATION_HANDLE 0x010
-#define EFI_HANDLE_TYPE_DRIVER_DIAGNOSTICS_HANDLE   0x020
-#define EFI_HANDLE_TYPE_COMPONENT_NAME_HANDLE       0x040
-#define EFI_HANDLE_TYPE_DEVICE_HANDLE               0x080
-#define EFI_HANDLE_TYPE_PARENT_HANDLE               0x100
-#define EFI_HANDLE_TYPE_CONTROLLER_HANDLE           0x200
-#define EFI_HANDLE_TYPE_CHILD_HANDLE                0x400 */
 
 static EFI_STATUS ConnectAllDriversToAllControllers(VOID)
 {
-    EFI_STATUS  Status;
-    UINTN       AllHandleCount;
-    EFI_HANDLE  *AllHandleBuffer;
-    UINTN       Index;
-    UINTN       HandleCount;
-    EFI_HANDLE  *HandleBuffer;
-    UINT32      *HandleType;
-    UINTN       HandleIndex;
-    BOOLEAN     Parent;
-    BOOLEAN     Device;
+    EFI_STATUS           Status;
+    UINTN                AllHandleCount;
+    EFI_HANDLE           *AllHandleBuffer;
+    UINTN                Index;
+    UINTN                HandleCount;
+    EFI_HANDLE           *HandleBuffer;
+    UINT32               *HandleType;
+    UINTN                HandleIndex;
+    BOOLEAN              Parent;
+    BOOLEAN              Device;
 
     Status = LibLocateHandle(AllHandles,
                              NULL,
@@ -1186,15 +1197,15 @@ static EFI_STATUS ConnectAllDriversToAllControllers(VOID)
             for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
                 if (HandleType[HandleIndex] & EFI_HANDLE_TYPE_PARENT_HANDLE)
                     Parent = TRUE;
-            }
+            } // for
 
             if (!Parent) {
                 if (HandleType[Index] & EFI_HANDLE_TYPE_DEVICE_HANDLE) {
-                    Status = refit_call4_wrapper(BS->ConnectController,
-                                                 AllHandleBuffer[Index],
-                                                 NULL,
-                                                 NULL,
-                                                 TRUE);
+                   Status = refit_call4_wrapper(BS->ConnectController,
+                                                AllHandleBuffer[Index],
+                                                NULL,
+                                                NULL,
+                                                TRUE);
                 }
             }
         }
@@ -1206,23 +1217,35 @@ static EFI_STATUS ConnectAllDriversToAllControllers(VOID)
 Done:
     FreePool (AllHandleBuffer);
     return Status;
-}
+} /* EFI_STATUS ConnectAllDriversToAllControllers() */
 
 static VOID LoadDrivers(VOID)
 {
-    CHAR16                  DirName[256];
-
-    // load drivers from /efi/refind/drivers
-    SPrint(DirName, 255, L"%s\\drivers", SelfDirPath);
-    ScanDriverDir(DirName);
-
-    // load drivers from /efi/tools/drivers
-    ScanDriverDir(L"\\efi\\tools\\drivers");
+    CHAR16        *Directory;
+    UINTN         i = 0, Length, NumFound = 0;
+
+    // load drivers from the "drivers" subdirectory of rEFInd's home directory
+    Directory = StrDuplicate(SelfDirPath);
+    MergeStrings(&Directory, L"drivers", L'\\');
+    NumFound += ScanDriverDir(Directory);
+
+    // Scan additional user-specified driver directories....
+    while ((Directory = FindCommaDelimited(GlobalConfig.DriverDirs, i++)) != NULL) {
+       Length = StrLen(Directory);
+       // Some EFI implementations won't read a directory if the path ends in
+       // a backslash, so eliminate this character, if it's present....
+       while ((Length > 0) && (Directory[Length - 1] == L'\\')) {
+          Directory[--Length] = 0;
+       } // while
+       if (Length > 0)
+          NumFound += ScanDriverDir(Directory);
+       FreePool(Directory);
+    } // while
 
     // connect all devices
-    ConnectAllDriversToAllControllers();
+    if (NumFound > 0)
+       ConnectAllDriversToAllControllers();
 }
-#endif /* DEBIAN_ENABLE_EFI110 */
 
 static VOID ScanForBootloaders(VOID) {
    UINTN i;
@@ -1352,9 +1375,7 @@ efi_main (IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)
 
     // further bootstrap (now with config available)
     SetupScreen();
-#ifdef DEBIAN_ENABLE_EFI110
     LoadDrivers();
-#endif /* DEBIAN_ENABLE_EFI110 */
     ScanForBootloaders();
     ScanForTools();
 
@@ -1414,6 +1435,6 @@ efi_main (IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)
     // fails, go into an endless loop.
     refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
     EndlessIdleLoop();
-    
+
     return EFI_SUCCESS;
 } /* efi_main() */