X-Git-Url: https://code.delx.au/refind/blobdiff_plain/eb5199b3df642f5a4de81cae543cac838e4c9fef..25fa33d6ede1e5a5dfa7831231e9068a25ccf7d2:/refind/main.c diff --git a/refind/main.c b/refind/main.c index ffbcdb3..f0f10df 100644 --- a/refind/main.c +++ b/refind/main.c @@ -49,32 +49,39 @@ #include "icns.h" #include "menu.h" #include "mok.h" +#include "security_policy.h" #include "../include/Handle.h" #include "../include/refit_call_wrapper.h" #include "driver_support.h" #include "../include/syslinux_mbr.h" -#ifdef __MAKEWITH_TIANO +#ifdef __MAKEWITH_GNUEFI +#define EFI_SECURITY_VIOLATION EFIERR (26) +#else #include "../EfiLib/BdsHelper.h" -#endif // __MAKEWITH_TIANO +#endif // __MAKEWITH_GNUEFI -// +// // 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" +#define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shellx64.efi" #define DRIVER_DIRS L"drivers,drivers_x64" +#define FALLBACK_FULLNAME L"EFI\\BOOT\\bootx64.efi" +#define FALLBACK_BASENAME L"bootx64.efi" #elif defined (EFI32) -#define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shellia32.efi" +#define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shellia32.efi" #define DRIVER_DIRS L"drivers,drivers_ia32" +#define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi" +#define FALLBACK_BASENAME L"bootia32.efi" #else #define SHELL_NAMES L"\\EFI\\tools\\shell.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 */ #endif -#define MOK_NAMES L"\\EFI\\tools\\MokManager.efi,\\EFI\\redhat\\MokManager.efi,\\EFI\\ubuntu\\MokManager.efi,\\EFI\\suse\\MokManager" - // 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 @@ -87,17 +94,24 @@ // a ".efi" extension to be found when scanning for boot loaders. #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*" +// Default hint text for program-launch submenus +#define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;" +#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" + 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 }; static REFIT_MENU_ENTRY MenuEntryReturn = { L"Return to Main Menu", TAG_RETURN, 0, 0, 0, NULL, NULL, NULL }; static REFIT_MENU_ENTRY MenuEntryExit = { L"Exit rEFInd", TAG_EXIT, 1, 0, 0, NULL, NULL, NULL }; -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 }; +static 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" }; +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, FALSE, 0, 0, 20, 0, 0, GRAPHICS_FOR_OSX, LEGACY_TYPE_MAC, 0, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +REFIT_CONFIG GlobalConfig = { FALSE, FALSE, 0, 0, 0, DONT_CHANGE_TEXT_MODE, 20, 0, 0, GRAPHICS_FOR_OSX, LEGACY_TYPE_MAC, 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, 0, 0, 0, 0, 0 }}; // Structure used to hold boot loader filenames and time stamps in @@ -118,7 +132,7 @@ static VOID AboutrEFInd(VOID) if (AboutMenu.EntryCount == 0) { AboutMenu.TitleImage = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT); - AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.5.0.1"); + AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.6.7"); AddMenuInfoLine(&AboutMenu, L""); AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2006-2010 Christoph Pfisterer"); AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2012 Roderick W. Smith"); @@ -160,6 +174,27 @@ static VOID AboutrEFInd(VOID) RunMenu(&AboutMenu, NULL); } /* VOID AboutrEFInd() */ +static VOID WarnSecureBootError(CHAR16 *Name, BOOLEAN Verbose) { + if (Name == NULL) + Name = L"the loader"; + + refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_ERROR); + Print(L"Secure Boot validation failure loading %s!\n", Name); + refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC); + if (Verbose && secure_mode()) { + Print(L"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name); + Print(L"\nYou can:\n * Launch another boot loader\n"); + Print(L" * Disable Secure Boot in your firmware\n"); + Print(L" * Sign %s with a machine owner key (MOK)\n", Name); + Print(L" * Use a MOK utility (often present on the second row) to add a MOK with which\n"); + Print(L" %s has already been signed.\n", Name); + Print(L" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name); + Print(L" signing it.\n"); + Print(L"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n"); + PauseForKey(); + } // if +} // VOID WarnSecureBootError() + // Launch an EFI binary. static EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths, IN CHAR16 *LoadOptions, IN CHAR16 *LoadOptionsPrefix, @@ -170,15 +205,9 @@ static EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths, EFI_STATUS Status, ReturnStatus; EFI_HANDLE ChildImageHandle; EFI_LOADED_IMAGE *ChildLoadedImage = NULL; - REFIT_FILE File; - VOID *ImageData = NULL; - UINTN ImageSize; - REFIT_VOLUME *DeviceVolume = NULL; UINTN DevicePathIndex; CHAR16 ErrorInfo[256]; CHAR16 *FullLoadOptions = NULL; - CHAR16 *loader = NULL; - BOOLEAN UseMok = FALSE; if (ErrorInStep != NULL) *ErrorInStep = 0; @@ -186,7 +215,6 @@ static EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths, // set load options if (LoadOptions != NULL) { if (LoadOptionsPrefix != NULL) { - MergeStrings(&FullLoadOptions, LoadOptionsPrefix, 0); MergeStrings(&FullLoadOptions, LoadOptions, L' '); if (OSType == 'M') { MergeStrings(&FullLoadOptions, L" ", 0); @@ -196,16 +224,20 @@ static EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths, } else { MergeStrings(&FullLoadOptions, LoadOptions, 0); } // if/else - // NOTE: We also include the terminating null in the length for safety. - } // if (LoadOptions != NULL) + } 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" "); + } if (Verbose) Print(L"Starting %s\nUsing load options '%s'\n", ImageTitle, FullLoadOptions); // load the image into memory (and execute it, in the case of a shim/MOK image). ReturnStatus = Status = EFI_NOT_FOUND; // in case the list is empty for (DevicePathIndex = 0; DevicePaths[DevicePathIndex] != NULL; DevicePathIndex++) { - // NOTE: Below commented-out line could be more efficient if the ReadFile() and - // FindVolumeAndFilename() calls were moved earlier, but it doesn't work on my + // 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 // kernel returns a "Failed to handle fs_proto" error message. // TODO: Track down the cause of this error and fix it, if possible. @@ -213,29 +245,14 @@ static EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths, // ImageData, ImageSize, &ChildImageHandle); ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex], NULL, 0, &ChildImageHandle); - if ((Status == EFI_ACCESS_DENIED) && (ShimLoaded())) { - FindVolumeAndFilename(DevicePaths[DevicePathIndex], &DeviceVolume, &loader); - if (DeviceVolume != NULL) { - Status = ReadFile(DeviceVolume->RootDir, loader, &File, &ImageSize); - ImageData = File.Buffer; - } else { - Status = EFI_NOT_FOUND; - Print(L"Error: device volume not found!\n"); - } // if/else - if (Status != EFI_NOT_FOUND) { - ReturnStatus = Status = start_image(SelfImageHandle, loader, ImageData, ImageSize, FullLoadOptions, - DeviceVolume, FileDevicePath(DeviceVolume->DeviceHandle, loader)); -// ReturnStatus = Status = start_image(SelfImageHandle, loader, ImageData, ImageSize, FullLoadOptions, -// DeviceVolume, DevicePaths[DevicePathIndex]); - } - if (ReturnStatus == EFI_SUCCESS) { - UseMok = TRUE; - } // if - } // if (UEFI SB failed; use shim) if (ReturnStatus != EFI_NOT_FOUND) { break; } } + if ((Status == EFI_ACCESS_DENIED) || (Status == EFI_SECURITY_VIOLATION)) { + WarnSecureBootError(ImageTitle, Verbose); + goto bailout; + } SPrint(ErrorInfo, 255, L"while loading %s", ImageTitle); if (CheckError(Status, ErrorInfo)) { if (ErrorInStep != NULL) @@ -243,37 +260,35 @@ static EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths, goto bailout; } - if (!UseMok) { - ReturnStatus = Status = refit_call3_wrapper(BS->HandleProtocol, ChildImageHandle, &LoadedImageProtocol, - (VOID **) &ChildLoadedImage); - if (CheckError(Status, L"while getting a LoadedImageProtocol handle")) { - if (ErrorInStep != NULL) - *ErrorInStep = 2; - goto bailout_unload; - } - ChildLoadedImage->LoadOptions = (VOID *)FullLoadOptions; - ChildLoadedImage->LoadOptionsSize = ((UINT32)StrLen(FullLoadOptions) + 1) * sizeof(CHAR16); - // turn control over to the image - // TODO: (optionally) re-enable the EFI watchdog timer! - - // close open file handles - UninitRefitLib(); - ReturnStatus = Status = refit_call3_wrapper(BS->StartImage, ChildImageHandle, NULL, NULL); - // control returns here when the child image calls Exit() - SPrint(ErrorInfo, 255, L"returned from %s", ImageTitle); - if (CheckError(Status, ErrorInfo)) { - if (ErrorInStep != NULL) - *ErrorInStep = 3; - } + ReturnStatus = Status = refit_call3_wrapper(BS->HandleProtocol, ChildImageHandle, &LoadedImageProtocol, + (VOID **) &ChildLoadedImage); + if (CheckError(Status, L"while getting a LoadedImageProtocol handle")) { + if (ErrorInStep != NULL) + *ErrorInStep = 2; + goto bailout_unload; + } + ChildLoadedImage->LoadOptions = (VOID *)FullLoadOptions; + ChildLoadedImage->LoadOptionsSize = ((UINT32)StrLen(FullLoadOptions) + 1) * sizeof(CHAR16); + // turn control over to the image + // TODO: (optionally) re-enable the EFI watchdog timer! - // re-open file handles - ReinitRefitLib(); - } // if + // close open file handles + UninitRefitLib(); + ReturnStatus = Status = refit_call3_wrapper(BS->StartImage, ChildImageHandle, NULL, NULL); + + // control returns here when the child image calls Exit() + SPrint(ErrorInfo, 255, L"returned from %s", ImageTitle); + if (CheckError(Status, ErrorInfo)) { + if (ErrorInStep != NULL) + *ErrorInStep = 3; + } + + // re-open file handles + ReinitRefitLib(); bailout_unload: // unload the image, we don't care if it works or not... - if (!UseMok) - Status = refit_call1_wrapper(BS->UnloadImage, ChildImageHandle); + Status = refit_call1_wrapper(BS->UnloadImage, ChildImageHandle); bailout: MyFreePool(FullLoadOptions); @@ -391,6 +406,8 @@ static REFIT_MENU_SCREEN* CopyMenuScreen(REFIT_MENU_SCREEN *Entry) { for (i = 0; i < Entry->EntryCount && NewEntry->Entries; i++) { AddMenuEntry(NewEntry, Entry->Entries[i]); } // for + NewEntry->Hint1 = (Entry->Hint1) ? StrDuplicate(Entry->Hint1) : NULL; + NewEntry->Hint2 = (Entry->Hint2) ? StrDuplicate(Entry->Hint2) : NULL; } // if return (NewEntry); } // static REFIT_MENU_SCREEN* CopyMenuScreen() @@ -451,6 +468,23 @@ LOADER_ENTRY *InitializeLoaderEntry(IN LOADER_ENTRY *Entry) { return (NewEntry); } // LOADER_ENTRY *InitializeLoaderEntry() +// Adds InitrdPath to Options, but only if Options doesn't already include an +// initrd= line. Done to enable overriding the default initrd selection in a +// refind_linux.conf file's options list. +// Returns a pointer to a new string. The calling function is responsible for +// freeing its memory. +static CHAR16 *AddInitrdToOptions(CHAR16 *Options, CHAR16 *InitrdPath) { + CHAR16 *NewOptions = NULL; + + if (Options != NULL) + NewOptions = StrDuplicate(Options); + if ((InitrdPath != NULL) && !StriSubCmp(L"initrd=", Options)) { + MergeStrings(&NewOptions, L"initrd=", L' '); + MergeStrings(&NewOptions, InitrdPath, 0); + } + return NewOptions; +} // CHAR16 *AddInitrdToOptions() + // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up // the default entry that launches the boot loader using the same options as the // main Entry does. Subsequent options can be added by the calling function. @@ -459,7 +493,7 @@ LOADER_ENTRY *InitializeLoaderEntry(IN LOADER_ENTRY *Entry) { // Returns a pointer to the new subscreen data structure, or NULL if there // were problems allocating memory. REFIT_MENU_SCREEN *InitializeSubScreen(IN LOADER_ENTRY *Entry) { - CHAR16 *FileName, *Temp = NULL; + CHAR16 *FileName, *MainOptions = NULL; REFIT_MENU_SCREEN *SubScreen = NULL; LOADER_ENTRY *SubEntry; @@ -474,15 +508,18 @@ REFIT_MENU_SCREEN *InitializeSubScreen(IN LOADER_ENTRY *Entry) { // default entry SubEntry = InitializeLoaderEntry(Entry); if (SubEntry != NULL) { - SubEntry->me.Title = L"Boot using default options"; - if ((SubEntry->InitrdPath != NULL) && (StrLen(SubEntry->InitrdPath) > 0) && (!StriSubCmp(L"initrd", SubEntry->LoadOptions))) { - MergeStrings(&Temp, L"initrd=", 0); - MergeStrings(&Temp, SubEntry->InitrdPath, 0); - MergeStrings(&SubEntry->LoadOptions, Temp, L' '); - MyFreePool(Temp); - } // if + SubEntry->me.Title = StrDuplicate(L"Boot using default options"); + MainOptions = SubEntry->LoadOptions; + SubEntry->LoadOptions = AddInitrdToOptions(MainOptions, SubEntry->InitrdPath); + MyFreePool(MainOptions); AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry); } // if (SubEntry != NULL) + SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1); + if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) { + SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR); + } else { + SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2); + } // if/else } // if (SubScreen != NULL) } else { // existing subscreen; less initialization, and just add new entry later.... SubScreen = Entry->me.SubScreen; @@ -493,7 +530,7 @@ REFIT_MENU_SCREEN *InitializeSubScreen(IN LOADER_ENTRY *Entry) { VOID GenerateSubScreen(LOADER_ENTRY *Entry, IN REFIT_VOLUME *Volume) { REFIT_MENU_SCREEN *SubScreen; LOADER_ENTRY *SubEntry; - CHAR16 *InitrdOption = NULL, *Temp; + CHAR16 *InitrdName; CHAR16 DiagsFileName[256]; REFIT_FILE *File; UINTN TokenCount; @@ -560,10 +597,20 @@ VOID GenerateSubScreen(LOADER_ENTRY *Entry, IN REFIT_VOLUME *Volume) { SubEntry->LoadOptions = L"-v -s"; AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry); } // if - } // not single-user + } // single-user mode allowed + + if (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_SAFEMODE)) { + SubEntry = InitializeLoaderEntry(Entry); + if (SubEntry != NULL) { + SubEntry->me.Title = L"Boot Mac OS X in safe mode"; + SubEntry->UseGraphicsMode = FALSE; + SubEntry->LoadOptions = L"-v -x"; + AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry); + } // if + } // safe mode allowed // check for Apple hardware diagnostics - StrCpy(DiagsFileName, L"\\System\\Library\\CoreServices\\.diagnostics\\diags.efi"); + StrCpy(DiagsFileName, L"System\\Library\\CoreServices\\.diagnostics\\diags.efi"); if (FileExists(Volume->RootDir, DiagsFileName) && !(GlobalConfig.HideUIFlags & HIDEUI_FLAG_HWTEST)) { SubEntry = InitializeLoaderEntry(Entry); if (SubEntry != NULL) { @@ -579,24 +626,26 @@ VOID GenerateSubScreen(LOADER_ENTRY *Entry, IN REFIT_VOLUME *Volume) { } else if (Entry->OSType == 'L') { // entries for Linux kernels with EFI stub loaders File = ReadLinuxOptionsFile(Entry->LoaderPath, Volume); if (File != NULL) { - if ((Temp = FindInitrd(Entry->LoaderPath, Volume)) != NULL) { - MergeStrings(&InitrdOption, L"initrd=", 0); - MergeStrings(&InitrdOption, Temp, 0); - } - TokenCount = ReadTokenLine(File, &TokenList); // read and discard first entry, since it's - FreeTokenLine(&TokenList, &TokenCount); // set up by InitializeSubScreen(), earlier.... + InitrdName = FindInitrd(Entry->LoaderPath, Volume); + TokenCount = ReadTokenLine(File, &TokenList); + // first entry requires special processing, since it was initially set + // up with a default title but correct options by InitializeSubScreen(), + // earlier.... + if ((SubScreen->Entries != NULL) && (SubScreen->Entries[0] != NULL)) { + MyFreePool(SubScreen->Entries[0]->Title); + SubScreen->Entries[0]->Title = TokenList[0] ? StrDuplicate(TokenList[0]) : StrDuplicate(L"Boot Linux"); + } // if + FreeTokenLine(&TokenList, &TokenCount); while ((TokenCount = ReadTokenLine(File, &TokenList)) > 1) { SubEntry = InitializeLoaderEntry(Entry); - SubEntry->me.Title = StrDuplicate(TokenList[0]); + SubEntry->me.Title = TokenList[0] ? StrDuplicate(TokenList[0]) : StrDuplicate(L"Boot Linux"); MyFreePool(SubEntry->LoadOptions); - SubEntry->LoadOptions = StrDuplicate(TokenList[1]); - MergeStrings(&SubEntry->LoadOptions, InitrdOption, L' '); + SubEntry->LoadOptions = AddInitrdToOptions(TokenList[1], InitrdName); FreeTokenLine(&TokenList, &TokenCount); SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_LINUX; AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry); } // while - MyFreePool(InitrdOption); - MyFreePool(Temp); + MyFreePool(InitrdName); MyFreePool(File); } // if Linux options file exists @@ -676,40 +725,38 @@ VOID GenerateSubScreen(LOADER_ENTRY *Entry, IN REFIT_VOLUME *Volume) { // 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, *InitrdOption = NULL; + CHAR16 *Options = NULL, *InitrdName, *FullOptions = NULL; Options = GetFirstOptionsFromFile(LoaderPath, Volume); InitrdName = FindInitrd(LoaderPath, Volume); - if (InitrdName != NULL) { - MergeStrings(&InitrdOption, L"initrd=", 0); - MergeStrings(&InitrdOption, InitrdName, 0); - } // if - MergeStrings(&Options, InitrdOption, ' '); - MyFreePool(InitrdOption); + FullOptions = AddInitrdToOptions(Options, InitrdName); + + MyFreePool(Options); MyFreePool(InitrdName); - return (Options); + return (FullOptions); } // static CHAR16 * GetMainLinuxOptions() // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options // that will (with luck) work fairly automatically. -VOID SetLoaderDefaults(LOADER_ENTRY *Entry, CHAR16 *LoaderPath, IN REFIT_VOLUME *Volume) { - CHAR16 IconFileName[256]; - CHAR16 *FileName, *PathOnly, *OSIconName = NULL, *Temp; - CHAR16 ShortcutLetter = 0; +VOID SetLoaderDefaults(LOADER_ENTRY *Entry, CHAR16 *LoaderPath, REFIT_VOLUME *Volume) { + CHAR16 *FileName, *PathOnly, *NoExtension, *OSIconName = NULL, *Temp, *SubString; + CHAR16 ShortcutLetter = 0; + UINTN i = 0, Length; FileName = Basename(LoaderPath); PathOnly = FindPath(LoaderPath); + NoExtension = StripEfiExtension(FileName); // locate a custom icon for the loader - StrCpy(IconFileName, LoaderPath); - 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)) { + // Anything found here takes precedence over the "hints" in the OSIconName variable + if (!Entry->me.Image) + Entry->me.Image = egFindIcon(NoExtension, 128); + if (!Entry->me.Image) Entry->me.Image = Volume->VolIconImage; - } // icon matched to loader or volume + // Begin creating icon "hints" by using last part of directory path leading + // to the loader Temp = FindLastDirName(LoaderPath); MergeStrings(&OSIconName, Temp, L','); MyFreePool(Temp); @@ -718,6 +765,26 @@ VOID SetLoaderDefaults(LOADER_ENTRY *Entry, CHAR16 *LoaderPath, IN REFIT_VOLUME ShortcutLetter = OSIconName[0]; } + // Add every "word" in the volume label, delimited by spaces, dashes (-), or + // underscores (_), to the list of hints to be used in searching for OS + // icons. + if ((Volume->VolName) && (StrLen(Volume->VolName) > 0)) { + Temp = SubString = StrDuplicate(Volume->VolName); + if (Temp != NULL) { + Length = StrLen(Temp); + for (i = 0; i < Length; i++) { + if ((Temp[i] == L' ') || (Temp[i] == L'_') || (Temp[i] == L'-')) { + Temp[i] = 0; + if (StrLen(SubString) > 0) + MergeStrings(&OSIconName, SubString, L','); + SubString = Temp + i + 1; + } // if + } // for + MergeStrings(&OSIconName, SubString, L','); + MyFreePool(Temp); + } // if + } // if + // detect specific loaders if (StriSubCmp(L"bzImage", LoaderPath) || StriSubCmp(L"vmlinuz", LoaderPath)) { MergeStrings(&OSIconName, L"linux", L','); @@ -743,12 +810,6 @@ VOID SetLoaderDefaults(LOADER_ENTRY *Entry, CHAR16 *LoaderPath, IN REFIT_VOLUME } else if (StriCmp(FileName, L"e.efi") == 0 || StriCmp(FileName, L"elilo.efi") == 0 || StriSubCmp(L"elilo", FileName)) { MergeStrings(&OSIconName, L"elilo,linux", L','); Entry->OSType = 'E'; - if (secure_mode()) { // hack to enable ELILO to boot in secure mode - Temp = StrDuplicate(L"-C "); - MergeStrings(&Temp, PathOnly, 0); - MergeStrings(&Temp, L"elilo.conf", L'\\'); - Entry->LoadOptions = Temp; - } if (ShortcutLetter == 0) ShortcutLetter = 'L'; Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO; @@ -758,7 +819,7 @@ VOID SetLoaderDefaults(LOADER_ENTRY *Entry, CHAR16 *LoaderPath, IN REFIT_VOLUME Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_GRUB; } else if (StriCmp(FileName, L"cdboot.efi") == 0 || StriCmp(FileName, L"bootmgr.efi") == 0 || - StriCmp(FileName, L"Bootmgfw.efi") == 0) { + StriCmp(FileName, L"bootmgfw.efi") == 0) { MergeStrings(&OSIconName, L"win", L','); Entry->OSType = 'W'; ShortcutLetter = 'W'; @@ -812,12 +873,11 @@ LOADER_ENTRY * AddLoaderEntry(IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN // (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) { +INTN TimeComp(IN EFI_TIME *Time1, IN 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. + // This is fine for the purpose of this function, which is limited 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) + @@ -865,39 +925,142 @@ static VOID CleanUpLoaderList(struct LOADER_LIST *LoaderList) { } // while } // static VOID CleanUpLoaderList() +// Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs +// or GlobalConfig.DontScanVolumes specification, or if Path points to a volume +// other than the one specified by Volume, or if the specified path is SelfDir. +// Returns TRUE if none of these conditions is met -- that is, if the path is +// eligible for scanning. +static BOOLEAN ShouldScan(REFIT_VOLUME *Volume, CHAR16 *Path) { + CHAR16 *VolName = NULL, *DontScanDir; + UINTN i = 0, VolNum; + BOOLEAN ScanIt = TRUE; + + if (IsIn(Volume->VolName, GlobalConfig.DontScanVolumes)) + return FALSE; + + if ((StriCmp(Path, SelfDirPath) == 0) && (Volume->DeviceHandle == SelfVolume->DeviceHandle)) + return FALSE; + + while ((DontScanDir = FindCommaDelimited(GlobalConfig.DontScanDirs, i++)) && ScanIt) { + SplitVolumeAndFilename(&DontScanDir, &VolName); + CleanUpPathNameSlashes(DontScanDir); + if (VolName != NULL) { + if ((StriCmp(VolName, Volume->VolName) == 0) && (StriCmp(DontScanDir, Path) == 0)) + ScanIt = FALSE; + if ((StrLen(VolName) > 2) && (VolName[0] == L'f') && (VolName[1] == L's') && (VolName[2] >= L'0') && (VolName[2] <= L'9')) { + VolNum = Atoi(VolName + 2); + if ((VolNum == Volume->VolNumber) && (StriCmp(DontScanDir, Path) == 0)) + ScanIt = FALSE; + } + } else { + if (StriCmp(DontScanDir, Path) == 0) + ScanIt = FALSE; + } + MyFreePool(DontScanDir); + DontScanDir = NULL; + } + return ScanIt; +} // BOOLEAN ShouldScan() + +// Returns TRUE if the file is byte-for-byte identical with the fallback file +// on the volume AND if the file is not itself the fallback file; returns +// FALSE if the file is not identical to the fallback file OR if the file +// IS the fallback file. Intended for use in excluding the fallback boot +// loader when it's a duplicate of another boot loader. +static BOOLEAN DuplicatesFallback(IN REFIT_VOLUME *Volume, IN CHAR16 *FileName) { + CHAR8 *FileContents, *FallbackContents; + EFI_FILE_HANDLE FileHandle, FallbackHandle; + EFI_FILE_INFO *FileInfo, *FallbackInfo; + UINTN FileSize = 0, FallbackSize = 0; + EFI_STATUS Status; + BOOLEAN AreIdentical = FALSE; + + CleanUpPathNameSlashes(FileName); + + if (StriCmp(FileName, FALLBACK_FULLNAME) == 0) + return FALSE; // identical filenames, so not a duplicate.... + + Status = refit_call5_wrapper(Volume->RootDir->Open, Volume->RootDir, &FileHandle, FileName, EFI_FILE_MODE_READ, 0); + if (Status == EFI_SUCCESS) { + FileInfo = LibFileInfo(FileHandle); + FileSize = FileInfo->FileSize; + } else { + return FALSE; + } + + Status = refit_call5_wrapper(Volume->RootDir->Open, Volume->RootDir, &FallbackHandle, FALLBACK_FULLNAME, EFI_FILE_MODE_READ, 0); + if (Status == EFI_SUCCESS) { + FallbackInfo = LibFileInfo(FallbackHandle); + FallbackSize = FallbackInfo->FileSize; + } else { + refit_call1_wrapper(FileHandle->Close, FileHandle); + return FALSE; + } + + if (FallbackSize != FileSize) { // not same size, so can't be identical + AreIdentical = FALSE; + } else { // could be identical; do full check.... + FileContents = AllocatePool(FileSize); + FallbackContents = AllocatePool(FallbackSize); + if (FileContents && FallbackContents) { + Status = refit_call3_wrapper(FileHandle->Read, FileHandle, &FileSize, FileContents); + if (Status == EFI_SUCCESS) + Status = refit_call3_wrapper(FallbackHandle->Read, FallbackHandle, &FallbackSize, FallbackContents); + if (Status == EFI_SUCCESS) { + AreIdentical = (CompareMem(FileContents, FallbackContents, FileSize) == 0); + } // if + } // if + MyFreePool(FileContents); + MyFreePool(FallbackContents); + } // if/else + + refit_call1_wrapper(FileHandle->Close, FileHandle); + refit_call1_wrapper(FileHandle->Close, FallbackHandle); + return AreIdentical; + +} // BOOLEAN DuplicatesFallback() + // Scan an individual directory for EFI boot loader files and, if found, -// 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) +// add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked +// up in ScanEfiFiles(). Sorts the entries within the loader directory so that +// the most recent one appears first in the list. +// Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not. +static BOOLEAN 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], *Extension; struct LOADER_LIST *LoaderList = NULL, *NewLoader; + BOOLEAN FoundFallbackDuplicate = FALSE; - if ((!SelfDirPath || !Path || ((StriCmp(Path, SelfDirPath) == 0) && Volume->DeviceHandle != SelfVolume->DeviceHandle) || - (StriCmp(Path, SelfDirPath) != 0)) && (!IsIn(Path, GlobalConfig.DontScanDirs))) { + if ((!SelfDirPath || !Path || ((StriCmp(Path, SelfDirPath) == 0) && (Volume->DeviceHandle != SelfVolume->DeviceHandle)) || + (StriCmp(Path, SelfDirPath) != 0)) && + (ShouldScan(Volume, Path))) { // 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(Extension, L".icns") == 0 || + StriCmp(Extension, L".png") == 0 || + (StriCmp(DirEntry->FileName, FALLBACK_BASENAME) == 0 && (StriCmp(Path, L"EFI\\BOOT") == 0)) || StriSubCmp(L"shell", DirEntry->FileName) || IsIn(DirEntry->FileName, GlobalConfig.DontScanFiles)) continue; // skip this if (Path) - SPrint(FileName, 255, L"\\%s\\%s", Path, DirEntry->FileName); + SPrint(FileName, 255, L"\\%s\\%s", Path, DirEntry->FileName); else - SPrint(FileName, 255, L"\\%s", DirEntry->FileName); + SPrint(FileName, 255, L"\\%s", DirEntry->FileName); CleanUpPathNameSlashes(FileName); NewLoader = AllocateZeroPool(sizeof(struct LOADER_LIST)); if (NewLoader != NULL) { NewLoader->FileName = StrDuplicate(FileName); NewLoader->TimeStamp = DirEntry->ModificationTime; LoaderList = AddLoaderListEntry(LoaderList, NewLoader); + if (DuplicatesFallback(Volume, FileName)) + FoundFallbackDuplicate = TRUE; } // if MyFreePool(Extension); } // while @@ -916,51 +1079,64 @@ static VOID ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 *P CheckError(Status, FileName); } // if (Status != EFI_NOT_FOUND) } // if not scanning our own directory + + return FoundFallbackDuplicate; } /* static VOID ScanLoaderDir() */ static VOID ScanEfiFiles(REFIT_VOLUME *Volume) { EFI_STATUS Status; REFIT_DIR_ITER EfiDirIter; EFI_FILE_INFO *EfiDirEntry; - CHAR16 FileName[256], *Directory, *MatchPatterns; + CHAR16 FileName[256], *Directory, *MatchPatterns, *VolName = NULL; UINTN i, Length; + BOOLEAN ScanFallbackLoader = TRUE; +// Print(L"Entering ScanEfiFiles(), GlobalConfig.ScanAllLinux = %s\n", GlobalConfig.ScanAllLinux ? L"TRUE" : L"FALSE"); MatchPatterns = StrDuplicate(LOADER_MATCH_PATTERNS); if (GlobalConfig.ScanAllLinux) MergeStrings(&MatchPatterns, LINUX_MATCH_PATTERNS, L','); +// Print(L"MatchPatterns = '%s'\n", MatchPatterns); if ((Volume->RootDir != NULL) && (Volume->VolName != NULL)) { // check for Mac OS X boot loader - if (!IsIn(L"\\System\\Library\\CoreServices", GlobalConfig.DontScanDirs)) { + if (ShouldScan(Volume, L"System\\Library\\CoreServices")) { StrCpy(FileName, MACOSX_LOADER_PATH); if (FileExists(Volume->RootDir, FileName) && !IsIn(L"boot.efi", GlobalConfig.DontScanFiles)) { AddLoaderEntry(FileName, L"Mac OS X", Volume); + if (DuplicatesFallback(Volume, FileName)) + ScanFallbackLoader = FALSE; } // check for XOM - StrCpy(FileName, L"\\System\\Library\\CoreServices\\xom.efi"); + StrCpy(FileName, L"System\\Library\\CoreServices\\xom.efi"); if (FileExists(Volume->RootDir, FileName) && !IsIn(L"boot.efi", GlobalConfig.DontScanFiles)) { AddLoaderEntry(FileName, L"Windows XP (XoM)", Volume); + if (DuplicatesFallback(Volume, FileName)) + ScanFallbackLoader = FALSE; } - } // if Mac directory not in GlobalConfig.DontScanDirs list + } // if should scan Mac directory // check for Microsoft boot loader/menu - StrCpy(FileName, L"\\EFI\\Microsoft\\Boot\\Bootmgfw.efi"); - if (FileExists(Volume->RootDir, FileName) && !IsIn(L"\\EFI\\Microsoft\\Boot", GlobalConfig.DontScanDirs) && + StrCpy(FileName, L"EFI\\Microsoft\\Boot\\Bootmgfw.efi"); + if (FileExists(Volume->RootDir, FileName) && ShouldScan(Volume, L"EFI\\Microsoft\\Boot") && !IsIn(L"bootmgfw.efi", GlobalConfig.DontScanFiles)) { AddLoaderEntry(FileName, L"Microsoft EFI boot", Volume); + if (DuplicatesFallback(Volume, FileName)) + ScanFallbackLoader = FALSE; } // scan the root directory for EFI executables - ScanLoaderDir(Volume, L"\\", MatchPatterns); + if (ScanLoaderDir(Volume, L"\\", MatchPatterns)) + ScanFallbackLoader = FALSE; // 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] == '.') - continue; // skip this, doesn't contain boot loaders - SPrint(FileName, 255, L"\\EFI\\%s", EfiDirEntry->FileName); - ScanLoaderDir(Volume, FileName, MatchPatterns); + 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)) + ScanFallbackLoader = FALSE; } // while() Status = DirIterClose(&EfiDirIter); if (Status != EFI_NOT_FOUND) @@ -969,12 +1145,20 @@ static VOID ScanEfiFiles(REFIT_VOLUME *Volume) { // Scan user-specified (or additional default) directories.... i = 0; while ((Directory = FindCommaDelimited(GlobalConfig.AlsoScan, i++)) != NULL) { + SplitVolumeAndFilename(&Directory, &VolName); CleanUpPathNameSlashes(Directory); Length = StrLen(Directory); - if (Length > 0) - ScanLoaderDir(Volume, Directory, MatchPatterns); + if ((Length > 0) && ScanLoaderDir(Volume, Directory, MatchPatterns)) + ScanFallbackLoader = FALSE; MyFreePool(Directory); + MyFreePool(VolName); } // while + + // If not a duplicate & if it exists & if it's not us, create an entry + // for the fallback boot loader + if (ScanFallbackLoader && FileExists(Volume->RootDir, FALLBACK_FULLNAME) && ShouldScan(Volume, L"EFI\\BOOT")) { + AddLoaderEntry(FALLBACK_FULLNAME, L"Fallback boot loader", Volume); + } } // if } // static VOID ScanEfiFiles() @@ -1258,6 +1442,12 @@ static LEGACY_ENTRY * AddLegacyEntry(IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Vo SubScreen->Title = AllocateZeroPool(256 * sizeof(CHAR16)); SPrint(SubScreen->Title, 255, L"Boot Options for %s on %s", LoaderTitle, VolDesc); SubScreen->TitleImage = Entry->me.Image; + SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1); + if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) { + SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR); + } else { + SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2); + } // if/else // default entry SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY)); @@ -1275,7 +1465,9 @@ static LEGACY_ENTRY * AddLegacyEntry(IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Vo } /* static LEGACY_ENTRY * AddLegacyEntry() */ -#ifdef __MAKEWITH_TIANO +#ifdef __MAKEWITH_GNUEFI +static VOID ScanLegacyUEFI(IN UINTN DiskType){} +#else // default volume badge icon based on disk kind static EG_IMAGE * GetDiskBadge(IN UINTN DiskType) { EG_IMAGE * Badge = NULL; @@ -1304,8 +1496,6 @@ static LEGACY_ENTRY * AddLegacyEntryUEFI(BDS_COMMON_OPTION *BdsOption, IN UINT16 CHAR16 ShortcutLetter = 0; CHAR16 *LegacyDescription = BdsOption->Description; -// ScanVolume(Volume); - // prepare the menu entry Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY)); Entry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16)); @@ -1317,7 +1507,6 @@ static LEGACY_ENTRY * AddLegacyEntryUEFI(BDS_COMMON_OPTION *BdsOption, IN UINT16 Entry->LoadOptions = (DiskType == BBS_CDROM) ? L"CD" : ((DiskType == BBS_USB) ? L"USB" : L"HD"); Entry->me.BadgeImage = GetDiskBadge(DiskType); -// Entry->me.BadgeImage = Volume->VolBadgeImage; Entry->BdsOption = BdsOption; Entry->Enabled = TRUE; @@ -1326,6 +1515,12 @@ static LEGACY_ENTRY * AddLegacyEntryUEFI(BDS_COMMON_OPTION *BdsOption, IN UINT16 SubScreen->Title = AllocateZeroPool(256 * sizeof(CHAR16)); SPrint(SubScreen->Title, 255, L"No boot options for legacy target"); SubScreen->TitleImage = Entry->me.Image; + SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1); + if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) { + SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR); + } else { + SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2); + } // if/else // default entry SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY)); @@ -1359,7 +1554,6 @@ static VOID ScanLegacyUEFI(IN UINTN DiskType) BDS_COMMON_OPTION *BdsOption; LIST_ENTRY TempList; BBS_BBS_DEVICE_PATH * BbsDevicePath = NULL; -// REFIT_VOLUME Volume; InitializeListHead (&TempList); ZeroMem (Buffer, sizeof (Buffer)); @@ -1399,9 +1593,7 @@ static VOID ScanLegacyUEFI(IN UINTN DiskType) Index++; } } /* static VOID ScanLegacyUEFI() */ -#else -static VOID ScanLegacyUEFI(IN UINTN DiskType){} -#endif // __MAKEWITH_TIANO +#endif // __MAKEWITH_GNUEFI static VOID ScanLegacyVolume(REFIT_VOLUME *Volume, UINTN VolumeIndex) { UINTN VolumeIndex2; @@ -1424,7 +1616,7 @@ static VOID ScanLegacyVolume(REFIT_VOLUME *Volume, UINTN VolumeIndex) { // check for other bootable entries on the same disk for (VolumeIndex2 = 0; VolumeIndex2 < VolumesCount; VolumeIndex2++) { if (VolumeIndex2 != VolumeIndex && Volumes[VolumeIndex2]->HasBootCode && - Volumes[VolumeIndex2]->WholeDiskBlockIO == Volume->WholeDiskBlockIO) + Volumes[VolumeIndex2]->WholeDiskBlockIO == Volume->WholeDiskBlockIO) ShowVolume = FALSE; } } @@ -1674,7 +1866,7 @@ static VOID FindLegacyBootType(VOID) { GlobalConfig.LegacyType = LEGACY_TYPE_NONE; // UEFI-style legacy BIOS support is available only with the TianoCore EDK2 - // build environment, and then only with some implementations.... + // build environment, and then only with some EFI implementations.... #ifdef __MAKEWITH_TIANO Status = gBS->LocateProtocol (&gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios); if (!EFI_ERROR (Status)) @@ -1738,7 +1930,7 @@ static VOID ScanForBootloaders(VOID) { ScanLegacyExternal(); break; case 'm': case 'M': - ScanUserConfigured(); + ScanUserConfigured(CONFIG_FILE_NAME); break; case 'e': case 'E': ScanExternal(); @@ -1763,12 +1955,17 @@ 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, Description[256]; + CHAR16 *FileName = NULL, *MokLocations, *MokName, *PathName, Description[256]; REFIT_MENU_ENTRY *TempMenuEntry; - UINTN i, j, VolumeIndex; + UINTN i, j, k, VolumeIndex; + + MokLocations = StrDuplicate(MOK_LOCATIONS); + if (MokLocations != NULL) + MergeStrings(&MokLocations, SelfDirPath, L','); for (i = 0; i < NUM_TOOLS; i++) { switch(GlobalConfig.ShowTools[i]) { + // NOTE: Be sure that FileName is NULL at the end of each case. case TAG_SHUTDOWN: TempMenuEntry = CopyMenuEntry(&MenuEntryShutdown); TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN); @@ -1791,26 +1988,24 @@ static VOID ScanForTools(VOID) { break; case TAG_SHELL: j = 0; - MyFreePool(FileName); while ((FileName = FindCommaDelimited(SHELL_NAMES, j++)) != NULL) { if (FileExists(SelfRootDir, FileName)) { AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL), 'S', FALSE); } + MyFreePool(FileName); } // while break; case TAG_GPTSYNC: - MyFreePool(FileName); - FileName = NULL; - MergeStrings(&FileName, L"\\efi\\tools\\gptsync.efi", 0); + FileName = StrDuplicate(L"\\efi\\tools\\gptsync.efi"); if (FileExists(SelfRootDir, FileName)) { AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART), 'P', FALSE); } - break; - case TAG_APPLE_RECOVERY: MyFreePool(FileName); FileName = NULL; - MergeStrings(&FileName, L"\\com.apple.recovery.boot\\boot.efi", 0); + break; + case TAG_APPLE_RECOVERY: + FileName = StrDuplicate(L"\\com.apple.recovery.boot\\boot.efi"); for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) { if ((Volumes[VolumeIndex]->RootDir != NULL) && (FileExists(Volumes[VolumeIndex]->RootDir, FileName))) { SPrint(Description, 255, L"Apple Recovery on %s", Volumes[VolumeIndex]->VolName); @@ -1818,29 +2013,31 @@ static VOID ScanForTools(VOID) { BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE), 'R', TRUE); } } // for + MyFreePool(FileName); + FileName = NULL; break; case TAG_MOK_TOOL: j = 0; - MyFreePool(FileName); - while ((FileName = FindCommaDelimited(MOK_NAMES, j++)) != NULL) { - if (FileExists(SelfRootDir, FileName)) { - SPrint(Description, 255, L"MOK Key Manager at %s", FileName); - AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, Description, - BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL), 'S', FALSE); - } - } // while - if (FileExists(SelfDir, L"MokManager.efi")) { + while ((FileName = FindCommaDelimited(MokLocations, j++)) != NULL) { + k = 0; + while ((MokName = FindCommaDelimited(MOK_NAMES, k++)) != NULL) { + PathName = StrDuplicate(FileName); + MergeStrings(&PathName, MokName, (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"MOK utility at %s on %s", PathName, Volumes[VolumeIndex]->VolName); + AddToolEntry(Volumes[VolumeIndex]->DeviceHandle, PathName, Description, + BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL), 'S', FALSE); + } // if + } // for + MyFreePool(PathName); + MyFreePool(MokName); + } // while MOK_NAMES MyFreePool(FileName); - FileName = StrDuplicate(SelfDirPath); - MergeStrings(&FileName, L"\\MokManager.efi", 0); - SPrint(Description, 255, L"MOK Key Manager at %s", FileName); - AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, Description, - BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL), 'S', FALSE); - } + } // while MokLocations + break; } // switch() - MyFreePool(FileName); - FileName = NULL; } // for } // static VOID ScanForTools @@ -1856,7 +2053,7 @@ VOID RescanAll(VOID) { FreeList((VOID ***) &(MainMenu.Entries), &MainMenu.EntryCount); MainMenu.Entries = NULL; MainMenu.EntryCount = 0; - ReadConfig(); + ReadConfig(CONFIG_FILE_NAME); ConnectAllDriversToAllControllers(); ScanVolumes(); ScanForBootloaders(); @@ -1864,7 +2061,7 @@ VOID RescanAll(VOID) { SetupScreen(); } // VOID RescanAll() -#ifndef __MAKEWITH_GNUEFI +#ifdef __MAKEWITH_TIANO // Minimal initialization function static VOID InitializeLib(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) { @@ -1880,23 +2077,59 @@ static VOID InitializeLib(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *System #endif +// Set up our own Secure Boot extensions.... +// Returns TRUE on success, FALSE otherwise +static BOOLEAN SecureBootSetup(VOID) { + EFI_STATUS Status; + BOOLEAN Success = FALSE; + + if (secure_mode() && ShimLoaded()) { + Status = security_policy_install(); + if (Status == EFI_SUCCESS) { + Success = TRUE; + } else { + Print(L"Failed to install MOK Secure Boot extensions"); + } + } + return Success; +} // VOID SecureBootSetup() + +// Remove our own Secure Boot extensions.... +// Returns TRUE on success, FALSE otherwise +static BOOLEAN SecureBootUninstall(VOID) { + EFI_STATUS Status; + BOOLEAN Success = TRUE; + + if (secure_mode()) { + Status = security_policy_uninstall(); + if (Status != EFI_SUCCESS) { + Success = FALSE; + BeginTextScreen(L"Secure Boot Policy Failure"); + Print(L"Failed to uninstall MOK Secure Boot extensions; forcing a reboot."); + PauseForKey(); + refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL); + } + } + return Success; +} // VOID SecureBootUninstall + // // main entry point // EFI_STATUS EFIAPI -efi_main (IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) +efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) { EFI_STATUS Status; BOOLEAN MainLoopRunning = TRUE; + BOOLEAN MokProtocol; REFIT_MENU_ENTRY *ChosenEntry; UINTN MenuExit, i; - CHAR16 *Selection; + CHAR16 *Selection = NULL; EG_PIXEL BGColor; // bootstrap InitializeLib(ImageHandle, SystemTable); - InitScreen(); Status = InitRefitLib(ImageHandle); if (EFI_ERROR(Status)) return Status; @@ -1906,7 +2139,10 @@ efi_main (IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) FindLegacyBootType(); if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) CopyMem(GlobalConfig.ScanFor, "ihebocm ", NUM_SCAN_OPTIONS); - ReadConfig(); + ScanVolumes(); + ReadConfig(CONFIG_FILE_NAME); + + InitScreen(); WarnIfLegacyProblems(); MainMenu.TimeoutSeconds = GlobalConfig.Timeout; @@ -1914,11 +2150,11 @@ efi_main (IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) refit_call4_wrapper(BS->SetWatchdogTimer, 0x0000, 0x0000, 0x0000, NULL); // further bootstrap (now with config available) - SetupScreen(); - ScanVolumes(); + MokProtocol = SecureBootSetup(); LoadDrivers(); ScanForBootloaders(); ScanForTools(); + SetupScreen(); if (GlobalConfig.ScanDelay > 0) { BGColor.b = 255; @@ -1931,7 +2167,9 @@ efi_main (IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) RescanAll(); } // if - Selection = StrDuplicate(GlobalConfig.DefaultSelection); + if (GlobalConfig.DefaultSelection) + Selection = StrDuplicate(GlobalConfig.DefaultSelection); + while (MainLoopRunning) { MenuExit = RunMainMenu(&MainMenu, Selection, &ChosenEntry); @@ -1971,15 +2209,19 @@ efi_main (IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) case TAG_LEGACY_UEFI: // Boot a legacy OS on a non-Mac StartLegacyUEFI((LEGACY_ENTRY *)ChosenEntry); break; -#endif // __MAKEWITH_TIANO +#endif case TAG_TOOL: // Start a EFI tool StartTool((LOADER_ENTRY *)ChosenEntry); break; case TAG_EXIT: // Terminate rEFInd - BeginTextScreen(L" "); - return EFI_SUCCESS; + if ((MokProtocol) && !SecureBootUninstall()) { + MainLoopRunning = FALSE; // just in case we get this far + } else { + BeginTextScreen(L" "); + return EFI_SUCCESS; + } break; } // switch()