]> code.delx.au - refind/blobdiff - refind/main.c
Added support for "%v" as a variable for the kernel version number in
[refind] / refind / main.c
index ab584f09628b522a93e0e61f011e44d0368f6cc7..eeb7719078fbac792c50d11a71a54bcddb3bff21 100644 (file)
@@ -34,7 +34,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 /*
- * Modifications copyright (c) 2012-2015 Roderick W. Smith
+ * Modifications copyright (c) 2012-2017 Roderick W. Smith
  *
  * Modifications distributed under the terms of the GNU General Public
  * License (GPL) version 3 (GPLv3), or (at your option) any later version.
@@ -64,6 +64,8 @@
 #include "menu.h"
 #include "mok.h"
 #include "gpt.h"
+#include "apple.h"
+#include "mystrings.h"
 #include "security_policy.h"
 #include "driver_support.h"
 #include "../include/Handle.h"
@@ -96,7 +98,6 @@
 #define GDISK_NAMES             L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_x64.efi"
 #define NETBOOT_NAMES           L"\\EFI\\tools\\ipxe.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"
 #define EFI_STUB_ARCH           0x8664
@@ -107,11 +108,20 @@ EFI_GUID gFreedesktopRootGuid = { 0x4f68bce3, 0xe8cd, 0x4db1, { 0x96, 0xe7, 0xfb
 #define GDISK_NAMES             L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_ia32.efi"
 #define NETBOOT_NAMES           L"\\EFI\\tools\\ipxe.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"
 #define EFI_STUB_ARCH           0x014c
 EFI_GUID gFreedesktopRootGuid = { 0x44479540, 0xf297, 0x41b2, { 0x9a, 0xf7, 0xd1, 0x31, 0xd5, 0xf0, 0x45, 0x8a }};
+#elif defined (EFIAARCH64)
+#define SHELL_NAMES             L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellaa64.efi,\\shell.efi,\\shellaa64.efi"
+#define GPTSYNC_NAMES           L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_aa64.efi"
+#define GDISK_NAMES             L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_aa64.efi"
+#define NETBOOT_NAMES           L"\\EFI\\tools\\ipxe.efi"
+#define MEMTEST_NAMES           L"memtest86.efi,memtest86_aa64.efi,memtest86aa64.efi,bootaa64.efi"
+#define FALLBACK_FULLNAME       L"EFI\\BOOT\\bootaa64.efi"
+#define FALLBACK_BASENAME       L"bootaa64.efi"
+#define EFI_STUB_ARCH           0xaa64
+EFI_GUID gFreedesktopRootGuid = { 0xb921b045, 0x1df0, 0x41c3, { 0xaf, 0x44, 0x4c, 0x6f, 0x28, 0x0d, 0x3f, 0xae }};
 #else
 #define SHELL_NAMES             L"\\EFI\\tools\\shell.efi,\\shell.efi"
 #define GPTSYNC_NAMES           L"\\EFI\\tools\\gptsync.efi"
@@ -121,25 +131,18 @@ EFI_GUID gFreedesktopRootGuid = { 0x44479540, 0xf297, 0x41b2, { 0x9a, 0xf7, 0xd1
 #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 */
-// Below is GUID for ARM64
-EFI_GUID gFreedesktopRootGuid = { 0xb921b045, 0x1df0, 0x41c3, { 0xaf, 0x44, 0x4c, 0x6f, 0x28, 0x0d, 0x3f, 0xae }};
+// Below is GUID for ARM32
+EFI_GUID gFreedesktopRootGuid = { 0x69dad710, 0x2ce4, 0x4e3c, { 0xb1, 0x6c, 0x21, 0xa1, 0xd4, 0x9a, 0xbe, 0xd3 }};
 #endif
 #define FAT_ARCH                0x0ef1fab9 /* ID for Apple "fat" binary */
 
 #define IPXE_DISCOVER_NAME      L"\\efi\\tools\\ipxe_discover.efi"
 #define IPXE_NAME               L"\\efi\\tools\\ipxe.efi"
 
-// 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*"
+#define LINUX_MATCH_PATTERNS    L"vmlinuz*,bzImage*,kernel*"
 
 // Maximum length of a text string in certain menus
 #define MAX_LINE_LENGTH 65
@@ -150,17 +153,21 @@ static REFIT_MENU_ENTRY MenuEntryShutdown = { L"Shut Down Computer", TAG_SHUTDOW
 REFIT_MENU_ENTRY MenuEntryReturn   = { L"Return to Main Menu", TAG_RETURN, 1, 0, 0, NULL, NULL, NULL };
 static REFIT_MENU_ENTRY MenuEntryExit     = { L"Exit rEFInd", TAG_EXIT, 1, 0, 0, NULL, NULL, NULL };
 static REFIT_MENU_ENTRY MenuEntryFirmware = { L"Reboot to Computer Setup Utility", TAG_FIRMWARE, 1, 0, 0, NULL, NULL, NULL };
+static REFIT_MENU_ENTRY MenuEntryRotateCsr = { L"Change SIP Policy", TAG_CSR_ROTATE, 1, 0, 0, NULL, NULL, NULL };
 
 REFIT_MENU_SCREEN MainMenu       = { L"Main Menu", NULL, 0, NULL, 0, NULL, 0, L"Automatic boot",
                                      L"Use arrow keys to move cursor; Enter to boot;",
-                                     L"Insert or F2 for more options; Esc to refresh" };
+                                     L"Insert, Tab, or F2 for more options; Esc or Backspace to refresh" };
 static REFIT_MENU_SCREEN AboutMenu      = { L"About", NULL, 0, NULL, 0, NULL, 0, NULL, L"Press Enter to return to main menu", L"" };
 
