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, FALSE, 20, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL,
+REFIT_CONFIG GlobalConfig = { FALSE, FALSE, 0, 0, 20, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
{TAG_SHELL, TAG_ABOUT, TAG_SHUTDOWN, TAG_REBOOT, 0, 0, 0, 0, 0 }};
+// Structure used to hold boot loader filenames and time stamps in
+// a linked list; used to sort entries within a directory.
+struct LOADER_LIST {
+ CHAR16 *FileName;
+ EFI_TIME TimeStamp;
+ struct LOADER_LIST *NextEntry;
+};
+
//
// misc functions
//
{
if (AboutMenu.EntryCount == 0) {
AboutMenu.TitleImage = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
- AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.2.7.2");
+ AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.3.5");
AddMenuInfoLine(&AboutMenu, L"");
AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2006-2010 Christoph Pfisterer");
AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2012 Roderick W. Smith");
while ((DirIterNext(&DirIter, 2, L"init*", &DirEntry)) && (InitrdName == NULL)) {
InitrdVersion = FindNumbers(DirEntry->FileName);
if (KernelVersion != NULL) {
- if (StriCmp(InitrdVersion, KernelVersion) == 0)
- InitrdName = PoolPrint(L"%s%s", Path, DirEntry->FileName);
+ if (StriCmp(InitrdVersion, KernelVersion) == 0)
+ InitrdName = PoolPrint(L"%s%s", Path, DirEntry->FileName);
} else {
if (InitrdVersion == NULL)
InitrdName = PoolPrint(L"%s%s", Path, DirEntry->FileName);
return(Entry);
} // LOADER_ENTRY * AddPreparedLoaderEntry()
+// Creates a copy of a menu screen.
+// Returns a pointer to the copy of the menu screen.
+static REFIT_MENU_SCREEN* CopyMenuScreen(REFIT_MENU_SCREEN *Entry) {
+ REFIT_MENU_SCREEN *NewEntry;
+ UINTN i;
+
+ NewEntry = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
+ if ((Entry != NULL) && (NewEntry != NULL)) {
+ CopyMem(NewEntry, Entry, sizeof(REFIT_MENU_SCREEN));
+ NewEntry->Title = StrDuplicate(Entry->Title);
+ NewEntry->TimeoutText = StrDuplicate(Entry->TimeoutText);
+ if (Entry->TitleImage != NULL) {
+ NewEntry->TitleImage = AllocatePool(sizeof(EG_IMAGE));
+ if (NewEntry->TitleImage != NULL)
+ CopyMem(NewEntry->TitleImage, Entry->TitleImage, sizeof(EG_IMAGE));
+ } // if
+ NewEntry->InfoLines = (CHAR16**) AllocateZeroPool(Entry->InfoLineCount * (sizeof(CHAR16*)));
+ for (i = 0; i < Entry->InfoLineCount && NewEntry->InfoLines; i++) {
+ NewEntry->InfoLines[i] = StrDuplicate(Entry->InfoLines[i]);
+ } // for
+ NewEntry->Entries = (REFIT_MENU_ENTRY**) AllocateZeroPool(Entry->EntryCount * (sizeof (REFIT_MENU_ENTRY*)));
+ for (i = 0; i < Entry->EntryCount && NewEntry->Entries; i++) {
+ AddMenuEntry(NewEntry, Entry->Entries[i]);
+ } // for
+ } // if
+ return (NewEntry);
+} // static REFIT_MENU_SCREEN* CopyMenuScreen()
+
+// Creates a copy of a menu entry. Intended to enable moving a stack-based
+// menu entry (such as the ones for the "reboot" and "exit" functions) to
+// to the heap. This enables easier deletion of the whole set of menu
+// entries when re-scanning.
+// Returns a pointer to the copy of the menu entry.
+static REFIT_MENU_ENTRY* CopyMenuEntry(REFIT_MENU_ENTRY *Entry) {
+ REFIT_MENU_ENTRY *NewEntry;
+
+ NewEntry = AllocateZeroPool(sizeof(REFIT_MENU_ENTRY));
+ if ((Entry != NULL) && (NewEntry != NULL)) {
+ CopyMem(NewEntry, Entry, sizeof(REFIT_MENU_ENTRY));
+ NewEntry->Title = StrDuplicate(Entry->Title);
+ if (Entry->BadgeImage != NULL) {
+ NewEntry->BadgeImage = AllocatePool(sizeof(EG_IMAGE));
+ if (NewEntry->BadgeImage != NULL)
+ CopyMem(NewEntry->BadgeImage, Entry->BadgeImage, sizeof(EG_IMAGE));
+ }
+ if (Entry->Image != NULL) {
+ NewEntry->Image = AllocatePool(sizeof(EG_IMAGE));
+ if (NewEntry->Image != NULL)
+ CopyMem(NewEntry->Image, Entry->Image, sizeof(EG_IMAGE));
+ }
+ if (Entry->SubScreen != NULL) {
+ NewEntry->SubScreen = CopyMenuScreen(Entry->SubScreen);
+// NewEntry->SubScreen = AllocatePool(sizeof(REFIT_MENU_SCREEN));
+// if (NewEntry->SubScreen != NULL)
+// CopyMem(NewEntry->SubScreen, Entry->SubScreen, sizeof(REFIT_MENU_SCREEN));
+ }
+ } // if
+ return (NewEntry);
+} // REFIT_MENU_ENTRY* CopyMenuEntry()
+
// Creates a new LOADER_ENTRY data structure and populates it with
// default values from the specified Entry, or NULL values if Entry
// is unspecified (NULL).
// locate a custom icon for the loader
StrCpy(IconFileName, LoaderPath);
- ReplaceExtension(IconFileName, L".icns");
+ ReplaceEfiExtension(IconFileName, L".icns");
if (FileExists(Volume->RootDir, IconFileName)) {
Entry->me.Image = LoadIcns(Volume->RootDir, IconFileName, 128);
} else if ((StrLen(PathOnly) == 0) && (Volume->VolIconImage != NULL)) {
Entry->OSType = 'E';
if (ShortcutLetter == 0)
ShortcutLetter = 'L';
+ } else if (StriSubCmp(L"grub", FileName)) {
+ Entry->OSType = 'G';
+ ShortcutLetter = 'G';
} else if (StriCmp(FileName, L"cdboot.efi") == 0 ||
StriCmp(FileName, L"bootmgr.efi") == 0 ||
StriCmp(FileName, L"Bootmgfw.efi") == 0) {
CleanUpPathNameSlashes(LoaderPath);
Entry = InitializeLoaderEntry(NULL);
if (Entry != NULL) {
- Entry->Title = StrDuplicate(LoaderTitle);
+ Entry->Title = StrDuplicate((LoaderTitle != NULL) ? LoaderTitle : LoaderPath);
Entry->me.Title = PoolPrint(L"Boot %s from %s", (LoaderTitle != NULL) ? LoaderTitle : LoaderPath, Volume->VolName);
Entry->me.Row = 0;
Entry->me.BadgeImage = Volume->VolBadgeImage;
return(Entry);
} // LOADER_ENTRY * AddLoaderEntry()
+// Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
+// (Time1 == Time2). Precision is only to the nearest second; since
+// this is used for sorting boot loader entries, differences smaller
+// than this are likely to be meaningless (and unlikely!).
+INTN TimeComp(EFI_TIME *Time1, EFI_TIME *Time2) {
+ INT64 Time1InSeconds, Time2InSeconds;
+
+ // Following values are overestimates; I'm assuming 31 days in every month.
+ // This is fine for the purpose of this function, which has a limited
+ // purpose.
+ Time1InSeconds = Time1->Second + (Time1->Minute * 60) + (Time1->Hour * 3600) + (Time1->Day * 86400) +
+ (Time1->Month * 2678400) + ((Time1->Year - 1998) * 32140800);
+ Time2InSeconds = Time2->Second + (Time2->Minute * 60) + (Time2->Hour * 3600) + (Time2->Day * 86400) +
+ (Time2->Month * 2678400) + ((Time2->Year - 1998) * 32140800);
+ if (Time1InSeconds < Time2InSeconds)
+ return (-1);
+ else if (Time1InSeconds > Time2InSeconds)
+ return (1);
+
+ 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).
+static struct LOADER_LIST * AddLoaderListEntry(struct LOADER_LIST *LoaderList, struct LOADER_LIST *NewEntry) {
+ struct LOADER_LIST *LatestEntry, *CurrentEntry, *PrevEntry = NULL;
+
+ LatestEntry = CurrentEntry = LoaderList;
+ if (LoaderList == NULL) {
+ LatestEntry = NewEntry;
+ } else {
+ while ((CurrentEntry != NULL) && (TimeComp(&(NewEntry->TimeStamp), &(CurrentEntry->TimeStamp)) < 0)) {
+ PrevEntry = CurrentEntry;
+ CurrentEntry = CurrentEntry->NextEntry;
+ } // while
+ NewEntry->NextEntry = CurrentEntry;
+ if (PrevEntry == NULL) {
+ LatestEntry = NewEntry;
+ } else {
+ PrevEntry->NextEntry = NewEntry;
+ } // if/else
+ } // if/else
+ return (LatestEntry);
+} // static VOID AddLoaderListEntry()
+
+// Delete the LOADER_LIST linked list
+static VOID CleanUpLoaderList(struct LOADER_LIST *LoaderList) {
+ struct LOADER_LIST *Temp;
+
+ while (LoaderList != NULL) {
+ Temp = LoaderList;
+ LoaderList = LoaderList->NextEntry;
+ FreePool(Temp->FileName);
+ FreePool(Temp);
+ } // while
+} // static VOID CleanUpLoaderList()
+
// Scan an individual directory for EFI boot loader files and, if found,
-// add them to the list.
+// add them to the list. Sorts the entries within the loader directory
+// so that the most recent one appears first in the list.
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];
+ CHAR16 FileName[256], *Extension;
+ struct LOADER_LIST *LoaderList = NULL, *NewLoader;
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, Pattern, &DirEntry)) {
+ Extension = FindExtension(DirEntry->FileName);
if (DirEntry->FileName[0] == '.' ||
StriCmp(DirEntry->FileName, L"TextMode.efi") == 0 ||
StriCmp(DirEntry->FileName, L"ebounce.efi") == 0 ||
StriCmp(DirEntry->FileName, L"GraphicsConsole.efi") == 0 ||
+ StriCmp(Extension, L".icns") == 0 ||
StriSubCmp(L"shell", DirEntry->FileName))
continue; // skip this
SPrint(FileName, 255, L"\\%s\\%s", Path, DirEntry->FileName);
else
SPrint(FileName, 255, L"\\%s", DirEntry->FileName);
- AddLoaderEntry(FileName, NULL, Volume);
- }
+ CleanUpPathNameSlashes(FileName);
+ NewLoader = AllocateZeroPool(sizeof(struct LOADER_LIST));
+ if (NewLoader != NULL) {
+ NewLoader->FileName = StrDuplicate(FileName);
+ NewLoader->TimeStamp = DirEntry->ModificationTime;
+ LoaderList = AddLoaderListEntry(LoaderList, NewLoader);
+ } // if
+ FreePool(Extension);
+ } // while
+ NewLoader = LoaderList;
+ while (NewLoader != NULL) {
+ AddLoaderEntry(NewLoader->FileName, NULL, Volume);
+ NewLoader = NewLoader->NextEntry;
+ } // while
+ CleanUpLoaderList(LoaderList);
Status = DirIterClose(&DirIter);
if (Status != EFI_NOT_FOUND) {
if (Path)
}
// scan the root directory for EFI executables
- ScanLoaderDir(Volume, NULL, MatchPatterns);
+ ScanLoaderDir(Volume, L"\\", MatchPatterns);
// scan subdirectories of the EFI directory (as per the standard)
DirIterOpen(Volume->RootDir, L"EFI", &EfiDirIter);
// Scan user-specified (or additional default) directories....
i = 0;
while ((Directory = FindCommaDelimited(GlobalConfig.AlsoScan, i++)) != NULL) {
+ CleanUpPathNameSlashes(Directory);
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);
Volume->BlockIOOffset == 0 &&
Volume->OSName == NULL)
// this is a whole disk (MBR) entry; hide if we have entries for partitions
- HideIfOthersFound = TRUE;
+ HideIfOthersFound = TRUE;
}
if (HideIfOthersFound) {
// check for other bootable entries on the same disk
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, TRUE);
- FinishExternalScreen();
+ 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, TRUE);
+ FinishExternalScreen();
} /* static VOID StartTool() */
static LOADER_ENTRY * AddToolEntry(IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN EG_IMAGE *Image,
EFI_FILE_INFO *DirEntry;
CHAR16 FileName[256];
+ CleanUpPathNameSlashes(Path);
// look through contents of the directory
DirIterOpen(SelfRootDir, Path, &DirIter);
- while (DirIterNext(&DirIter, 2, L"*.efi", &DirEntry)) {
+ while (DirIterNext(&DirIter, 2, LOADER_MATCH_PATTERNS, &DirEntry)) {
if (DirEntry->FileName[0] == '.')
continue; // skip this
return Status;
} /* EFI_STATUS ConnectAllDriversToAllControllers() */
+// 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;
// load drivers from the "drivers" subdirectory of rEFInd's home directory
Directory = StrDuplicate(SelfDirPath);
+ CleanUpPathNameSlashes(Directory);
MergeStrings(&Directory, L"drivers", L'\\');
NumFound += ScanDriverDir(Directory);
// Scan additional user-specified driver directories....
while ((Directory = FindCommaDelimited(GlobalConfig.DriverDirs, i++)) != NULL) {
+ CleanUpPathNameSlashes(Directory);
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);
// connect all devices
if (NumFound > 0)
ConnectAllDriversToAllControllers();
-}
+} /* static VOID LoadDrivers() */
static VOID ScanForBootloaders(VOID) {
UINTN i;
// reboot, etc.)
static VOID ScanForTools(VOID) {
CHAR16 *FileName = NULL;
+ REFIT_MENU_ENTRY *TempMenuEntry;
UINTN i, j;
for (i = 0; i < NUM_TOOLS; i++) {
switch(GlobalConfig.ShowTools[i]) {
case TAG_SHUTDOWN:
- MenuEntryShutdown.Image = BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN);
- AddMenuEntry(&MainMenu, &MenuEntryShutdown);
+ TempMenuEntry = CopyMenuEntry(&MenuEntryShutdown);
+ TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN);
+ AddMenuEntry(&MainMenu, TempMenuEntry);
break;
case TAG_REBOOT:
- MenuEntryReset.Image = BuiltinIcon(BUILTIN_ICON_FUNC_RESET);
- AddMenuEntry(&MainMenu, &MenuEntryReset);
+ TempMenuEntry = CopyMenuEntry(&MenuEntryReset);
+ TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_RESET);
+ AddMenuEntry(&MainMenu, TempMenuEntry);
break;
case TAG_ABOUT:
- MenuEntryAbout.Image = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
- AddMenuEntry(&MainMenu, &MenuEntryAbout);
+ TempMenuEntry = CopyMenuEntry(&MenuEntryAbout);
+ TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
+ AddMenuEntry(&MainMenu, TempMenuEntry);
break;
case TAG_EXIT:
- MenuEntryExit.Image = BuiltinIcon(BUILTIN_ICON_FUNC_EXIT);
- AddMenuEntry(&MainMenu, &MenuEntryExit);
+ TempMenuEntry = CopyMenuEntry(&MenuEntryExit);
+ TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_EXIT);
+ AddMenuEntry(&MainMenu, TempMenuEntry);
break;
case TAG_SHELL:
j = 0;
while ((FileName = FindCommaDelimited(SHELL_NAMES, j++)) != NULL) {
if (FileExists(SelfRootDir, FileName)) {
- AddToolEntry(FileName, L"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL), 'E', FALSE);
+ AddToolEntry(FileName, L"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL), 'S', FALSE);
}
} // while
break;
EFIAPI
efi_main (IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)
{
- EFI_STATUS Status;
- BOOLEAN MainLoopRunning = TRUE;
- REFIT_MENU_ENTRY *ChosenEntry;
- UINTN MenuExit;
+ EFI_STATUS Status;
+ BOOLEAN MainLoopRunning = TRUE;
+ REFIT_MENU_ENTRY *ChosenEntry;
+ UINTN MenuExit;
+ CHAR16 *Selection;
// bootstrap
InitializeLib(ImageHandle, SystemTable);
ScanForBootloaders();
ScanForTools();
+ Selection = StrDuplicate(GlobalConfig.DefaultSelection);
while (MainLoopRunning) {
- MenuExit = RunMainMenu(&MainMenu, GlobalConfig.DefaultSelection, &ChosenEntry);
+ MenuExit = RunMainMenu(&MainMenu, Selection, &ChosenEntry);
// We don't allow exiting the main menu with the Escape key.
if (MenuExit == MENU_EXIT_ESCAPE) {
- // Commented-out below: Was part of an attempt to get rEFInd to
- // re-scan disk devices on pressing Esc; but doesn't work (yet), so
- // removed....
-// ReadConfig();
-// ScanForBootloaders();
-// SetupScreen();
+ FreeList((VOID ***) &(MainMenu.Entries), &MainMenu.EntryCount);
+ MainMenu.Entries = NULL;
+ MainMenu.EntryCount = 0;
+ ReadConfig();
+ ConnectAllDriversToAllControllers();
+ ScanForBootloaders();
+ ScanForTools();
+ SetupScreen();
continue;
}
return EFI_SUCCESS;
break;
- }
- }
+ } // switch()
+ FreePool(Selection);
+ Selection = StrDuplicate(ChosenEntry->Title);
+ } // while()
// If we end up here, things have gone wrong. Try to reboot, and if that
// fails, go into an endless loop.