]> code.delx.au - refind/blobdiff - refind/config.c
Added feature to detect the root (/) Linux filesystem based on
[refind] / refind / config.c
index c56c54f1332d3e14fd028a24397cd02754ac4e9d..4abd48b7dbc36d5d0fe3d5440aeb741e893e1dfe 100644 (file)
@@ -35,7 +35,7 @@
  */
 
 /*
- * Modifications copyright (c) 2012-2013 Roderick W. Smith
+ * Modifications copyright (c) 2012-2015 Roderick W. Smith
  * 
  * Modifications distributed under the terms of the GNU General Public
  * License (GPL) version 3 (GPLv3), a copy of which must be distributed
@@ -50,6 +50,7 @@
 #include "config.h"
 #include "screen.h"
 #include "../include/refit_call_wrapper.h"
+#include "../mok/mok.h"
 
 // constants
 
@@ -63,7 +64,8 @@
 #define GetTime ST->RuntimeServices->GetTime
 #define LAST_MINUTE 1439 /* Last minute of a day */
 
-static REFIT_MENU_ENTRY MenuEntryReturn   = { L"Return to Main Menu", TAG_RETURN, 0, 0, 0, NULL, NULL, NULL };
+extern REFIT_MENU_ENTRY MenuEntryReturn;
+//static REFIT_MENU_ENTRY MenuEntryReturn   = { L"Return to Main Menu", TAG_RETURN, 0, 0, 0, NULL, NULL, NULL };
 
 //
 // read a file into a buffer
@@ -265,6 +267,10 @@ UINTN ReadTokenLine(IN REFIT_FILE *File, OUT CHAR16 ***TokenList)
         Line = ReadLine(File);
         if (Line == NULL)
             return(0);
+        if (Line[0] == L'\0') {
+            MyFreePool(Line);
+            return(0);
+        } // if
 
         p = Line;
         LineFinished = FALSE;
