X-Git-Url: https://code.delx.au/refind/blobdiff_plain/34d525d050a59927fce87dbce5af4bd47c8fbe52..75afe82dbf9803baa447499a1e7942a4ca342535:/refind/main.c diff --git a/refind/main.c b/refind/main.c index 22693f1..e27e149 100644 --- a/refind/main.c +++ b/refind/main.c @@ -49,16 +49,17 @@ #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 -#include "../EfiLib/BdsHelper.h" -#else +#ifdef __MAKEWITH_GNUEFI #define EFI_SECURITY_VIOLATION EFIERR (26) -#endif // __MAKEWITH_TIANO +#else +#include "../EfiLib/BdsHelper.h" +#endif // __MAKEWITH_GNUEFI // // variables @@ -67,15 +68,21 @@ #if defined (EFIX64) #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,\\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" +#define MOK_NAMES L"\\EFI\\tools\\MokManager.efi,\\EFI\\fedora\\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 @@ -105,8 +112,8 @@ static REFIT_MENU_SCREEN MainMenu = { L"Main Menu", NULL, 0, NULL, 0, NULL 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, 0, 20, 0, 0, GRAPHICS_FOR_OSX, LEGACY_TYPE_MAC, 0, - NULL, 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 @@ -127,7 +134,7 @@ static VOID AboutrEFInd(VOID) if (AboutMenu.EntryCount == 0) { AboutMenu.TitleImage = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT); - AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.6.0.4"); + AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.6.6.4"); AddMenuInfoLine(&AboutMenu, L""); AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2006-2010 Christoph Pfisterer"); AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2012 Roderick W. Smith"); @@ -179,15 +186,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; @@ -195,7 +196,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); @@ -217,8 +217,8 @@ static EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths, // 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. @@ -226,25 +226,6 @@ 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) || (Status == EFI_SECURITY_VIOLATION)) && (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; } @@ -256,37 +237,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); @@ -595,7 +574,17 @@ 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"); @@ -621,12 +610,12 @@ VOID GenerateSubScreen(LOADER_ENTRY *Entry, IN REFIT_VOLUME *Volume) { // earlier.... if ((SubScreen->Entries != NULL) && (SubScreen->Entries[0] != NULL)) { MyFreePool(SubScreen->Entries[0]->Title); - SubScreen->Entries[0]->Title = StrDuplicate(TokenList[0]); + 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 = AddInitrdToOptions(TokenList[1], InitrdName); FreeTokenLine(&TokenList, &TokenCount); @@ -728,23 +717,20 @@ static CHAR16 * GetMainLinuxOptions(IN CHAR16 * LoaderPath, IN REFIT_VOLUME *Vol // 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, REFIT_VOLUME *Volume) { - CHAR16 IconFileName[256]; - CHAR16 *FileName, *PathOnly, *OSIconName = NULL, *Temp, *SubString; + CHAR16 *FileName, *PathOnly, *NoExtension, *OSIconName = NULL, *Temp, *SubString; CHAR16 ShortcutLetter = 0; - UINTN i, Length; + UINTN i = 0, Length; FileName = Basename(LoaderPath); PathOnly = FindPath(LoaderPath); + NoExtension = StripEfiExtension(FileName); // locate a custom icon for the loader // Anything found here takes precedence over the "hints" in the OSIconName variable - 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)) { + 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 @@ -916,27 +902,124 @@ 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. Returns TRUE if none of these conditions +// is met -- that is, if the path is eligible for scanning. Also reduces *Path to a +// path alone, with no volume specification. +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; + + 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. +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; +// Print(L"Entering ScanLoaderDir(), scanning '%s' for pattern '%s'\n", Path, Pattern); if ((!SelfDirPath || !Path || ((StriCmp(Path, SelfDirPath) == 0) && (Volume->DeviceHandle != SelfVolume->DeviceHandle)) || (StriCmp(Path, SelfDirPath) != 0)) && - (!IsIn(Path, GlobalConfig.DontScanDirs)) && - (!IsIn(Volume->VolName, GlobalConfig.DontScanVolumes))) { + (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 @@ -951,6 +1034,8 @@ static VOID ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 *P NewLoader->FileName = StrDuplicate(FileName); NewLoader->TimeStamp = DirEntry->ModificationTime; LoaderList = AddLoaderListEntry(LoaderList, NewLoader); + if (DuplicatesFallback(Volume, FileName)) + FoundFallbackDuplicate = TRUE; } // if MyFreePool(Extension); } // while @@ -969,18 +1054,23 @@ 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 +// PauseForKey(); + 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; - UINTN i, Length; + CHAR16 FileName[256], *Directory, *MatchPatterns, *VolName = NULL; + UINTN i, Length, VolNum; + 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 @@ -988,12 +1078,16 @@ static VOID ScanEfiFiles(REFIT_VOLUME *Volume) { 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"); 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 @@ -1002,18 +1096,22 @@ static VOID ScanEfiFiles(REFIT_VOLUME *Volume) { if (FileExists(Volume->RootDir, FileName) && !IsIn(L"EFI\\Microsoft\\Boot", GlobalConfig.DontScanDirs) && !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 + continue; // skip this, doesn't contain boot loaders or is scanned later SPrint(FileName, 255, L"EFI\\%s", EfiDirEntry->FileName); - ScanLoaderDir(Volume, FileName, MatchPatterns); + if (ScanLoaderDir(Volume, FileName, MatchPatterns)) + ScanFallbackLoader = FALSE; } // while() Status = DirIterClose(&EfiDirIter); if (Status != EFI_NOT_FOUND) @@ -1022,12 +1120,26 @@ static VOID ScanEfiFiles(REFIT_VOLUME *Volume) { // Scan user-specified (or additional default) directories.... i = 0; while ((Directory = FindCommaDelimited(GlobalConfig.AlsoScan, i++)) != NULL) { + VolNum = VOL_DONTSCAN; + SplitVolumeAndFilename(&Directory, &VolName); CleanUpPathNameSlashes(Directory); Length = StrLen(Directory); - if (Length > 0) - ScanLoaderDir(Volume, Directory, MatchPatterns); + if (VolName && (Length > 0) && (StrLen(VolName) > 2) && (VolName[0] == L'f') && (VolName[1] == L's') && + (VolName[2] >= L'0') && (VolName[2] <= L'9')) + VolNum = Atoi(VolName + 2); + if ((Length > 0) && ((VolName == NULL) || (StriCmp(VolName, Volume->VolName) == 0) || (Volume->VolNumber == VolNum))) + if (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) && + ((StriCmp(SelfDirPath, L"EFI\\BOOT") != 0) || (Volume->DeviceHandle != SelfVolume->DeviceHandle))) { + AddLoaderEntry(FALLBACK_FULLNAME, L"Fallback boot loader", Volume); + } } // if } // static VOID ScanEfiFiles() @@ -1334,7 +1446,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; @@ -1463,9 +1577,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; @@ -1738,7 +1850,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)) @@ -1802,7 +1914,7 @@ static VOID ScanForBootloaders(VOID) { ScanLegacyExternal(); break; case 'm': case 'M': - ScanUserConfigured(); + ScanUserConfigured(CONFIG_FILE_NAME); break; case 'e': case 'E': ScanExternal(); @@ -1866,7 +1978,6 @@ static VOID ScanForTools(VOID) { case TAG_GPTSYNC: MyFreePool(FileName); FileName = StrDuplicate(L"\\efi\\tools\\gptsync.efi"); -// MergeStrings(&FileName, L"\\efi\\tools\\gptsync.efi", 0); if (FileExists(SelfRootDir, FileName)) { AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART), 'P', FALSE); } @@ -1874,7 +1985,6 @@ static VOID ScanForTools(VOID) { case TAG_APPLE_RECOVERY: MyFreePool(FileName); FileName = StrDuplicate(L"\\com.apple.recovery.boot\\boot.efi"); -// MergeStrings(&FileName, L"\\com.apple.recovery.boot\\boot.efi", 0); 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); @@ -1895,7 +2005,7 @@ static VOID ScanForTools(VOID) { } // while if (FileExists(SelfDir, L"MokManager.efi")) { MyFreePool(FileName); - FileName = StrDuplicate(SelfDirPath); + FileName = SelfDirPath ? StrDuplicate(SelfDirPath) : NULL; MergeStrings(&FileName, L"\\MokManager.efi", 0); SPrint(Description, 255, L"MOK Key Manager at %s", FileName); AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, Description, @@ -1928,7 +2038,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) { @@ -1944,6 +2054,42 @@ 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 // @@ -1953,14 +2099,14 @@ 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; @@ -1970,7 +2116,10 @@ efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) FindLegacyBootType(); if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) CopyMem(GlobalConfig.ScanFor, "ihebocm ", NUM_SCAN_OPTIONS); + ScanVolumes(); ReadConfig(CONFIG_FILE_NAME); + + InitScreen(); WarnIfLegacyProblems(); MainMenu.TimeoutSeconds = GlobalConfig.Timeout; @@ -1978,11 +2127,11 @@ efi_main (EFI_HANDLE ImageHandle, 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; @@ -1995,7 +2144,9 @@ efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) RescanAll(); } // if - Selection = StrDuplicate(GlobalConfig.DefaultSelection); + if (GlobalConfig.DefaultSelection) + Selection = StrDuplicate(GlobalConfig.DefaultSelection); + while (MainLoopRunning) { MenuExit = RunMainMenu(&MainMenu, Selection, &ChosenEntry); @@ -2035,15 +2186,19 @@ efi_main (EFI_HANDLE ImageHandle, 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()