-REFIT_CONFIG GlobalConfig = { FALSE, TRUE, FALSE, FALSE, TRUE,  0, 0, 0, DONT_CHANGE_TEXT_MODE, 20, 0, 0, GRAPHICS_FOR_OSX, LEGACY_TYPE_MAC,
-                              0, 0, { DEFAULT_BIG_ICON_SIZE / 4, DEFAULT_SMALL_ICON_SIZE, DEFAULT_BIG_ICON_SIZE }, BANNER_NOSCALE,
-                              NULL, NULL, NULL, NULL, CONFIG_FILE_NAME, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                              { TAG_SHELL, TAG_MEMTEST, TAG_GDISK, TAG_APPLE_RECOVERY, TAG_WINDOWS_RECOVERY, TAG_MOK_TOOL,
-                                TAG_ABOUT, TAG_SHUTDOWN, TAG_REBOOT, TAG_FIRMWARE, 0, 0, 0, 0, 0, 0, 0, 0 }
+REFIT_CONFIG GlobalConfig = { FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, 0, 0, 0, DONT_CHANGE_TEXT_MODE,
+                              20, 0, 0, GRAPHICS_FOR_OSX, LEGACY_TYPE_MAC,
+                              0, 0, { DEFAULT_BIG_ICON_SIZE / 4, DEFAULT_SMALL_ICON_SIZE, DEFAULT_BIG_ICON_SIZE },
+                              BANNER_NOSCALE, NULL, NULL, NULL, NULL, CONFIG_FILE_NAME, NULL, NULL, NULL, NULL,
+                              NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                              { TAG_SHELL, TAG_MEMTEST, TAG_GDISK, TAG_APPLE_RECOVERY, TAG_WINDOWS_RECOVERY,
+                                TAG_MOK_TOOL, TAG_ABOUT, TAG_SHUTDOWN, TAG_REBOOT, TAG_FIRMWARE, TAG_FWUPDATE_TOOL,
+                                0, 0, 0, 0, 0, 0, 0, 0 }
                             };
 
 EFI_GUID GlobalGuid = EFI_GLOBAL_VARIABLE;
@@ -182,14 +189,15 @@ struct LOADER_LIST {
 
 static VOID AboutrEFInd(VOID)
 {
-    CHAR16 *FirmwareVendor;
+    CHAR16     *FirmwareVendor;
+    UINT32     CsrStatus;
 
     if (AboutMenu.EntryCount == 0) {
         AboutMenu.TitleImage = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
-        AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.9.2");
+        AddMenuInfoLine(&AboutMenu, PoolPrint(L"rEFInd Version %s", REFIND_VERSION));
         AddMenuInfoLine(&AboutMenu, L"");
         AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2006-2010 Christoph Pfisterer");
-        AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2012-2015 Roderick W. Smith");
+        AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2012-2016 Roderick W. Smith");
         AddMenuInfoLine(&AboutMenu, L"Portions Copyright (c) Intel Corporation and others");
         AddMenuInfoLine(&AboutMenu, L"Distributed under the terms of the GNU GPLv3 license");
         AddMenuInfoLine(&AboutMenu, L"");
@@ -201,9 +209,16 @@ static VOID AboutrEFInd(VOID)
 #elif defined(EFIX64)
         AddMenuInfoLine(&AboutMenu, PoolPrint(L" Platform: x86_64 (64 bit); Secure Boot %s",
                                               secure_mode() ? L"active" : L"inactive"));
+#elif defined(EFIAARCH64)
+        AddMenuInfoLine(&AboutMenu, PoolPrint(L" Platform: ARM (64 bit); Secure Boot %s",
+                                              secure_mode() ? L"active" : L"inactive"));
 #else
         AddMenuInfoLine(&AboutMenu, L" Platform: unknown");
 #endif
+        if (GetCsrStatus(&CsrStatus) == EFI_SUCCESS) {
+            RecordgCsrStatus(CsrStatus, FALSE);
+            AddMenuInfoLine(&AboutMenu, gCsrStatus);
+        }
         FirmwareVendor = StrDuplicate(ST->FirmwareVendor);
         LimitStringLength(FirmwareVendor, MAX_LINE_LENGTH); // More than ~65 causes empty info page on 800x600 display
         AddMenuInfoLine(&AboutMenu, PoolPrint(L" Firmware: %s %d.%02d", FirmwareVendor, ST->FirmwareRevision >> 16,
@@ -248,7 +263,7 @@ static VOID WarnSecureBootError(CHAR16 *Name, BOOLEAN Verbose) {
 // 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)
+#if defined (EFIX64) | defined (EFI32) | defined (EFIAARCH64)
     EFI_STATUS      Status;
     EFI_FILE_HANDLE FileHandle;
     CHAR8           Header[512];
@@ -395,6 +410,12 @@ EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths,
         if (ErrorInStep != NULL)
             *ErrorInStep = 3;
     }
+    if (IsDriver) {
+        // Below should have no effect on most systems, but works
+        // around bug with some EFIs that prevents filesystem drivers
+        // from binding to partitions.
+        ConnectFilesystemDriver(ChildImageHandle);
+    }
 
     // re-open file handles
     ReinitRefitLib();
@@ -409,13 +430,12 @@ bailout:
     return ReturnStatus;
 } /* EFI_STATUS StartEFIImageList() */
 
-static EFI_STATUS StartEFIImage(IN EFI_DEVICE_PATH *DevicePath,
-                                IN CHAR16 *LoadOptions, IN UINTN LoaderType,
-                                IN CHAR16 *ImageTitle, IN CHAR8 OSType,
-                                OUT UINTN *ErrorInStep,
-                                IN BOOLEAN Verbose,
-                                IN BOOLEAN IsDriver
-                               )
+EFI_STATUS StartEFIImage(IN EFI_DEVICE_PATH *DevicePath,
+                         IN CHAR16 *LoadOptions, IN UINTN LoaderType,
+                         IN CHAR16 *ImageTitle, IN CHAR8 OSType,
+                         OUT UINTN *ErrorInStep,
+                         IN BOOLEAN Verbose,
+                         IN BOOLEAN IsDriver)
 {
     EFI_DEVICE_PATH *DevicePaths[2];
 
@@ -472,6 +492,7 @@ VOID StoreLoaderName(IN CHAR16 *Name) {
 // for information on Intel VMX features
 static VOID DoEnableAndLockVMX(VOID)
 {
+#if defined (EFIX64) | defined (EFI32)
     UINT32 msr = 0x3a;
     UINT32 low_bits = 0, high_bits = 0;
 
@@ -484,7 +505,8 @@ static VOID DoEnableAndLockVMX(VOID)
         low_bits = 0x05;
         msr = 0x3a;
         __asm__ volatile ("wrmsr" : : "c" (msr), "a" (low_bits), "d" (high_bits));
-    } 
+    }
+#endif
 } // VOID DoEnableAndLockVMX()
 
 static VOID StartLoader(LOADER_ENTRY *Entry, CHAR16 *SelectionName)
@@ -508,13 +530,16 @@ static VOID StartLoader(LOADER_ENTRY *Entry, CHAR16 *SelectionName)
 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
 // has a file called initramfs-3.3.0.img, this function will return the string
 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
-// initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
-// however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
-// finds.) Thus, care should be taken to avoid placing duplicate matching files in
-// the kernel's directory.
+// initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match.
+// If more than one initrd file matches the extracted version string, the one
+// that matches more characters AFTER (actually, from the start of) the version
+// string is used.
 // If no matching init file can be found, returns NULL.
 static CHAR16 * FindInitrd(IN CHAR16 *LoaderPath, IN REFIT_VOLUME *Volume) {
     CHAR16              *InitrdName = NULL, *FileName, *KernelVersion, *InitrdVersion, *Path;
+    CHAR16              *KernelPostNum, *InitrdPostNum;
+    UINTN               MaxSharedChars, SharedChars;
+    STRING_LIST         *InitrdNames = NULL, *FinalInitrdName = NULL, *CurrentInitrdName = NULL, *MaxSharedInitrd;
     REFIT_DIR_ITER      DirIter;
     EFI_FILE_INFO       *DirEntry;
 
@@ -533,20 +558,44 @@ static CHAR16 * FindInitrd(IN CHAR16 *LoaderPath, IN REFIT_VOLUME *Volume) {
     // building the InitrdName later....
     if ((StrLen(Path) > 0) && (Path[StrLen(Path) - 1] != L'\\'))
         MergeStrings(&Path, L"\\", 0);
-    while ((DirIterNext(&DirIter, 2, L"init*", &DirEntry)) && (InitrdName == NULL)) {
+    while (DirIterNext(&DirIter, 2, L"init*", &DirEntry)) {
         InitrdVersion = FindNumbers(DirEntry->FileName);
-        if (KernelVersion != NULL) {
-            if (MyStriCmp(InitrdVersion, KernelVersion)) {
-                InitrdName = PoolPrint(L"%s%s", Path, DirEntry->FileName);
-            } // if
+        if (((KernelVersion != NULL) && (MyStriCmp(InitrdVersion, KernelVersion))) ||
+            ((KernelVersion == NULL) && (InitrdVersion == NULL))) {
+                CurrentInitrdName = AllocateZeroPool(sizeof(STRING_LIST));
+                if (InitrdNames == NULL)
+                    InitrdNames = FinalInitrdName = CurrentInitrdName;
+                if (CurrentInitrdName) {
+                    CurrentInitrdName->Value = PoolPrint(L"%s%s", Path, DirEntry->FileName);
+                    if (CurrentInitrdName != FinalInitrdName) {
+                        FinalInitrdName->Next = CurrentInitrdName;
+                        FinalInitrdName = CurrentInitrdName;
+                    } // if
+                } // if
+        } // if
+    } // while
+    if (InitrdNames) {
+        if (InitrdNames->Next == NULL) {
+            InitrdName = StrDuplicate(InitrdNames -> Value);
         } else {
-            if (InitrdVersion == NULL) {
-                InitrdName = PoolPrint(L"%s%s", Path, DirEntry->FileName);
-            } // if
+            MaxSharedInitrd = CurrentInitrdName = InitrdNames;
+            MaxSharedChars = 0;
+            while (CurrentInitrdName != NULL) {
+                KernelPostNum = MyStrStr(LoaderPath, KernelVersion);
+                InitrdPostNum = MyStrStr(CurrentInitrdName->Value, KernelVersion);
+                SharedChars = NumCharsInCommon(KernelPostNum, InitrdPostNum);
+                if (SharedChars > MaxSharedChars) {
+                    MaxSharedChars = SharedChars;
+                    MaxSharedInitrd = CurrentInitrdName;
+                } // if
+                // TODO: Compute number of shared characters & compare with max.
+                CurrentInitrdName = CurrentInitrdName->Next;
+            }
+            if (MaxSharedInitrd)
+                InitrdName = StrDuplicate(MaxSharedInitrd->Value);
         } // if/else
-        MyFreePool(InitrdVersion);
-    } // while
-    DirIterClose(&DirIter);
+    } // if
+    DeleteStringList(InitrdNames);
 
     // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
     MyFreePool(KernelVersion);
@@ -708,7 +757,7 @@ REFIT_MENU_SCREEN *InitializeSubScreen(IN LOADER_ENTRY *Entry) {
 VOID GenerateSubScreen(LOADER_ENTRY *Entry, IN REFIT_VOLUME *Volume, IN BOOLEAN GenerateReturn) {
     REFIT_MENU_SCREEN  *SubScreen;
     LOADER_ENTRY       *SubEntry;
-    CHAR16             *InitrdName;
+    CHAR16             *InitrdName, *KernelVersion = NULL;
     CHAR16             DiagsFileName[256];
     REFIT_FILE         *File;
     UINTN              TokenCount;
@@ -806,6 +855,8 @@ VOID GenerateSubScreen(LOADER_ENTRY *Entry, IN REFIT_VOLUME *Volume, IN BOOLEAN
         if (File != NULL) {
             InitrdName =  FindInitrd(Entry->LoaderPath, Volume);
             TokenCount = ReadTokenLine(File, &TokenList);
+            KernelVersion = FindNumbers(Entry->LoaderPath);
+            ReplaceSubstring(&(TokenList[1]), KERNEL_VERSION, KernelVersion);
             // first entry requires special processing, since it was initially set
             // up with a default title but correct options by InitializeSubScreen(),
             // earlier....
@@ -815,6 +866,7 @@ VOID GenerateSubScreen(LOADER_ENTRY *Entry, IN REFIT_VOLUME *Volume, IN BOOLEAN
             } // if
             FreeTokenLine(&TokenList, &TokenCount);
             while ((TokenCount = ReadTokenLine(File, &TokenList)) > 1) {
+                ReplaceSubstring(&(TokenList[1]), KERNEL_VERSION, KernelVersion);
                 SubEntry = InitializeLoaderEntry(Entry);
                 SubEntry->me.Title = TokenList[0] ? StrDuplicate(TokenList[0]) : StrDuplicate(L"Boot Linux");
                 MyFreePool(SubEntry->LoadOptions);
@@ -894,20 +946,24 @@ VOID GenerateSubScreen(LOADER_ENTRY *Entry, IN REFIT_VOLUME *Volume, IN BOOLEAN
     if (GenerateReturn)
         AddMenuEntry(SubScreen, &MenuEntryReturn);
     Entry->me.SubScreen = SubScreen;
+    MyFreePool(KernelVersion);
 } // VOID GenerateSubScreen()
 
 // Returns options for a Linux kernel. Reads them from an options file in the
 // kernel's directory; and if present, adds an initrd= option for an initial
 // RAM disk file with the same version number as the kernel file.
 static CHAR16 * GetMainLinuxOptions(IN CHAR16 * LoaderPath, IN REFIT_VOLUME *Volume) {
-    CHAR16 *Options = NULL, *InitrdName, *FullOptions = NULL;
+    CHAR16 *Options = NULL, *InitrdName, *FullOptions = NULL, *KernelVersion;
 
     Options = GetFirstOptionsFromFile(LoaderPath, Volume);
     InitrdName = FindInitrd(LoaderPath, Volume);
+    KernelVersion = FindNumbers(InitrdName);
+    ReplaceSubstring(&Options, KERNEL_VERSION, KernelVersion);
     FullOptions = AddInitrdToOptions(Options, InitrdName);
 
     MyFreePool(Options);
     MyFreePool(InitrdName);
+    MyFreePool(KernelVersion);
     return (FullOptions);
 } // static CHAR16 * GetMainLinuxOptions()
 
@@ -992,7 +1048,7 @@ VOID SetLoaderDefaults(LOADER_ENTRY *Entry, CHAR16 *LoaderPath, REFIT_VOLUME *Vo
     } // if/else network boot
 
     // detect specific loaders
-    if (StriSubCmp(L"bzImage", NameClues) || StriSubCmp(L"vmlinuz", NameClues)) {
+    if (StriSubCmp(L"bzImage", NameClues) || StriSubCmp(L"vmlinuz", NameClues) || StriSubCmp(L"kernel", NameClues)) {
         if (Volume->DiskKind != DISK_KIND_NET) {
             GuessLinuxDistribution(&OSIconName, Volume, LoaderPath);
             Entry->LoadOptions = GetMainLinuxOptions(LoaderPath, Volume);
@@ -1094,16 +1150,19 @@ static LOADER_ENTRY * AddLoaderEntry(IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTit
 // Add a Linux kernel as a submenu entry for another (pre-existing) Linux kernel entry.
 static VOID AddKernelToSubmenu(LOADER_ENTRY * TargetLoader, CHAR16 *FileName, REFIT_VOLUME *Volume) {
     REFIT_FILE          *File;
-    CHAR16              **TokenList = NULL, *InitrdName, *SubmenuName = NULL, *VolName = NULL, *Path = NULL, *Title;
+    CHAR16              **TokenList = NULL, *InitrdName, *SubmenuName = NULL, *VolName = NULL;
+    CHAR16              *Path = NULL, *Title, *KernelVersion;
     REFIT_MENU_SCREEN   *SubScreen;
     LOADER_ENTRY        *SubEntry;
     UINTN               TokenCount;
 
+    KernelVersion = FindNumbers(FileName);
     File = ReadLinuxOptionsFile(TargetLoader->LoaderPath, Volume);
     if (File != NULL) {
         SubScreen = TargetLoader->me.SubScreen;
         InitrdName = FindInitrd(FileName, Volume);
         while ((TokenCount = ReadTokenLine(File, &TokenList)) > 1) {
+            ReplaceSubstring(&(TokenList[1]), KERNEL_VERSION, KernelVersion);
             SubEntry = InitializeLoaderEntry(TargetLoader);
             SplitPathName(FileName, &VolName, &Path, &SubmenuName);
             MergeStrings(&SubmenuName, L": ", '\0');
@@ -1127,6 +1186,7 @@ static VOID AddKernelToSubmenu(LOADER_ENTRY * TargetLoader, CHAR16 *FileName, RE
         MyFreePool(InitrdName);
         MyFreePool(File);
     } // if
+    MyFreePool(KernelVersion);
 } // static VOID AddKernelToSubmenu()
 
 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
@@ -1150,16 +1210,22 @@ INTN TimeComp(IN EFI_TIME *Time1, IN EFI_TIME *Time2) {
     return 0;
 } // INTN TimeComp()
 
-// Adds a loader list element, keeping it sorted by date. Returns the new
-// first element (the one with the most recent date).
+// Adds a loader list element, keeping it sorted by date. EXCEPTION: Fedora's rescue
+// kernel, which begins with "vmlinuz-0-rescue," should not be at the top of the list,
+// since that will make it the default if kernel folding is enabled, so float it to
+// the end.
+// Returns the new first element (the one with the most recent date).
 static struct LOADER_LIST * AddLoaderListEntry(struct LOADER_LIST *LoaderList, struct LOADER_LIST *NewEntry) {
     struct LOADER_LIST *LatestEntry, *CurrentEntry, *PrevEntry = NULL;
+    BOOLEAN LinuxRescue = FALSE;
 
     LatestEntry = CurrentEntry = LoaderList;
     if (LoaderList == NULL) {
         LatestEntry = NewEntry;
     } else {
-        while ((CurrentEntry != NULL) && (TimeComp(&(NewEntry->TimeStamp), &(CurrentEntry->TimeStamp)) < 0)) {
+        if (StriSubCmp(L"vmlinuz-0-rescue", NewEntry->FileName))
+            LinuxRescue = TRUE;
+        while ((CurrentEntry != NULL) && (LinuxRescue || (TimeComp(&(NewEntry->TimeStamp), &(CurrentEntry->TimeStamp)) < 0))) {
             PrevEntry = CurrentEntry;
             CurrentEntry = CurrentEntry->NextEntry;
         } // while
@@ -1272,9 +1338,7 @@ static BOOLEAN DuplicatesFallback(IN REFIT_VOLUME *Volume, IN CHAR16 *FileName)
         return FALSE;
     }
 
-    if (FallbackSize != FileSize) { // not same size, so can't be identical
-        AreIdentical = FALSE;
-    } else { // could be identical; do full check....
+    if (FallbackSize == FileSize) { // could be identical; do full check....
         FileContents = AllocatePool(FileSize);
         FallbackContents = AllocatePool(FallbackSize);
         if (FileContents && FallbackContents) {
@@ -1305,25 +1369,20 @@ static BOOLEAN DuplicatesFallback(IN REFIT_VOLUME *Volume, IN CHAR16 *FileName)
 // file to fail to open, which would return a false positive -- but as I use
 // this function to exclude symbolic links from the list of boot loaders,
 // that would be fine, since such boot loaders wouldn't work.)
-static BOOLEAN IsSymbolicLink(REFIT_VOLUME *Volume, CHAR16 *Path, EFI_FILE_INFO *DirEntry) {
+// CAUTION: *FullName MUST be properly cleaned up (via CleanUpPathNameSlashes())
+static BOOLEAN IsSymbolicLink(REFIT_VOLUME *Volume, CHAR16 *FullName, EFI_FILE_INFO *DirEntry) {
     EFI_FILE_HANDLE FileHandle;
     EFI_FILE_INFO   *FileInfo = NULL;
     EFI_STATUS      Status;
     UINTN           FileSize2 = 0;
-    CHAR16          *FileName;
 
-    FileName = StrDuplicate(Path);
-    MergeStrings(&FileName, DirEntry->FileName, L'\\');
-    CleanUpPathNameSlashes(FileName);
-
-    Status = refit_call5_wrapper(Volume->RootDir->Open, Volume->RootDir, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
+    Status = refit_call5_wrapper(Volume->RootDir->Open, Volume->RootDir, &FileHandle, FullName, EFI_FILE_MODE_READ, 0);
     if (Status == EFI_SUCCESS) {
         FileInfo = LibFileInfo(FileHandle);
         if (FileInfo != NULL)
             FileSize2 = FileInfo->FileSize;
     }
 
-    MyFreePool(FileName);
     MyFreePool(FileInfo);
 
     return (DirEntry->FileSize != FileSize2);
@@ -1335,15 +1394,14 @@ static BOOLEAN IsSymbolicLink(REFIT_VOLUME *Volume, CHAR16 *Path, EFI_FILE_INFO
 // there's no point in cluttering the display with two kernels that will
 // behave identically on non-SB systems, or when one will fail when SB
 // is active.
-static BOOLEAN HasSignedCounterpart(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 *Filename) {
+// CAUTION: *FullName MUST be properly cleaned up (via CleanUpPathNameSlashes())
+static BOOLEAN HasSignedCounterpart(IN REFIT_VOLUME *Volume, IN CHAR16 *FullName) {
     CHAR16 *NewFile = NULL;
     BOOLEAN retval = FALSE;
 
-    MergeStrings(&NewFile, Path, 0);
-    MergeStrings(&NewFile, Filename, L'\\');
+    MergeStrings(&NewFile, FullName, 0);
     MergeStrings(&NewFile, L".efi.signed", 0);
     if (NewFile != NULL) {
-        CleanUpPathNameSlashes(NewFile);
         if (FileExists(Volume->RootDir, NewFile))
             retval = TRUE;
         MyFreePool(NewFile);
@@ -1362,7 +1420,7 @@ static BOOLEAN ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16
     EFI_STATUS              Status;
     REFIT_DIR_ITER          DirIter;
     EFI_FILE_INFO           *DirEntry;
-    CHAR16                  FileName[256], *Extension;
+    CHAR16                  Message[256], *Extension, *FullName;
     struct LOADER_LIST      *LoaderList = NULL, *NewLoader;
     LOADER_ENTRY            *FirstKernel = NULL, *LatestEntry = NULL;
     BOOLEAN                 FoundFallbackDuplicate = FALSE, IsLinux = FALSE, InSelfPath;
@@ -1374,39 +1432,38 @@ static BOOLEAN ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16
        DirIterOpen(Volume->RootDir, Path, &DirIter);
        while (DirIterNext(&DirIter, 2, Pattern, &DirEntry)) {
           Extension = FindExtension(DirEntry->FileName);
+          FullName = StrDuplicate(Path);
+          MergeStrings(&FullName, DirEntry->FileName, L'\\');
+          CleanUpPathNameSlashes(FullName);
           if (DirEntry->FileName[0] == '.' ||
               MyStriCmp(Extension, L".icns") ||
               MyStriCmp(Extension, L".png") ||
               (MyStriCmp(DirEntry->FileName, FALLBACK_BASENAME) && (MyStriCmp(Path, L"EFI\\BOOT"))) ||
-              StriSubCmp(L"shell", DirEntry->FileName) ||
-              IsSymbolicLink(Volume, Path, DirEntry) || /* is symbolic link */
-              HasSignedCounterpart(Volume, Path, DirEntry->FileName) || /* a file with same name plus ".efi.signed" is present */
-              FilenameIn(Volume, Path, DirEntry->FileName, GlobalConfig.DontScanFiles))
+              FilenameIn(Volume, Path, DirEntry->FileName, SHELL_NAMES) ||
+              IsSymbolicLink(Volume, FullName, DirEntry) || /* is symbolic link */
+              HasSignedCounterpart(Volume, FullName) || /* a file with same name plus ".efi.signed" is present */
+              FilenameIn(Volume, Path, DirEntry->FileName, GlobalConfig.DontScanFiles) ||
+              !IsValidLoader(Volume->RootDir, FullName)) {
                 continue;   // skip this
-
-          if (Path)
-             SPrint(FileName, 255, L"\\%s\\%s", Path, DirEntry->FileName);
-          else
-             SPrint(FileName, 255, L"\\%s", DirEntry->FileName);
-          CleanUpPathNameSlashes(FileName);
-
-          if(!IsValidLoader(Volume->RootDir, FileName))
-             continue;
+          }
 
           NewLoader = AllocateZeroPool(sizeof(struct LOADER_LIST));
           if (NewLoader != NULL) {
-             NewLoader->FileName = StrDuplicate(FileName);
+             NewLoader->FileName = StrDuplicate(FullName);
              NewLoader->TimeStamp = DirEntry->ModificationTime;
              LoaderList = AddLoaderListEntry(LoaderList, NewLoader);
-             if (DuplicatesFallback(Volume, FileName))
+             if (DuplicatesFallback(Volume, FullName))
                 FoundFallbackDuplicate = TRUE;
           } // if
           MyFreePool(Extension);
+          MyFreePool(FullName);
        } // while
 
        NewLoader = LoaderList;
        while (NewLoader != NULL) {
-           IsLinux = (StriSubCmp(L"bzImage", NewLoader->FileName) || StriSubCmp(L"vmlinuz", NewLoader->FileName));
+           IsLinux = (StriSubCmp(L"bzImage", NewLoader->FileName) ||
+                      StriSubCmp(L"vmlinuz", NewLoader->FileName) ||
+                      StriSubCmp(L"kernel", NewLoader->FileName));
            if ((FirstKernel != NULL) && IsLinux && GlobalConfig.FoldLinuxKernels) {
                AddKernelToSubmenu(FirstKernel, NewLoader->FileName, Volume);
            } else {
@@ -1427,10 +1484,10 @@ static BOOLEAN ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16
        // it down to buggy EFI implementations and ignoring that particular error....
        if ((Status != EFI_NOT_FOUND) && (Status != EFI_INVALID_PARAMETER)) {
           if (Path)
-             SPrint(FileName, 255, L"while scanning the %s directory", Path);
+             SPrint(Message, 255, L"while scanning the %s directory", Path);
           else
-             StrCpy(FileName, L"while scanning the root directory");
-          CheckError(Status, FileName);
+             StrCpy(Message, L"while scanning the root directory");
+          CheckError(Status, Message);
        } // if (Status != EFI_NOT_FOUND)
     } // if not scanning a blacklisted directory
 
@@ -1668,149 +1725,6 @@ static LOADER_ENTRY * AddToolEntry(EFI_HANDLE DeviceHandle, IN CHAR16 *LoaderPat
     return Entry;
 } /* static LOADER_ENTRY * AddToolEntry() */
 
-//
-// pre-boot driver functions
-//
-
-static UINTN ScanDriverDir(IN CHAR16 *Path)
-{
-    EFI_STATUS              Status;
-    REFIT_DIR_ITER          DirIter;
-    UINTN                   NumFound = 0;
-    EFI_FILE_INFO           *DirEntry;
-    CHAR16                  FileName[256];
-
-    CleanUpPathNameSlashes(Path);
-    // look through contents of the directory
-    DirIterOpen(SelfRootDir, Path, &DirIter);
-    while (DirIterNext(&DirIter, 2, LOADER_MATCH_PATTERNS, &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"", TYPE_EFI, DirEntry->FileName, 0, NULL, FALSE, TRUE);
-    }
-    Status = DirIterClose(&DirIter);
-    if ((Status != EFI_NOT_FOUND) && (Status != EFI_INVALID_PARAMETER)) {
-        SPrint(FileName, 255, L"while scanning the %s directory", Path);
-        CheckError(Status, FileName);
-    }
-    return (NumFound);
-}
-
-#ifdef __MAKEWITH_GNUEFI
-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;
-
-    Status = LibLocateHandle(AllHandles,
-                             NULL,
-                             NULL,
-                             &AllHandleCount,
-                             &AllHandleBuffer);
-    if (EFI_ERROR(Status))
-        return Status;
-
-    for (Index = 0; Index < AllHandleCount; Index++) {
-        //
-        // Scan the handle database
-        //
-        Status = LibScanHandleDatabase(NULL,
-                                       NULL,
-                                       AllHandleBuffer[Index],
-                                       NULL,
-                                       &HandleCount,
-                                       &HandleBuffer,
-                                       &HandleType);
-        if (EFI_ERROR (Status))
-            goto Done;
-
-        Device = TRUE;
-        if (HandleType[Index] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE)
-            Device = FALSE;
-        if (HandleType[Index] & EFI_HANDLE_TYPE_IMAGE_HANDLE)
-            Device = FALSE;
-
-        if (Device) {
-            Parent = FALSE;
-            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);
-                }
-            }
-        }
-
-        MyFreePool (HandleBuffer);
-        MyFreePool (HandleType);
-    }
-
-Done:
-    MyFreePool (AllHandleBuffer);
-    return Status;
-} /* EFI_STATUS ConnectAllDriversToAllControllers() */
-#else
-static EFI_STATUS ConnectAllDriversToAllControllers(VOID) {
-    BdsLibConnectAllDriversToAllControllers();
-    return 0;
-}
-#endif
-
-// Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
-// directories specified by the user in the "scan_driver_dirs" configuration
-// file line.
-static VOID LoadDrivers(VOID)
-{
-    CHAR16        *Directory, *SelfDirectory;
-    UINTN         i = 0, Length, NumFound = 0;
-
-    // load drivers from the subdirectories of rEFInd's home directory specified
-    // in the DRIVER_DIRS constant.
-    while ((Directory = FindCommaDelimited(DRIVER_DIRS, i++)) != NULL) {
-       SelfDirectory = SelfDirPath ? StrDuplicate(SelfDirPath) : NULL;
-       CleanUpPathNameSlashes(SelfDirectory);
-       MergeStrings(&SelfDirectory, Directory, L'\\');
-       NumFound += ScanDriverDir(SelfDirectory);
-       MyFreePool(Directory);
-       MyFreePool(SelfDirectory);
-    }
-
-    // Scan additional user-specified driver directories....
-    i = 0;
-    while ((Directory = FindCommaDelimited(GlobalConfig.DriverDirs, i++)) != NULL) {
-       CleanUpPathNameSlashes(Directory);
-       Length = StrLen(Directory);
-       if (Length > 0) {
-          NumFound += ScanDriverDir(Directory);
-       } // if
-       MyFreePool(Directory);
-    } // while
-
-    // connect all devices
-    if (NumFound > 0) {
-       ConnectAllDriversToAllControllers();
-    }
-} /* static VOID LoadDrivers() */
-
 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
 static VOID ScanForBootloaders(VOID) {
     UINTN    i;
@@ -1902,6 +1816,7 @@ static VOID ScanForTools(VOID) {
     UINTN i, j, VolumeIndex;
     UINT64 osind;
     CHAR8 *b = 0;
+    UINT32 CsrValue;
 
     MokLocations = StrDuplicate(MOK_LOCATIONS);
     if (MokLocations != NULL)
@@ -2032,6 +1947,18 @@ static VOID ScanForTools(VOID) {
                 FindTool(MokLocations, MOK_NAMES, L"MOK utility", BUILTIN_ICON_TOOL_MOK_TOOL);
                 break;
 
+            case TAG_FWUPDATE_TOOL:
+                FindTool(MokLocations, FWUPDATE_NAMES, L"firmware update utility", BUILTIN_ICON_TOOL_FWUPDATE);
+                break;
+
+            case TAG_CSR_ROTATE:
+                if ((GetCsrStatus(&CsrValue) == EFI_SUCCESS) && (GlobalConfig.CsrValues)) {
+                    TempMenuEntry = CopyMenuEntry(&MenuEntryRotateCsr);
+                    TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_CSR_ROTATE);
+                    AddMenuEntry(&MainMenu, TempMenuEntry);
+                } // if
+                break;
+
             case TAG_MEMTEST:
                 FindTool(MEMTEST_LOCATIONS, MEMTEST_NAMES, L"Memory test utility", BUILTIN_ICON_TOOL_MEMTEST);
                 break;
@@ -2053,9 +1980,10 @@ static VOID RescanAll(BOOLEAN DisplayMessage) {
     FreeList((VOID ***) &(MainMenu.Entries), &MainMenu.EntryCount);
     MainMenu.Entries = NULL;
     MainMenu.EntryCount = 0;
-    ReadConfig(GlobalConfig.ConfigFilename);
     ConnectAllDriversToAllControllers();
     ScanVolumes();
+    ReadConfig(GlobalConfig.ConfigFilename);
+    SetVolumeIcons();
     ScanForBootloaders();
     ScanForTools();
     SetupScreen();
@@ -2168,7 +2096,14 @@ efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
     if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC)
        CopyMem(GlobalConfig.ScanFor, "ihebocm   ", NUM_SCAN_OPTIONS);
     SetConfigFilename(ImageHandle);
+    MokProtocol = SecureBootSetup();
+    LoadDrivers();
+    ScanVolumes(); // Do before ReadConfig() because it needs SelfVolume->VolName
     ReadConfig(GlobalConfig.ConfigFilename);
+    SetVolumeIcons();
+
+    if (GlobalConfig.SpoofOSXVersion && GlobalConfig.SpoofOSXVersion[0] != L'\0')
+        SetAppleOSInfo();
 
     InitScreen();
     WarnIfLegacyProblems();
@@ -2178,9 +2113,6 @@ efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
     refit_call4_wrapper(BS->SetWatchdogTimer, 0x0000, 0x0000, 0x0000, NULL);
 
     // further bootstrap (now with config available)
-    MokProtocol = SecureBootSetup();
-    LoadDrivers();
-    ScanVolumes();
     ScanForBootloaders();
     ScanForTools();
     SetupScreen();
@@ -2257,6 +2189,10 @@ efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
                 RebootIntoFirmware();
                 break;
 
+            case TAG_CSR_ROTATE:
+                RotateCsrValue();
+                break;
+
         } // switch()
     } // while()