@@ -318,9 +324,18 @@ static VOID HandleInt(IN CHAR16 **TokenList, IN UINTN TokenCount, OUT UINTN *Val
 
 // handle a parameter with a single string argument
 static VOID HandleString(IN CHAR16 **TokenList, IN UINTN TokenCount, OUT CHAR16 **Target) {
-   if (TokenCount == 2) {
-      MyFreePool(*Target);
-      *Target = StrDuplicate(TokenList[1]);
+   if ((TokenCount == 2) && Target) {
+      if ((StrLen(TokenList[1]) > 1) && (TokenList[1][0] == L'+') &&
+          ((TokenList[1][1] == L',') || (TokenList[1][1] == L' '))) {
+         if (*Target) {
+            MergeStrings(Target, TokenList[1] + 2, L',');
+         } else {
+            *Target = StrDuplicate(TokenList[1] + 2);
+         } // if/else
+      } else {
+         MyFreePool(*Target);
+         *Target = StrDuplicate(TokenList[1]);
+      } // if/else
    } // if
 } // static VOID HandleString()
 
@@ -372,12 +387,24 @@ static UINTN HandleTime(IN CHAR16 *TimeString) {
    return (Hour * 60 + Minute);
 } // BOOLEAN HandleTime()
 
+static BOOLEAN HandleBoolean(IN CHAR16 **TokenList, IN UINTN TokenCount) {
+   BOOLEAN TruthValue = TRUE;
+
+   if ((TokenCount >= 2) && ((StriCmp(TokenList[1], L"0") == 0) ||
+                             (StriCmp(TokenList[1], L"false") == 0) ||
+                             (StriCmp(TokenList[1], L"off") == 0))) {
+      TruthValue = FALSE;
+   } // if
+
+   return TruthValue;
+} // BOOLEAN HandleBoolean
+
 // Sets the default boot loader IF the current time is within the bounds
 // defined by the third and fourth tokens in the TokenList.
 static VOID SetDefaultByTime(IN CHAR16 **TokenList, OUT CHAR16 **Default) {
    EFI_STATUS            Status;
    EFI_TIME              CurrentTime;
-   UINTN                 StartTime = LAST_MINUTE + 1, EndTime = LAST_MINUTE + 1, Now;
+   UINTN                 StartTime, EndTime, Now;
    BOOLEAN               SetIt = FALSE;
 
    StartTime = HandleTime(TokenList[2]);
@@ -418,6 +445,7 @@ VOID ReadConfig(CHAR16 *FileName)
     CHAR16          *FlagName;
     CHAR16          *TempStr = NULL;
     UINTN           TokenCount, i;
+    EFI_GUID        RefindGuid = REFIND_GUID_VALUE;
 
     // Set a few defaults only if we're loading the default file.
     if (StriCmp(FileName, GlobalConfig.ConfigFilename) == 0) {
@@ -442,11 +470,22 @@ VOID ReadConfig(CHAR16 *FileName)
        MyFreePool(GlobalConfig.DontScanVolumes);
        GlobalConfig.DontScanVolumes = StrDuplicate(DONT_SCAN_VOLUMES);
        GlobalConfig.WindowsRecoveryFiles = StrDuplicate(WINDOWS_RECOVERY_FILES);
+       if (GlobalConfig.DefaultSelection != NULL) {
+          MyFreePool(GlobalConfig.DefaultSelection);
+          GlobalConfig.DefaultSelection = NULL;
+       }
+       Status = EfivarGetRaw(&RefindGuid, L"PreviousBoot", (CHAR8**) &(GlobalConfig.DefaultSelection), &i);
+       if (Status != EFI_SUCCESS)
+          GlobalConfig.DefaultSelection = NULL;
     } // if
 
     if (!FileExists(SelfDir, FileName)) {
-        Print(L"Configuration file '%s' missing!\n", FileName);
-        return;
+       Print(L"Configuration file '%s' missing!\n", FileName);
+       if (!FileExists(SelfDir, L"icons")) {
+          Print(L"Icons directory doesn't exist; setting textonly = TRUE!\n");
+          GlobalConfig.TextOnly = TRUE;
+       }
+       return;
     }
 
     Status = ReadFile(SelfDir, FileName, &File, &i);
@@ -480,6 +519,8 @@ VOID ReadConfig(CHAR16 *FileName)
                    GlobalConfig.HideUIFlags |= HIDEUI_FLAG_EDITOR;
                 } else if (StriCmp(FlagName, L"safemode") == 0) {
                    GlobalConfig.HideUIFlags |= HIDEUI_FLAG_SAFEMODE;
+                } else if (StriCmp(FlagName, L"badges") == 0) {
+                   GlobalConfig.HideUIFlags |= HIDEUI_FLAG_BADGES;
                 } else if (StriCmp(FlagName, L"all") == 0) {
                    GlobalConfig.HideUIFlags = HIDEUI_FLAG_ALL;
                 } else {
@@ -498,6 +539,9 @@ VOID ReadConfig(CHAR16 *FileName)
                  GlobalConfig.ScanFor[i] = ' ';
            }
 
+        } else if (StriCmp(TokenList[0], L"uefi_deep_legacy_scan") == 0) {
+           GlobalConfig.DeepLegacyScan = HandleBoolean(TokenList, TokenCount);
+
         } else if ((StriCmp(TokenList[0], L"scan_delay") == 0) && (TokenCount == 2)) {
            HandleInt(TokenList, TokenCount, &(GlobalConfig.ScanDelay));
 
@@ -532,6 +576,8 @@ VOID ReadConfig(CHAR16 *FileName)
                     GlobalConfig.ShowTools[i - 1] = TAG_SHELL;
                 } else if (StriCmp(FlagName, L"gptsync") == 0) {
                     GlobalConfig.ShowTools[i - 1] = TAG_GPTSYNC;
+                } else if (StriCmp(FlagName, L"gdisk") == 0) {
+                   GlobalConfig.ShowTools[i - 1] = TAG_GDISK;
                 } else if (StriCmp(FlagName, L"about") == 0) {
                    GlobalConfig.ShowTools[i - 1] = TAG_ABOUT;
                 } else if (StriCmp(FlagName, L"exit") == 0) {
@@ -550,6 +596,8 @@ VOID ReadConfig(CHAR16 *FileName)
                    GlobalConfig.ShowTools[i - 1] = TAG_FIRMWARE;
                 } else if ((StriCmp(FlagName, L"memtest86") == 0) || (StriCmp(FlagName, L"memtest") == 0)) {
                    GlobalConfig.ShowTools[i - 1] = TAG_MEMTEST;
+                } else if (StriCmp(FlagName, L"netboot") == 0) {
+                   GlobalConfig.ShowTools[i - 1] = TAG_NETBOOT;
                 } else {
                    Print(L" unknown showtools flag: '%s'\n", FlagName);
                 }
@@ -558,6 +606,27 @@ VOID ReadConfig(CHAR16 *FileName)
         } else if (StriCmp(TokenList[0], L"banner") == 0) {
            HandleString(TokenList, TokenCount, &(GlobalConfig.BannerFileName));
 
+        } else if ((StriCmp(TokenList[0], L"banner_scale") == 0) && (TokenCount == 2)) {
+           if (StriCmp(TokenList[1], L"noscale") == 0) {
+              GlobalConfig.BannerScale = BANNER_NOSCALE;
+           } else if ((StriCmp(TokenList[1], L"fillscreen") == 0) || (StriCmp(TokenList[1], L"fullscreen") == 0)) {
+              GlobalConfig.BannerScale = BANNER_FILLSCREEN;
+           } else {
+              Print(L" unknown banner_type flag: '%s'\n", TokenList[1]);
+           } // if/else
+
+        } else if ((StriCmp(TokenList[0], L"small_icon_size") == 0) && (TokenCount == 2)) {
+           HandleInt(TokenList, TokenCount, &i);
+           if (i >= 32)
+              GlobalConfig.IconSizes[ICON_SIZE_SMALL] = i;
+
+        } else if ((StriCmp(TokenList[0], L"big_icon_size") == 0) && (TokenCount == 2)) {
+           HandleInt(TokenList, TokenCount, &i);
+           if (i >= 32) {
+              GlobalConfig.IconSizes[ICON_SIZE_BIG] = i;
+              GlobalConfig.IconSizes[ICON_SIZE_BADGE] = i / 4;
+           }
+
         } else if (StriCmp(TokenList[0], L"selection_small") == 0) {
            HandleString(TokenList, TokenCount, &(GlobalConfig.SelectionSmallFileName));
 
@@ -572,11 +641,7 @@ VOID ReadConfig(CHAR16 *FileName)
            }
 
         } else if (StriCmp(TokenList[0], L"textonly") == 0) {
-           if ((TokenCount >= 2) && (StriCmp(TokenList[1], L"0") == 0)) {
-              GlobalConfig.TextOnly = FALSE;
-           } else {
-              GlobalConfig.TextOnly = TRUE;
-           }
+           GlobalConfig.TextOnly = HandleBoolean(TokenList, TokenCount);
 
         } else if (StriCmp(TokenList[0], L"textmode") == 0) {
            HandleInt(TokenList, TokenCount, &(GlobalConfig.RequestedTextMode));
@@ -612,15 +677,14 @@ VOID ReadConfig(CHAR16 *FileName)
            egLoadFont(TokenList[1]);
 
         } else if (StriCmp(TokenList[0], L"scan_all_linux_kernels") == 0) {
-           if ((TokenCount >= 2) && (StriCmp(TokenList[1], L"0") == 0)) {
-              GlobalConfig.ScanAllLinux = FALSE;
-           } else {
-              GlobalConfig.ScanAllLinux = TRUE;
-           }
+           GlobalConfig.ScanAllLinux = HandleBoolean(TokenList, TokenCount);
 
         } else if (StriCmp(TokenList[0], L"max_tags") == 0) {
            HandleInt(TokenList, TokenCount, &(GlobalConfig.MaxTags));
 
+       } else if (StriCmp(TokenList[0], L"enable_and_lock_vmx") == 0) {
+          GlobalConfig.EnableAndLockVMX = HandleBoolean(TokenList, TokenCount);
+
         } else if ((StriCmp(TokenList[0], L"include") == 0) && (TokenCount == 2) &&
                    (StriCmp(FileName, GlobalConfig.ConfigFilename) == 0)) {
            if (StriCmp(TokenList[1], FileName) != 0) {
@@ -634,20 +698,30 @@ VOID ReadConfig(CHAR16 *FileName)
     if ((GlobalConfig.DontScanFiles) && (GlobalConfig.WindowsRecoveryFiles))
        MergeStrings(&(GlobalConfig.DontScanFiles), GlobalConfig.WindowsRecoveryFiles, L',');
     MyFreePool(File.Buffer);
+
+    if (!FileExists(SelfDir, L"icons") && !FileExists(SelfDir, GlobalConfig.IconsDir)) {
+       Print(L"Icons directory doesn't exist; setting textonly = TRUE!\n");
+       GlobalConfig.TextOnly = TRUE;
+    }
 } /* VOID ReadConfig() */
 
-// Finds a volume with the specified Identifier (a volume label or a number
-// followed by a colon, for the moment). If found, sets *Volume to point to
-// that volume. If not, leaves it unchanged.
+// Finds a volume with the specified Identifier (a filesystem label, a
+// partition name, a partition GUID, or a number followed by a colon). If
+// found, sets *Volume to point to that volume. If not, leaves it unchanged.
 // Returns TRUE if a match was found, FALSE if not.
 static BOOLEAN FindVolume(REFIT_VOLUME **Volume, CHAR16 *Identifier) {
-   UINTN     i = 0, CountedVolumes = 0;
+   UINTN     i = 0, CountedVolumes = 0, Length;
    INTN      Number = -1;
-   BOOLEAN   Found = FALSE;
+   BOOLEAN   Found = FALSE, IdIsGuid = FALSE;
+   EFI_GUID  VolGuid, NullGuid = NULL_GUID_VALUE;
 
-   if ((StrLen(Identifier) >= 2) && (Identifier[StrLen(Identifier) - 1] == L':') &&
+   VolGuid = StringAsGuid(Identifier);
+   Length = StrLen(Identifier);
+   if ((Length >= 2) && (Identifier[Length - 1] == L':') &&
        (Identifier[0] >= L'0') && (Identifier[0] <= L'9')) {
       Number = (INTN) Atoi(Identifier);
+   } else if (IsGuid(Identifier)) {
+      IdIsGuid = TRUE;
    }
    while ((i < VolumesCount) && (!Found)) {
       if (Number >= 0) { // User specified a volume by number
@@ -658,11 +732,18 @@ static BOOLEAN FindVolume(REFIT_VOLUME **Volume, CHAR16 *Identifier) {
             }
             CountedVolumes++;
          } // if
-      } else { // User specified a volume by label
-         if (StriCmp(Identifier, Volumes[i]->VolName) == 0) {
+      } else { // User specified a volume by label or GUID
+         if ((StriCmp(Identifier, Volumes[i]->VolName) == 0) ||
+             (StriCmp(Identifier, Volumes[i]->PartName) == 0)) {
             *Volume = Volumes[i];
             Found = TRUE;
          } // if
+         if (IdIsGuid && !Found) {
+            if (GuidsAreEqual(&VolGuid, &(Volumes[i]->PartGuid)) && !GuidsAreEqual(&NullGuid, &(Volumes[i]->PartGuid))) {
+               *Volume = Volumes[i];
+               Found = TRUE;
+            } // if
+         } // if
       } // if/else
       i++;
    } // while()
@@ -693,11 +774,13 @@ static VOID AddSubmenu(LOADER_ENTRY *Entry, REFIT_FILE *File, REFIT_VOLUME *Volu
 
       } else if ((StriCmp(TokenList[0], L"volume") == 0) && (TokenCount > 1)) {
          if (FindVolume(&Volume, TokenList[1])) {
-            MyFreePool(SubEntry->me.Title);
-            SubEntry->me.Title        = AllocateZeroPool(256 * sizeof(CHAR16));
-            SPrint(SubEntry->me.Title, 255, L"Boot %s from %s", (Title != NULL) ? Title : L"Unknown", Volume->VolName);
-            SubEntry->me.BadgeImage   = Volume->VolBadgeImage;
-            SubEntry->VolName         = Volume->VolName;
+            if ((Volume != NULL) && (Volume->IsReadable) && (Volume->RootDir)) {
+               MyFreePool(SubEntry->me.Title);
+               SubEntry->me.Title        = AllocateZeroPool(256 * sizeof(CHAR16));
+               SPrint(SubEntry->me.Title, 255, L"Boot %s from %s", (Title != NULL) ? Title : L"Unknown", Volume->VolName);
+               SubEntry->me.BadgeImage   = Volume->VolBadgeImage;
+               SubEntry->VolName         = Volume->VolName;
+            } // if volume is readable
          } // if match found
 
       } else if (StriCmp(TokenList[0], L"initrd") == 0) {
@@ -774,18 +857,20 @@ static LOADER_ENTRY * AddStanzaEntries(REFIT_FILE *File, REFIT_VOLUME *Volume, C
 
       } else if ((StriCmp(TokenList[0], L"volume") == 0) && (TokenCount > 1)) {
          if (FindVolume(&CurrentVolume, TokenList[1])) {
-            MyFreePool(Entry->me.Title);
-            Entry->me.Title        = AllocateZeroPool(256 * sizeof(CHAR16));
-            SPrint(Entry->me.Title, 255, L"Boot %s from %s", (Title != NULL) ? Title : L"Unknown", CurrentVolume->VolName);
-            Entry->me.BadgeImage   = CurrentVolume->VolBadgeImage;
-            Entry->VolName         = CurrentVolume->VolName;
+            if ((CurrentVolume != NULL) && (CurrentVolume->IsReadable) && (CurrentVolume->RootDir)) {
+               MyFreePool(Entry->me.Title);
+               Entry->me.Title        = AllocateZeroPool(256 * sizeof(CHAR16));
+               SPrint(Entry->me.Title, 255, L"Boot %s from %s", (Title != NULL) ? Title : L"Unknown", CurrentVolume->VolName);
+               Entry->me.BadgeImage   = CurrentVolume->VolBadgeImage;
+               Entry->VolName         = CurrentVolume->VolName;
+            } // if volume is readable
          } // if match found
 
       } else if ((StriCmp(TokenList[0], L"icon") == 0) && (TokenCount > 1)) {
          MyFreePool(Entry->me.Image);
-         Entry->me.Image = egLoadIcon(CurrentVolume->RootDir, TokenList[1], 128);
+         Entry->me.Image = egLoadIcon(CurrentVolume->RootDir, TokenList[1], GlobalConfig.IconSizes[ICON_SIZE_BIG]);
          if (Entry->me.Image == NULL) {
-            Entry->me.Image = DummyImage(128);
+            Entry->me.Image = DummyImage(GlobalConfig.IconSizes[ICON_SIZE_BIG]);
          }
 
       } else if ((StriCmp(TokenList[0], L"initrd") == 0) && (TokenCount > 1)) {
@@ -933,6 +1018,44 @@ static REFIT_FILE * GenerateOptionsFromEtcFstab(REFIT_VOLUME *Volume) {
    return Options;
 } // GenerateOptionsFromEtcFstab()
 
+// Create options from partition type codes. Specifically, if the earlier
+// partition scan found a partition with a type code corresponding to a root
+// filesystem according to the Freedesktop.org Discoverable Partitions Spec
+// (http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/),
+// this function returns an appropriate file with two lines, one with
+// "ro root=PARTUUID={GUID}" and the other with that plus "single".
+// Note that this function returns the LAST partition found with the
+// appropriate type code, so this will work poorly on dual-boot systems or
+// if the type code is set incorrectly.
+static REFIT_FILE * GenerateOptionsFromPartTypes(VOID) {
+    REFIT_FILE   *Options = NULL;
+    CHAR16       *Line, *GuidString;
+
+    if (GlobalConfig.DiscoveredRoot) {
+        Options = AllocateZeroPool(sizeof(REFIT_FILE));
+        if (Options) {
+            Options->Encoding = ENCODING_UTF16_LE;
+            GuidString = GuidAsString(&(GlobalConfig.DiscoveredRoot->PartGuid));
+            ToLower(GuidString);
+            if (GuidString) {
+                Line = PoolPrint(L"\"Boot with normal options\"    \"ro root=/dev/disk/by-partuuid/%s\"\n", GuidString);
+                MergeStrings((CHAR16 **) &(Options->Buffer), Line, 0);
+                MyFreePool(Line);
+                Line = PoolPrint(L"\"Boot into single-user mode\"  \"ro root=/dev/disk/by-partuuid/%s single\"\n", GuidString);
+                MergeStrings((CHAR16**) &(Options->Buffer), Line, 0);
+                MyFreePool(Line);
+                MyFreePool(GuidString);
+            } // if (GuidString)
+            Options->BufferSize = StrLen((CHAR16*) Options->Buffer) * sizeof(CHAR16);
+
+            Options->Current8Ptr  = (CHAR8 *)Options->Buffer;
+            Options->End8Ptr      = Options->Current8Ptr + Options->BufferSize;
+            Options->Current16Ptr = (CHAR16 *)Options->Buffer;
+            Options->End16Ptr     = Options->Current16Ptr + (Options->BufferSize >> 1);
+        } // if (Options allocated OK)
+    } // if (partition has root GUID)
+    return Options;
+} // REFIT_FILE * GenerateOptionsFromPartTypes()
 
 // Read a Linux kernel options file for a Linux boot loader into memory. The LoaderPath
 // and Volume variables identify the location of the options file, but not its name --
@@ -977,12 +1100,17 @@ REFIT_FILE * ReadLinuxOptionsFile(IN CHAR16 *LoaderPath, IN REFIT_VOLUME *Volume
       } else { // a filename string is NULL
          GoOn = FALSE;
       } // if/else
-      if (!FileFound)
-         File = GenerateOptionsFromEtcFstab(Volume);
       MyFreePool(OptionsFilename);
       MyFreePool(FullFilename);
       OptionsFilename = FullFilename = NULL;
    } while (GoOn);
+   if (!FileFound) {
+      // No refind_linux.conf file; look for /etc/fstab and try to pull values from there....
+      File = GenerateOptionsFromEtcFstab(Volume);
+      // If still no joy, try to use Freedesktop.org Discoverable Partitions Spec....
+      if (!File)
+         File = GenerateOptionsFromPartTypes();
+   } // if
    return (File);
 } // static REFIT_FILE * ReadLinuxOptionsFile()