X-Git-Url: https://code.delx.au/refind/blobdiff_plain/012b59e3b9537d04f9bd7f66a3d673fa999e80a4..9a669ad3b5fb341500d76d487c38a7102eb01b3d:/refind/main.c diff --git a/refind/main.c b/refind/main.c index 187e7c0..45d544c 100644 --- a/refind/main.c +++ b/refind/main.c @@ -34,7 +34,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* - * Modifications copyright (c) 2012-2013 Roderick W. Smith + * Modifications copyright (c) 2012-2014 Roderick W. Smith * * Modifications distributed under the terms of the GNU General Public * License (GPL) version 3 (GPLv3), a copy of which must be distributed @@ -49,6 +49,7 @@ #include "icns.h" #include "menu.h" #include "mok.h" +#include "gpt.h" #include "security_policy.h" #include "../include/Handle.h" #include "../include/refit_call_wrapper.h" @@ -56,38 +57,54 @@ #include "../include/syslinux_mbr.h" #ifdef __MAKEWITH_GNUEFI +#ifndef EFI_SECURITY_VIOLATION #define EFI_SECURITY_VIOLATION EFIERR (26) -#else +#endif +#endif + #include "../EfiLib/BdsHelper.h" -#endif // __MAKEWITH_GNUEFI +#include "../EfiLib/legacy.h" #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL #endif +#ifdef __MAKEWITH_TIANO +#define LibLocateHandle gBS->LocateHandleBuffer +#endif + // -// variables +// constants #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi" #if defined (EFIX64) #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shell.efi,\\shellx64.efi" #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_x64.efi" +#define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_x64.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 #elif defined (EFI32) #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shell.efi,\\shellia32.efi" #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_ia32.efi" +#define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_ia32.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 #else #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shell.efi" #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi" +#define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi" +#define MEMTEST_NAMES L"memtest86.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 FAT_ARCH 0x0ef1fab9 /* ID for Apple "fat" binary */ // 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 @@ -106,6 +123,10 @@ #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" +// Load types +#define TYPE_EFI 1 +#define TYPE_LEGACY 2 + 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 }; @@ -118,14 +139,17 @@ 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, DONT_CHANGE_TEXT_MODE, 20, 0, 0, GRAPHICS_FOR_OSX, LEGACY_TYPE_MAC, 0, 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, TAG_FIRMWARE, - 0, 0, 0, 0, 0, 0 } +REFIT_CONFIG GlobalConfig = { FALSE, FALSE, 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, 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 } }; EFI_GUID GlobalGuid = EFI_GLOBAL_VARIABLE; +GPT_DATA *gPartitions = NULL; + // Structure used to hold boot loader filenames and time stamps in // a linked list; used to sort entries within a directory. struct LOADER_LIST { @@ -140,37 +164,32 @@ struct LOADER_LIST { static VOID AboutrEFInd(VOID) { - CHAR16 *TempStr; // Note: Don't deallocate; moved to menu structure + CHAR16 *FirmwareVendor; if (AboutMenu.EntryCount == 0) { AboutMenu.TitleImage = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT); - AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.6.11.2"); + AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.8.1.1"); AddMenuInfoLine(&AboutMenu, L""); AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2006-2010 Christoph Pfisterer"); - AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2012-2013 Roderick W. Smith"); + AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2012-2014 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""); AddMenuInfoLine(&AboutMenu, L"Running on:"); - TempStr = AllocateZeroPool(256 * sizeof(CHAR16)); - SPrint(TempStr, 255, L" EFI Revision %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & ((1 << 16) - 1)); - AddMenuInfoLine(&AboutMenu, TempStr); + AddMenuInfoLine(&AboutMenu, PoolPrint(L" EFI Revision %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & ((1 << 16) - 1))); #if defined(EFI32) AddMenuInfoLine(&AboutMenu, L" Platform: x86 (32 bit)"); #elif defined(EFIX64) - TempStr = AllocateZeroPool(256 * sizeof(CHAR16)); - SPrint(TempStr, 255, L" Platform: x86_64 (64 bit); Secure Boot %s", secure_mode() ? L"active" : L"inactive"); - AddMenuInfoLine(&AboutMenu, TempStr); + AddMenuInfoLine(&AboutMenu, PoolPrint(L" Platform: x86_64 (64 bit); Secure Boot %s", + secure_mode() ? L"active" : L"inactive")); #else AddMenuInfoLine(&AboutMenu, L" Platform: unknown"); #endif - TempStr = AllocateZeroPool(256 * sizeof(CHAR16)); - SPrint(TempStr, 255, L" Firmware: %s %d.%02d", - ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & ((1 << 16) - 1)); - AddMenuInfoLine(&AboutMenu, TempStr); - TempStr = AllocateZeroPool(256 * sizeof(CHAR16)); - SPrint(TempStr, 255, L" Screen Output: %s", egScreenDescription()); - AddMenuInfoLine(&AboutMenu, TempStr); + FirmwareVendor = StrDuplicate(ST->FirmwareVendor); + LimitStringLength(FirmwareVendor, 65); // More than ~65 causes empty info page on 800x600 display + AddMenuInfoLine(&AboutMenu, PoolPrint(L" Firmware: %s %d.%02d", FirmwareVendor, ST->FirmwareRevision >> 16, + ST->FirmwareRevision & ((1 << 16) - 1))); + AddMenuInfoLine(&AboutMenu, PoolPrint(L" Screen Output: %s", egScreenDescription())); AddMenuInfoLine(&AboutMenu, L""); #if defined(__MAKEWITH_GNUEFI) AddMenuInfoLine(&AboutMenu, L"Built with GNU-EFI"); @@ -207,9 +226,44 @@ static VOID WarnSecureBootError(CHAR16 *Name, BOOLEAN Verbose) { } // if } // VOID WarnSecureBootError() +// 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) + EFI_STATUS Status; + EFI_FILE_HANDLE FileHandle; + CHAR8 Header[512]; + UINTN Size = sizeof(Header); + + if ((RootDir == NULL) || (FileName == NULL)) { + // Assume valid here, because Macs produce NULL RootDir (& maybe FileName) + // when launching from a Firewire drive. This should be handled better, but + // fix would have to be in StartEFIImageList() and/or in FindVolumeAndFilename(). + return TRUE; + } // if + + Status = refit_call5_wrapper(RootDir->Open, RootDir, &FileHandle, FileName, EFI_FILE_MODE_READ, 0); + if (EFI_ERROR(Status)) + return FALSE; + + Status = refit_call3_wrapper(FileHandle->Read, FileHandle, &Size, Header); + refit_call1_wrapper(FileHandle->Close, FileHandle); + + IsValid = !EFI_ERROR(Status) && + Size == sizeof(Header) && + ((Header[0] == 'M' && Header[1] == 'Z' && + (Size = *(UINT32 *)&Header[0x3c]) < 0x180 && + Header[Size] == 'P' && Header[Size+1] == 'E' && + Header[Size+2] == 0 && Header[Size+3] == 0 && + *(UINT16 *)&Header[Size+4] == EFI_STUB_ARCH) || + (*(UINT32 *)&Header == FAT_ARCH)); +#endif + return IsValid; +} // BOOLEAN IsValidLoader() + // Launch an EFI binary. static EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths, - IN CHAR16 *LoadOptions, IN CHAR16 *LoadOptionsPrefix, + IN CHAR16 *LoadOptions, IN UINTN LoaderType, IN CHAR16 *ImageTitle, IN CHAR8 OSType, OUT UINTN *ErrorInStep, IN BOOLEAN Verbose) @@ -217,46 +271,57 @@ static EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths, EFI_STATUS Status, ReturnStatus; EFI_HANDLE ChildImageHandle; EFI_LOADED_IMAGE *ChildLoadedImage = NULL; + REFIT_VOLUME *Volume = NULL; UINTN DevicePathIndex; CHAR16 ErrorInfo[256]; CHAR16 *FullLoadOptions = NULL; + CHAR16 *Filename = NULL; + CHAR16 *Temp; if (ErrorInStep != NULL) *ErrorInStep = 0; // set load options if (LoadOptions != NULL) { - if (LoadOptionsPrefix != NULL) { - MergeStrings(&FullLoadOptions, LoadOptions, L' '); - if (OSType == 'M') { - MergeStrings(&FullLoadOptions, L" ", 0); - // NOTE: That last space is also added by the EFI shell and seems to be significant - // when passing options to Apple's boot.efi... - } // if - } else { - MergeStrings(&FullLoadOptions, LoadOptions, 0); - } // if/else - } 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" "); - } + FullLoadOptions = StrDuplicate(LoadOptions); + if ((LoaderType == TYPE_EFI) && (OSType == 'M')) { + MergeStrings(&FullLoadOptions, L" ", 0); + // NOTE: That last space is also added by the EFI shell and seems to be significant + // when passing options to Apple's boot.efi... + } // if + } // if (LoadOptions != NULL) if (Verbose) - Print(L"Starting %s\nUsing load options '%s'\n", ImageTitle, FullLoadOptions); + Print(L"Starting %s\nUsing load options '%s'\n", ImageTitle, FullLoadOptions ? FullLoadOptions : L""); // 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 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. - // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex], - // ImageData, ImageSize, &ChildImageHandle); - ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex], - NULL, 0, &ChildImageHandle); + FindVolumeAndFilename(DevicePaths[DevicePathIndex], &Volume, &Filename); + // Some EFIs crash if attempting to load driver for invalid architecture, so + // protect for this condition; but sometimes Volume comes back NULL, so provide + // an exception. (TODO: Handle this special condition better.) + if ((LoaderType == TYPE_LEGACY) || (Volume == NULL) || IsValidLoader(Volume->RootDir, Filename)) { + if (Filename && (LoaderType != TYPE_LEGACY)) { + Temp = PoolPrint(L"\\%s %s", Filename, FullLoadOptions ? FullLoadOptions : L""); + if (Temp != NULL) { + MyFreePool(FullLoadOptions); + FullLoadOptions = Temp; + } + } // if (Filename) + + // 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. + // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex], + // ImageData, ImageSize, &ChildImageHandle); + ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex], + NULL, 0, &ChildImageHandle); + } else { + Print(L"Invalid loader file!\n"); + ReturnStatus = EFI_LOAD_ERROR; + } if (ReturnStatus != EFI_NOT_FOUND) { break; } @@ -280,7 +345,7 @@ static EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths, goto bailout_unload; } ChildLoadedImage->LoadOptions = (VOID *)FullLoadOptions; - ChildLoadedImage->LoadOptionsSize = ((UINT32)StrLen(FullLoadOptions) + 1) * sizeof(CHAR16); + ChildLoadedImage->LoadOptionsSize = FullLoadOptions ? ((UINT32)StrLen(FullLoadOptions) + 1) * sizeof(CHAR16) : 0; // turn control over to the image // TODO: (optionally) re-enable the EFI watchdog timer! @@ -308,7 +373,7 @@ bailout: } /* static EFI_STATUS StartEFIImageList() */ static EFI_STATUS StartEFIImage(IN EFI_DEVICE_PATH *DevicePath, - IN CHAR16 *LoadOptions, IN CHAR16 *LoadOptionsPrefix, + IN CHAR16 *LoadOptions, IN UINTN LoaderType, IN CHAR16 *ImageTitle, IN CHAR8 OSType, OUT UINTN *ErrorInStep, IN BOOLEAN Verbose) @@ -317,7 +382,7 @@ static EFI_STATUS StartEFIImage(IN EFI_DEVICE_PATH *DevicePath, DevicePaths[0] = DevicePath; DevicePaths[1] = NULL; - return StartEFIImageList(DevicePaths, LoadOptions, LoadOptionsPrefix, ImageTitle, OSType, ErrorInStep, Verbose); + return StartEFIImageList(DevicePaths, LoadOptions, LoaderType, ImageTitle, OSType, ErrorInStep, Verbose); } /* static EFI_STATUS StartEFIImage() */ // From gummiboot: Retrieve a raw EFI variable. @@ -374,7 +439,6 @@ static EFI_STATUS RebootIntoFirmware(VOID) { refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL); Print(L"Error calling ResetSystem: %r", err); PauseForKey(); -// uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); return err; } @@ -383,12 +447,12 @@ static EFI_STATUS RebootIntoFirmware(VOID) { // EFI OS loader functions // -static VOID StartLoader(IN LOADER_ENTRY *Entry) +static VOID StartLoader(LOADER_ENTRY *Entry) { UINTN ErrorInStep = 0; BeginExternalScreen(Entry->UseGraphicsMode, L"Booting OS"); - StartEFIImage(Entry->DevicePath, Entry->LoadOptions, Basename(Entry->LoaderPath), + StartEFIImage(Entry->DevicePath, Entry->LoadOptions, TYPE_EFI, Basename(Entry->LoaderPath), Entry->OSType, &ErrorInStep, !Entry->UseGraphicsMode); FinishExternalScreen(); } @@ -428,13 +492,11 @@ static CHAR16 * FindInitrd(IN CHAR16 *LoaderPath, IN REFIT_VOLUME *Volume) { InitrdVersion = FindNumbers(DirEntry->FileName); if (KernelVersion != NULL) { if (StriCmp(InitrdVersion, KernelVersion) == 0) { - MergeStrings(&InitrdName, Path, 0); - MergeStrings(&InitrdName, DirEntry->FileName, 0); + InitrdName = PoolPrint(L"%s%s", Path, DirEntry->FileName); } // if } else { if (InitrdVersion == NULL) { - MergeStrings(&InitrdName, Path, 0); - MergeStrings(&InitrdName, DirEntry->FileName, 0); + InitrdName = PoolPrint(L"%s%s", Path, DirEntry->FileName); } // if } // if/else MyFreePool(InitrdVersion); @@ -849,10 +911,12 @@ VOID SetLoaderDefaults(LOADER_ENTRY *Entry, CHAR16 *LoaderPath, REFIT_VOLUME *Vo // locate a custom icon for the loader // Anything found here takes precedence over the "hints" in the OSIconName variable - if (!Entry->me.Image) - Entry->me.Image = egLoadIconAnyType(Volume->RootDir, PathOnly, NoExtension, 128); - if (!Entry->me.Image) + if (!Entry->me.Image) { + Entry->me.Image = egLoadIconAnyType(Volume->RootDir, PathOnly, NoExtension, GlobalConfig.IconSizes[ICON_SIZE_BIG]); + } + if (!Entry->me.Image) { Entry->me.Image = egCopyImage(Volume->VolIconImage); + } // Begin creating icon "hints" by using last part of directory path leading // to the loader @@ -885,7 +949,7 @@ VOID SetLoaderDefaults(LOADER_ENTRY *Entry, CHAR16 *LoaderPath, REFIT_VOLUME *Vo } // if // detect specific loaders - if (StriSubCmp(L"bzImage", LoaderPath) || StriSubCmp(L"vmlinuz", LoaderPath)) { + if (StriSubCmp(L"bzImage", FileName) || StriSubCmp(L"vmlinuz", FileName)) { GuessLinuxDistribution(&OSIconName, Volume, LoaderPath); MergeStrings(&OSIconName, L"linux", L','); Entry->OSType = 'L'; @@ -902,9 +966,6 @@ VOID SetLoaderDefaults(LOADER_ENTRY *Entry, CHAR16 *LoaderPath, REFIT_VOLUME *Vo Entry->OSType = 'R'; ShortcutLetter = 'R'; } else if (StriCmp(LoaderPath, MACOSX_LOADER_PATH) == 0) { - if (Volume->VolIconImage != NULL) { // custom icon file found - Entry->me.Image = Volume->VolIconImage; - } MergeStrings(&OSIconName, L"mac", L','); Entry->OSType = 'M'; ShortcutLetter = 'M'; @@ -923,7 +984,8 @@ VOID SetLoaderDefaults(LOADER_ENTRY *Entry, CHAR16 *LoaderPath, REFIT_VOLUME *Vo 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 || + StriCmp(FileName, L"bkpbootmgfw.efi") == 0) { MergeStrings(&OSIconName, L"win", L','); Entry->OSType = 'W'; ShortcutLetter = 'W'; @@ -954,7 +1016,7 @@ LOADER_ENTRY * AddLoaderEntry(IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN if (Entry != NULL) { Entry->Title = StrDuplicate((LoaderTitle != NULL) ? LoaderTitle : LoaderPath); Entry->me.Title = AllocateZeroPool(sizeof(CHAR16) * 256); - SPrint(Entry->me.Title, 255, L"Boot %s from %s", (LoaderTitle != NULL) ? LoaderTitle : LoaderPath, Volume->VolName); + SPrint(Entry->me.Title, 255, L"Boot %s from %s ", (LoaderTitle != NULL) ? LoaderTitle : LoaderPath, Volume->VolName); Entry->me.Row = 0; Entry->me.BadgeImage = Volume->VolBadgeImage; if ((LoaderPath != NULL) && (LoaderPath[0] != L'\\')) { @@ -1035,34 +1097,46 @@ static VOID CleanUpLoaderList(struct LOADER_LIST *LoaderList) { // 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; + CHAR16 *VolName = NULL, *DontScanDir, *PathCopy = NULL; + UINTN i = 0; BOOLEAN ScanIt = TRUE; - if (IsIn(Volume->VolName, GlobalConfig.DontScanVolumes)) + if ((IsIn(Volume->VolName, GlobalConfig.DontScanVolumes)) || (IsIn(Volume->PartName, GlobalConfig.DontScanVolumes))) return FALSE; if ((StriCmp(Path, SelfDirPath) == 0) && (Volume->DeviceHandle == SelfVolume->DeviceHandle)) return FALSE; - while ((DontScanDir = FindCommaDelimited(GlobalConfig.DontScanDirs, i++)) && ScanIt) { + // See if Path includes an explicit volume declaration that's NOT Volume.... + PathCopy = StrDuplicate(Path); + if (SplitVolumeAndFilename(&PathCopy, &VolName)) { + VolumeNumberToName(Volume, &VolName); + if (VolName && StriCmp(VolName, Volume->VolName) != 0) { + ScanIt = FALSE; + } // if + } // if Path includes volume specification + MyFreePool(PathCopy); + MyFreePool(VolName); + VolName = NULL; + + // See if Volume is in GlobalConfig.DontScanDirs.... + while (ScanIt && (DontScanDir = FindCommaDelimited(GlobalConfig.DontScanDirs, i++))) { SplitVolumeAndFilename(&DontScanDir, &VolName); CleanUpPathNameSlashes(DontScanDir); + VolumeNumberToName(Volume, &VolName); 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); + MyFreePool(VolName); DontScanDir = NULL; - } + VolName = NULL; + } // while() + return ScanIt; } // BOOLEAN ShouldScan() @@ -1161,6 +1235,29 @@ static BOOLEAN IsSymbolicLink(REFIT_VOLUME *Volume, CHAR16 *Path, EFI_FILE_INFO return (DirEntry->FileSize != FileSize2); } // BOOLEAN IsSymbolicLink() +// Returns TRUE if a file with the same name as the original but with +// ".efi.signed" is also present in the same directory. Ubuntu is using +// this filename as a signed version of the original unsigned kernel, and +// 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) { + CHAR16 *NewFile = NULL; + BOOLEAN retval = FALSE; + + MergeStrings(&NewFile, Path, 0); + MergeStrings(&NewFile, Filename, L'\\'); + MergeStrings(&NewFile, L".efi.signed", 0); + if (NewFile != NULL) { + CleanUpPathNameSlashes(NewFile); + if (FileExists(Volume->RootDir, NewFile)) + retval = TRUE; + MyFreePool(NewFile); + } // if + + return retval; +} // BOOLEAN HasSignedCounterpart() + // Scan an individual directory for EFI boot loader files and, if found, // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked // up in ScanEfiFiles(). Sorts the entries within the loader directory so that @@ -1176,8 +1273,7 @@ static BOOLEAN ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 BOOLEAN FoundFallbackDuplicate = FALSE; if ((!SelfDirPath || !Path || ((StriCmp(Path, SelfDirPath) == 0) && (Volume->DeviceHandle != SelfVolume->DeviceHandle)) || - (StriCmp(Path, SelfDirPath) != 0)) && - (ShouldScan(Volume, Path))) { + (StriCmp(Path, SelfDirPath) != 0)) && (ShouldScan(Volume, Path))) { // look through contents of the directory DirIterOpen(Volume->RootDir, Path, &DirIter); while (DirIterNext(&DirIter, 2, Pattern, &DirEntry)) { @@ -1188,7 +1284,8 @@ static BOOLEAN ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 (StriCmp(DirEntry->FileName, FALLBACK_BASENAME) == 0 && (StriCmp(Path, L"EFI\\BOOT") == 0)) || StriSubCmp(L"shell", DirEntry->FileName) || IsSymbolicLink(Volume, Path, DirEntry) || /* is symbolic link */ - IsIn(DirEntry->FileName, GlobalConfig.DontScanFiles)) + HasSignedCounterpart(Volume, Path, DirEntry->FileName) || /* a file with same name plus ".efi.signed" is present */ + FilenameIn(Volume, Path, DirEntry->FileName, GlobalConfig.DontScanFiles)) continue; // skip this if (Path) @@ -1196,6 +1293,10 @@ static BOOLEAN ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 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); @@ -1235,19 +1336,20 @@ static VOID ScanEfiFiles(REFIT_VOLUME *Volume) { EFI_STATUS Status; REFIT_DIR_ITER EfiDirIter; EFI_FILE_INFO *EfiDirEntry; - CHAR16 FileName[256], *Directory, *MatchPatterns, *VolName = NULL, *SelfPath; + CHAR16 FileName[256], *Directory = NULL, *MatchPatterns, *VolName = NULL, *SelfPath; UINTN i, Length; BOOLEAN ScanFallbackLoader = TRUE; + BOOLEAN FoundBRBackup = FALSE; - MatchPatterns = StrDuplicate(LOADER_MATCH_PATTERNS); - if (GlobalConfig.ScanAllLinux) - MergeStrings(&MatchPatterns, LINUX_MATCH_PATTERNS, L','); + if ((Volume->RootDir != NULL) && (Volume->VolName != NULL) && (Volume->IsReadable)) { + MatchPatterns = StrDuplicate(LOADER_MATCH_PATTERNS); + if (GlobalConfig.ScanAllLinux) + MergeStrings(&MatchPatterns, LINUX_MATCH_PATTERNS, L','); - if ((Volume->RootDir != NULL) && (Volume->VolName != NULL)) { // check for Mac OS X boot loader if (ShouldScan(Volume, L"System\\Library\\CoreServices")) { StrCpy(FileName, MACOSX_LOADER_PATH); - if (FileExists(Volume->RootDir, FileName) && !IsIn(L"boot.efi", GlobalConfig.DontScanFiles)) { + if (FileExists(Volume->RootDir, FileName) && !FilenameIn(Volume, Directory, L"boot.efi", GlobalConfig.DontScanFiles)) { AddLoaderEntry(FileName, L"Mac OS X", Volume); if (DuplicatesFallback(Volume, FileName)) ScanFallbackLoader = FALSE; @@ -1255,7 +1357,7 @@ static VOID ScanEfiFiles(REFIT_VOLUME *Volume) { // check for XOM StrCpy(FileName, L"System\\Library\\CoreServices\\xom.efi"); - if (FileExists(Volume->RootDir, FileName) && !IsIn(L"boot.efi", GlobalConfig.DontScanFiles)) { + if (FileExists(Volume->RootDir, FileName) && !FilenameIn(Volume, Directory, L"boot.efi", GlobalConfig.DontScanFiles)) { AddLoaderEntry(FileName, L"Windows XP (XoM)", Volume); if (DuplicatesFallback(Volume, FileName)) ScanFallbackLoader = FALSE; @@ -1263,13 +1365,25 @@ static VOID ScanEfiFiles(REFIT_VOLUME *Volume) { } // if should scan Mac directory // check for Microsoft boot loader/menu - 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; - } + if (ShouldScan(Volume, L"EFI\\Microsoft\\Boot")) { + StrCpy(FileName, L"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi"); + if (FileExists(Volume->RootDir, FileName) && !FilenameIn(Volume, Directory, L"bkpbootmgfw.efi", + GlobalConfig.DontScanFiles)) { + AddLoaderEntry(FileName, L"Microsoft EFI boot (Boot Repair backup)", Volume); + FoundBRBackup = TRUE; + if (DuplicatesFallback(Volume, FileName)) + ScanFallbackLoader = FALSE; + } + StrCpy(FileName, L"EFI\\Microsoft\\Boot\\bootmgfw.efi"); + if (FileExists(Volume->RootDir, FileName) && !FilenameIn(Volume, Directory, L"bootmgfw.efi", GlobalConfig.DontScanFiles)) { + if (FoundBRBackup) + AddLoaderEntry(FileName, L"Supposed Microsoft EFI boot (probably GRUB)", Volume); + else + AddLoaderEntry(FileName, L"Microsoft EFI boot", Volume); + if (DuplicatesFallback(Volume, FileName)) + ScanFallbackLoader = FALSE; + } + } // if // scan the root directory for EFI executables if (ScanLoaderDir(Volume, L"\\", MatchPatterns)) @@ -1291,13 +1405,15 @@ 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)) - ScanFallbackLoader = FALSE; + if (ShouldScan(Volume, Directory)) { + SplitVolumeAndFilename(&Directory, &VolName); + CleanUpPathNameSlashes(Directory); + Length = StrLen(Directory); + if ((Length > 0) && ScanLoaderDir(Volume, Directory, MatchPatterns)) + ScanFallbackLoader = FALSE; + MyFreePool(VolName); + } // if should scan dir MyFreePool(Directory); - MyFreePool(VolName); } // while // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself.... @@ -1526,7 +1642,7 @@ static VOID StartLegacy(IN LEGACY_ENTRY *Entry) ExtractLegacyLoaderPaths(DiscoveredPathList, MAX_DISCOVERED_PATHS, LegacyLoaderList); - Status = StartEFIImageList(DiscoveredPathList, Entry->LoadOptions, NULL, L"legacy loader", 0, &ErrorInStep, TRUE); + Status = StartEFIImageList(DiscoveredPathList, Entry->LoadOptions, TYPE_LEGACY, L"legacy loader", 0, &ErrorInStep, TRUE); if (Status == EFI_NOT_FOUND) { if (ErrorInStep == 1) { Print(L"\nPlease make sure that you have the latest firmware update installed.\n"); @@ -1539,8 +1655,7 @@ static VOID StartLegacy(IN LEGACY_ENTRY *Entry) } /* static VOID StartLegacy() */ // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL -#ifdef __MAKEWITH_TIANO -static VOID StartLegacyUEFI(IN LEGACY_ENTRY *Entry) +static VOID StartLegacyUEFI(LEGACY_ENTRY *Entry) { BeginExternalScreen(TRUE, L"Booting Legacy OS (UEFI mode)"); @@ -1552,13 +1667,12 @@ static VOID StartLegacyUEFI(IN LEGACY_ENTRY *Entry) PauseForKey(); FinishExternalScreen(); } // static VOID StartLegacyUEFI() -#endif // __MAKEWITH_TIANO static LEGACY_ENTRY * AddLegacyEntry(IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume) { LEGACY_ENTRY *Entry, *SubEntry; REFIT_MENU_SCREEN *SubScreen; - CHAR16 *VolDesc; + CHAR16 *VolDesc, *LegacyTitle; CHAR16 ShortcutLetter = 0; if (LoaderTitle == NULL) { @@ -1574,10 +1688,17 @@ static LEGACY_ENTRY * AddLegacyEntry(IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Vo else VolDesc = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" : L"HD"; + LegacyTitle = AllocateZeroPool(256 * sizeof(CHAR16)); + if (LegacyTitle != NULL) + SPrint(LegacyTitle, 255, L"Boot %s from %s", LoaderTitle, VolDesc); + if (IsInSubstring(LegacyTitle, GlobalConfig.DontScanVolumes)) { + MyFreePool(LegacyTitle); + return NULL; + } // if + // prepare the menu entry Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY)); - Entry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16)); - SPrint(Entry->me.Title, 255, L"Boot %s from %s", LoaderTitle, VolDesc); + Entry->me.Title = LegacyTitle; Entry->me.Tag = TAG_LEGACY; Entry->me.Row = 0; Entry->me.ShortcutLetter = ShortcutLetter; @@ -1585,7 +1706,7 @@ static LEGACY_ENTRY * AddLegacyEntry(IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Vo Entry->me.BadgeImage = Volume->VolBadgeImage; Entry->Volume = Volume; Entry->LoadOptions = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" : - ((Volume->DiskKind == DISK_KIND_EXTERNAL) ? L"USB" : L"HD"); + ((Volume->DiskKind == DISK_KIND_EXTERNAL) ? L"USB" : L"HD"); Entry->Enabled = TRUE; // create the submenu @@ -1616,9 +1737,6 @@ static LEGACY_ENTRY * AddLegacyEntry(IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Vo } /* static LEGACY_ENTRY * AddLegacyEntry() */ -#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; @@ -1645,7 +1763,15 @@ static LEGACY_ENTRY * AddLegacyEntryUEFI(BDS_COMMON_OPTION *BdsOption, IN UINT16 LEGACY_ENTRY *Entry, *SubEntry; REFIT_MENU_SCREEN *SubScreen; CHAR16 ShortcutLetter = 0; - CHAR16 *LegacyDescription = BdsOption->Description; + CHAR16 *LegacyDescription = StrDuplicate(BdsOption->Description); + + if (IsInSubstring(LegacyDescription, GlobalConfig.DontScanVolumes)) + return NULL; + + // Remove stray spaces, since many EFIs produce descriptions with lots of + // extra spaces, especially at the end; this throws off centering of the + // description on the screen.... + LimitStringLength(LegacyDescription, 100); // prepare the menu entry Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY)); @@ -1701,19 +1827,28 @@ static VOID ScanLegacyUEFI(IN UINTN DiskType) UINTN Index = 0; CHAR16 BootOption[10]; UINTN BootOrderSize = 0; - CHAR16 Buffer[20]; - BDS_COMMON_OPTION *BdsOption; - LIST_ENTRY TempList; - BBS_BBS_DEVICE_PATH * BbsDevicePath = NULL; + CHAR16 Buffer[20]; + BDS_COMMON_OPTION *BdsOption; + LIST_ENTRY TempList; + BBS_BBS_DEVICE_PATH *BbsDevicePath = NULL; + BOOLEAN SearchingForUsb = FALSE; InitializeListHead (&TempList); ZeroMem (Buffer, sizeof (Buffer)); // If LegacyBios protocol is not implemented on this platform, then //we do not support this type of legacy boot on this machine. - Status = gBS->LocateProtocol(&gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios); + Status = refit_call3_wrapper(gBS->LocateProtocol, &gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios); if (EFI_ERROR (Status)) - return; + return; + + // EFI calls USB drives BBS_HARDDRIVE, but we want to distinguish them, + // so we set DiskType inappropriately elsewhere in the program and + // "translate" it here. + if (DiskType == BBS_USB) { + DiskType = BBS_HARDDISK; + SearchingForUsb = TRUE; + } // if // Grab the boot order BootOrder = BdsLibGetVariableAndSize(L"BootOrder", &gEfiGlobalVariableGuid, &BootOrderSize); @@ -1731,20 +1866,28 @@ static VOID ScanLegacyUEFI(IN UINTN DiskType) if (BdsOption != NULL) { BbsDevicePath = (BBS_BBS_DEVICE_PATH *)BdsOption->DevicePath; - // Only add the entry if it is of a requested type (e.g. USB, HD) - // Two checks necessary because some systems return EFI boot loaders // with a DeviceType value that would inappropriately include them // as legacy loaders.... if ((BbsDevicePath->DeviceType == DiskType) && (BdsOption->DevicePath->Type == DEVICE_TYPE_BIOS)) { - AddLegacyEntryUEFI(BdsOption, BbsDevicePath->DeviceType); - } - } + // USB flash drives appear as hard disks with certain media flags set. + // Look for this, and if present, pass it on with the (technically + // incorrect, but internally useful) BBS_TYPE_USB flag set. + if (DiskType == BBS_HARDDISK) { + if (SearchingForUsb && (BbsDevicePath->StatusFlag & (BBS_MEDIA_PRESENT | BBS_MEDIA_MAYBE_PRESENT))) { + AddLegacyEntryUEFI(BdsOption, BBS_USB); + } else if (!SearchingForUsb && !(BbsDevicePath->StatusFlag & (BBS_MEDIA_PRESENT | BBS_MEDIA_MAYBE_PRESENT))) { + AddLegacyEntryUEFI(BdsOption, DiskType); + } + } else { + AddLegacyEntryUEFI(BdsOption, DiskType); + } // if/else + } // if + } // if (BdsOption != NULL) Index++; - } + } // while } /* static VOID ScanLegacyUEFI() */ -#endif // __MAKEWITH_GNUEFI static VOID ScanLegacyVolume(REFIT_VOLUME *Volume, UINTN VolumeIndex) { UINTN VolumeIndex2; @@ -1808,6 +1951,8 @@ static VOID ScanLegacyInternal(VOID) ScanLegacyVolume(Volume, VolumeIndex); } // for } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) { + // TODO: This actually picks up USB flash drives, too; try to find + // a way to differentiate the two.... ScanLegacyUEFI(BBS_HARDDISK); } } /* static VOID ScanLegacyInternal() */ @@ -1826,6 +1971,8 @@ static VOID ScanLegacyExternal(VOID) ScanLegacyVolume(Volume, VolumeIndex); } // for } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) { + // TODO: This actually doesn't do anything useful; leaving in hopes of + // fixing it later.... ScanLegacyUEFI(BBS_USB); } } /* static VOID ScanLegacyExternal() */ @@ -1837,7 +1984,7 @@ static VOID ScanLegacyExternal(VOID) static VOID StartTool(IN LOADER_ENTRY *Entry) { BeginExternalScreen(Entry->UseGraphicsMode, Entry->me.Title + 6); // assumes "Start