X-Git-Url: https://code.delx.au/refind/blobdiff_plain/12cfc677aad6ab1519212ee74eae05b2d04b8dc6..05d641020fe8cea57fa41b9e19459727a463d0c0:/refind/main.c diff --git a/refind/main.c b/refind/main.c index d116576..ccbf97d 100644 --- a/refind/main.c +++ b/refind/main.c @@ -35,11 +35,11 @@ */ /* * Modifications copyright (c) 2012 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 * with this source code or binaries made from it. - * + * */ #include "global.h" @@ -48,26 +48,36 @@ #include "lib.h" #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_GNUEFI +#define EFI_SECURITY_VIOLATION EFIERR (26) +#else +#include "../EfiLib/BdsHelper.h" +#endif // __MAKEWITH_GNUEFI + +// // variables #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" #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" #else #define SHELL_NAMES L"\\EFI\\tools\\shell.efi" #define DRIVER_DIRS L"drivers" #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 @@ -80,17 +90,25 @@ // 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, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - {TAG_SHELL, TAG_ABOUT, TAG_SHUTDOWN, TAG_REBOOT, 0, 0, 0, 0, 0 }}; +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 // a linked list; used to sort entries within a directory. @@ -110,7 +128,7 @@ static VOID AboutrEFInd(VOID) if (AboutMenu.EntryCount == 0) { AboutMenu.TitleImage = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT); - AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.4.4"); + AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.6.5"); AddMenuInfoLine(&AboutMenu, L""); AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2006-2010 Christoph Pfisterer"); AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2012 Roderick W. Smith"); @@ -124,7 +142,9 @@ static VOID AboutrEFInd(VOID) #if defined(EFI32) AddMenuInfoLine(&AboutMenu, L" Platform: x86 (32 bit)"); #elif defined(EFIX64) - AddMenuInfoLine(&AboutMenu, L" Platform: x86_64 (64 bit)"); + 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); #else AddMenuInfoLine(&AboutMenu, L" Platform: unknown"); #endif @@ -150,31 +170,59 @@ static VOID AboutrEFInd(VOID) RunMenu(&AboutMenu, NULL); } /* VOID AboutrEFInd() */ +// Launch an EFI binary. static EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths, IN CHAR16 *LoadOptions, IN CHAR16 *LoadOptionsPrefix, - IN CHAR16 *ImageTitle, + IN CHAR16 *ImageTitle, IN CHAR8 OSType, OUT UINTN *ErrorInStep, IN BOOLEAN Verbose) { EFI_STATUS Status, ReturnStatus; EFI_HANDLE ChildImageHandle; - EFI_LOADED_IMAGE *ChildLoadedImage; + EFI_LOADED_IMAGE *ChildLoadedImage = NULL; UINTN DevicePathIndex; CHAR16 ErrorInfo[256]; CHAR16 *FullLoadOptions = NULL; - if (Verbose) - Print(L"Starting %s\n", ImageTitle); if (ErrorInStep != NULL) *ErrorInStep = 0; - // load the image into memory + // 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" "); + } + 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++) { - ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex], NULL, 0, &ChildImageHandle); - if (ReturnStatus != EFI_NOT_FOUND) { - break; - } + // 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); + if (ReturnStatus != EFI_NOT_FOUND) { + break; + } } SPrint(ErrorInfo, 255, L"while loading %s", ImageTitle); if (CheckError(Status, ErrorInfo)) { @@ -183,35 +231,22 @@ static EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths, goto bailout; } - // set load options - if (LoadOptions != NULL) { - 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; - } - - if (LoadOptionsPrefix != NULL) { - MergeStrings(&FullLoadOptions, LoadOptionsPrefix, 0); - MergeStrings(&FullLoadOptions, LoadOptions, L' '); - 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... - } - // NOTE: We also include the terminating null in the length for safety. - ChildLoadedImage->LoadOptions = (VOID *)FullLoadOptions; - ChildLoadedImage->LoadOptionsSize = ((UINT32)StrLen(FullLoadOptions) + 1) * sizeof(CHAR16); - if (Verbose) - Print(L"Using load options '%s'\n", FullLoadOptions); + 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(); - - // turn control over to the image - // TODO: (optionally) re-enable the EFI watchdog timer! 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)) { @@ -225,15 +260,15 @@ static EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths, bailout_unload: // unload the image, we don't care if it works or not... Status = refit_call1_wrapper(BS->UnloadImage, ChildImageHandle); + bailout: - if (FullLoadOptions != NULL) - FreePool(FullLoadOptions); + MyFreePool(FullLoadOptions); return ReturnStatus; } /* static EFI_STATUS StartEFIImageList() */ static EFI_STATUS StartEFIImage(IN EFI_DEVICE_PATH *DevicePath, IN CHAR16 *LoadOptions, IN CHAR16 *LoadOptionsPrefix, - IN CHAR16 *ImageTitle, + IN CHAR16 *ImageTitle, IN CHAR8 OSType, OUT UINTN *ErrorInStep, IN BOOLEAN Verbose) { @@ -241,7 +276,7 @@ static EFI_STATUS StartEFIImage(IN EFI_DEVICE_PATH *DevicePath, DevicePaths[0] = DevicePath; DevicePaths[1] = NULL; - return StartEFIImageList(DevicePaths, LoadOptions, LoadOptionsPrefix, ImageTitle, ErrorInStep, Verbose); + return StartEFIImageList(DevicePaths, LoadOptions, LoadOptionsPrefix, ImageTitle, OSType, ErrorInStep, Verbose); } /* static EFI_STATUS StartEFIImage() */ // @@ -253,8 +288,8 @@ static VOID StartLoader(IN LOADER_ENTRY *Entry) UINTN ErrorInStep = 0; BeginExternalScreen(Entry->UseGraphicsMode, L"Booting OS"); - StartEFIImage(Entry->DevicePath, Entry->LoadOptions, - Basename(Entry->LoaderPath), Basename(Entry->LoaderPath), &ErrorInStep, !Entry->UseGraphicsMode); + StartEFIImage(Entry->DevicePath, Entry->LoadOptions, Basename(Entry->LoaderPath), + Basename(Entry->LoaderPath), Entry->OSType, &ErrorInStep, !Entry->UseGraphicsMode); FinishExternalScreen(); } @@ -302,14 +337,13 @@ static CHAR16 * FindInitrd(IN CHAR16 *LoaderPath, IN REFIT_VOLUME *Volume) { MergeStrings(&InitrdName, DirEntry->FileName, 0); } // if } // if/else - if (InitrdVersion != NULL) - FreePool(InitrdVersion); + MyFreePool(InitrdVersion); } // while DirIterClose(&DirIter); // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed. - FreePool(KernelVersion); - FreePool(Path); + MyFreePool(KernelVersion); + MyFreePool(Path); return (InitrdName); } // static CHAR16 * FindInitrd() @@ -328,8 +362,8 @@ static REFIT_MENU_SCREEN* CopyMenuScreen(REFIT_MENU_SCREEN *Entry) { NewEntry = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN)); if ((Entry != NULL) && (NewEntry != NULL)) { CopyMem(NewEntry, Entry, sizeof(REFIT_MENU_SCREEN)); - NewEntry->Title = StrDuplicate(Entry->Title); - NewEntry->TimeoutText = StrDuplicate(Entry->TimeoutText); + NewEntry->Title = (Entry->Title) ? StrDuplicate(Entry->Title) : NULL; + NewEntry->TimeoutText = (Entry->TimeoutText) ? StrDuplicate(Entry->TimeoutText) : NULL; if (Entry->TitleImage != NULL) { NewEntry->TitleImage = AllocatePool(sizeof(EG_IMAGE)); if (NewEntry->TitleImage != NULL) @@ -337,12 +371,14 @@ static REFIT_MENU_SCREEN* CopyMenuScreen(REFIT_MENU_SCREEN *Entry) { } // if NewEntry->InfoLines = (CHAR16**) AllocateZeroPool(Entry->InfoLineCount * (sizeof(CHAR16*))); for (i = 0; i < Entry->InfoLineCount && NewEntry->InfoLines; i++) { - NewEntry->InfoLines[i] = StrDuplicate(Entry->InfoLines[i]); + NewEntry->InfoLines[i] = (Entry->InfoLines[i]) ? StrDuplicate(Entry->InfoLines[i]) : NULL; } // for NewEntry->Entries = (REFIT_MENU_ENTRY**) AllocateZeroPool(Entry->EntryCount * (sizeof (REFIT_MENU_ENTRY*))); for (i = 0; i < Entry->EntryCount && NewEntry->Entries; i++) { AddMenuEntry(NewEntry, Entry->Entries[i]); } // for + NewEntry->Hint1 = (Entry->Hint1) ? StrDuplicate(Entry->Hint1) : NULL; + NewEntry->Hint2 = (Entry->Hint2) ? StrDuplicate(Entry->Hint2) : NULL; } // if return (NewEntry); } // static REFIT_MENU_SCREEN* CopyMenuScreen() @@ -358,7 +394,7 @@ static REFIT_MENU_ENTRY* CopyMenuEntry(REFIT_MENU_ENTRY *Entry) { NewEntry = AllocateZeroPool(sizeof(REFIT_MENU_ENTRY)); if ((Entry != NULL) && (NewEntry != NULL)) { CopyMem(NewEntry, Entry, sizeof(REFIT_MENU_ENTRY)); - NewEntry->Title = StrDuplicate(Entry->Title); + NewEntry->Title = (Entry->Title) ? StrDuplicate(Entry->Title) : NULL; if (Entry->BadgeImage != NULL) { NewEntry->BadgeImage = AllocatePool(sizeof(EG_IMAGE)); if (NewEntry->BadgeImage != NULL) @@ -392,17 +428,34 @@ LOADER_ENTRY *InitializeLoaderEntry(IN LOADER_ENTRY *Entry) { NewEntry->UseGraphicsMode = FALSE; NewEntry->OSType = 0; if (Entry != NULL) { - NewEntry->LoaderPath = StrDuplicate(Entry->LoaderPath); - NewEntry->VolName = StrDuplicate(Entry->VolName); + NewEntry->LoaderPath = (Entry->LoaderPath) ? StrDuplicate(Entry->LoaderPath) : NULL; + NewEntry->VolName = (Entry->VolName) ? StrDuplicate(Entry->VolName) : NULL; NewEntry->DevicePath = Entry->DevicePath; NewEntry->UseGraphicsMode = Entry->UseGraphicsMode; - NewEntry->LoadOptions = StrDuplicate(Entry->LoadOptions); - NewEntry->InitrdPath = StrDuplicate(Entry->InitrdPath); + NewEntry->LoadOptions = (Entry->LoadOptions) ? StrDuplicate(Entry->LoadOptions) : NULL; + NewEntry->InitrdPath = (Entry->InitrdPath) ? StrDuplicate(Entry->InitrdPath) : NULL; } } // if 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. @@ -411,7 +464,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; @@ -426,15 +479,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' '); - FreePool(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; @@ -445,7 +501,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; @@ -453,7 +509,7 @@ VOID GenerateSubScreen(LOADER_ENTRY *Entry, IN REFIT_VOLUME *Volume) { // create the submenu if (StrLen(Entry->Title) == 0) { - FreePool(Entry->Title); + MyFreePool(Entry->Title); Entry->Title = NULL; } SubScreen = InitializeSubScreen(Entry); @@ -512,15 +568,25 @@ 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) { SubEntry->me.Title = L"Run Apple Hardware Test"; - FreePool(SubEntry->LoaderPath); + MyFreePool(SubEntry->LoaderPath); SubEntry->LoaderPath = StrDuplicate(DiagsFileName); SubEntry->DevicePath = FileDevicePath(Volume->DeviceHandle, SubEntry->LoaderPath); SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_OSX; @@ -531,28 +597,27 @@ 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 = StrDuplicate(TokenList[0]); + } // if + FreeTokenLine(&TokenList, &TokenCount); while ((TokenCount = ReadTokenLine(File, &TokenList)) > 1) { SubEntry = InitializeLoaderEntry(Entry); SubEntry->me.Title = StrDuplicate(TokenList[0]); - if (SubEntry->LoadOptions != NULL) - FreePool(SubEntry->LoadOptions); - SubEntry->LoadOptions = StrDuplicate(TokenList[1]); - MergeStrings(&SubEntry->LoadOptions, InitrdOption, L' '); + MyFreePool(SubEntry->LoadOptions); + SubEntry->LoadOptions = AddInitrdToOptions(TokenList[1], InitrdName); FreeTokenLine(&TokenList, &TokenCount); SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_LINUX; AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry); } // while - if (InitrdOption) - FreePool(InitrdOption); - if (Temp) - FreePool(Temp); - FreePool(File); + MyFreePool(InitrdName); + MyFreePool(File); } // if Linux options file exists } else if (Entry->OSType == 'E') { // entries for ELILO @@ -631,49 +696,71 @@ 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, ' '); - if (InitrdOption != NULL) - FreePool(InitrdOption); - if (InitrdName != NULL) - FreePool(InitrdName); - return (Options); + FullOptions = AddInitrdToOptions(Options, InitrdName); + + MyFreePool(Options); + MyFreePool(InitrdName); + 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, *IconNames = NULL, *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 + while ((Temp = FindCommaDelimited(ICON_EXTENSIONS, i++)) != NULL) { + MergeStrings(&IconNames, NoExtension, L','); + MergeStrings(&IconNames, Temp, L'.'); + MyFreePool(Temp); + } + Entry->me.Image = LoadIcns(Volume->RootDir, IconNames, 128); + if (!Entry->me.Image) Entry->me.Image = Volume->VolIconImage; - } // icon matched to loader or volume + MyFreePool(IconNames); + // Begin creating icon "hints" by using last part of directory path leading + // to the loader Temp = FindLastDirName(LoaderPath); MergeStrings(&OSIconName, Temp, L','); - FreePool(Temp); + MyFreePool(Temp); + Temp = NULL; if (OSIconName != NULL) { 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','); @@ -696,7 +783,7 @@ VOID SetLoaderDefaults(LOADER_ENTRY *Entry, CHAR16 *LoaderPath, IN REFIT_VOLUME Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_OSX; } else if (StriCmp(FileName, L"diags.efi") == 0) { MergeStrings(&OSIconName, L"hwtest", L','); - } else if (StriCmp(FileName, L"e.efi") == 0 || StriCmp(FileName, L"elilo.efi") == 0) { + } 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 (ShortcutLetter == 0) @@ -708,7 +795,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'; @@ -726,8 +813,7 @@ VOID SetLoaderDefaults(LOADER_ENTRY *Entry, CHAR16 *LoaderPath, IN REFIT_VOLUME Entry->me.ShortcutLetter = ShortcutLetter; if (Entry->me.Image == NULL) Entry->me.Image = LoadOSIcon(OSIconName, L"unknown", FALSE); - if (PathOnly != NULL) - FreePool(PathOnly); + MyFreePool(PathOnly); } // VOID SetLoaderDefaults() // Add a specified EFI boot loader to the list, using automatic settings @@ -763,12 +849,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) + @@ -811,11 +896,45 @@ static VOID CleanUpLoaderList(struct LOADER_LIST *LoaderList) { while (LoaderList != NULL) { Temp = LoaderList; LoaderList = LoaderList->NextEntry; - FreePool(Temp->FileName); - FreePool(Temp); + MyFreePool(Temp->FileName); + MyFreePool(Temp); } // 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() + // 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. @@ -827,24 +946,24 @@ static VOID ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 *P CHAR16 FileName[256], *Extension; struct LOADER_LIST *LoaderList = NULL, *NewLoader; - if ((!SelfDirPath || !Path || ((StriCmp(Path, SelfDirPath) == 0) && Volume->DeviceHandle != SelfVolume->DeviceHandle) || - (StriCmp(Path, SelfDirPath) != 0)) && (!IsIn(Path, GlobalConfig.DontScan))) { + 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(DirEntry->FileName, L"TextMode.efi") == 0 || - StriCmp(DirEntry->FileName, L"ebounce.efi") == 0 || - StriCmp(DirEntry->FileName, L"GraphicsConsole.efi") == 0 || StriCmp(Extension, L".icns") == 0 || - StriSubCmp(L"shell", DirEntry->FileName)) + StriCmp(Extension, L".png") == 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) { @@ -852,7 +971,7 @@ static VOID ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 *P NewLoader->TimeStamp = DirEntry->ModificationTime; LoaderList = AddLoaderListEntry(LoaderList, NewLoader); } // if - FreePool(Extension); + MyFreePool(Extension); } // while NewLoader = LoaderList; while (NewLoader != NULL) { @@ -863,9 +982,9 @@ static VOID ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 *P Status = DirIterClose(&DirIter); if (Status != EFI_NOT_FOUND) { if (Path) - SPrint(FileName, 255, L"while scanning the %s directory", Path); + SPrint(FileName, 255, L"while scanning the %s directory", Path); else - StrCpy(FileName, L"while scanning the root directory"); + StrCpy(FileName, L"while scanning the root directory"); CheckError(Status, FileName); } // if (Status != EFI_NOT_FOUND) } // if not scanning our own directory @@ -875,31 +994,32 @@ 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; - MatchPatterns = StrDuplicate(LOADER_MATCH_PATTERNS); - if (GlobalConfig.ScanAllLinux) - MergeStrings(&MatchPatterns, LINUX_MATCH_PATTERNS, L','); + 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 (!IsIn(L"System\\Library\\CoreServices", GlobalConfig.DontScan)) { + if (!IsIn(L"System\\Library\\CoreServices", GlobalConfig.DontScanDirs)) { StrCpy(FileName, MACOSX_LOADER_PATH); - if (FileExists(Volume->RootDir, FileName)) { + if (FileExists(Volume->RootDir, FileName) && !IsIn(L"boot.efi", GlobalConfig.DontScanFiles)) { AddLoaderEntry(FileName, L"Mac OS X", Volume); } // check for XOM StrCpy(FileName, L"System\\Library\\CoreServices\\xom.efi"); - if (FileExists(Volume->RootDir, FileName)) { + if (FileExists(Volume->RootDir, FileName) && !IsIn(L"boot.efi", GlobalConfig.DontScanFiles)) { AddLoaderEntry(FileName, L"Windows XP (XoM)", Volume); } - } // if Mac directory not in GlobalConfig.DontScan list + } // if Mac directory not in GlobalConfig.DontScanDirs list // 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.DontScan)) { + 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); } @@ -921,11 +1041,17 @@ 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) + 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))) ScanLoaderDir(Volume, Directory, MatchPatterns); - FreePool(Directory); + MyFreePool(Directory); + MyFreePool(VolName); } // while } // if } // static VOID ScanEfiFiles() @@ -1128,7 +1254,7 @@ static VOID StartLegacy(IN LEGACY_ENTRY *Entry) UINTN ErrorInStep = 0; EFI_DEVICE_PATH *DiscoveredPathList[MAX_DISCOVERED_PATHS]; - BeginExternalScreen(TRUE, L"Booting Legacy OS"); + BeginExternalScreen(TRUE, L"Booting Legacy OS (Mac mode)"); BootLogoImage = LoadOSIcon(Entry->Volume->OSIconName, L"legacy", TRUE); if (BootLogoImage != NULL) @@ -1137,12 +1263,13 @@ static VOID StartLegacy(IN LEGACY_ENTRY *Entry) (UGAHeight - BootLogoImage->Height) >> 1, &StdBackgroundPixel); - if (Entry->Volume->IsMbrPartition) + if (Entry->Volume->IsMbrPartition) { ActivateMbrPartition(Entry->Volume->WholeDiskBlockIO, Entry->Volume->MbrPartitionIndex); + } ExtractLegacyLoaderPaths(DiscoveredPathList, MAX_DISCOVERED_PATHS, LegacyLoaderList); - Status = StartEFIImageList(DiscoveredPathList, Entry->LoadOptions, NULL, L"legacy loader", &ErrorInStep, TRUE); + Status = StartEFIImageList(DiscoveredPathList, Entry->LoadOptions, NULL, 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"); @@ -1154,6 +1281,22 @@ static VOID StartLegacy(IN LEGACY_ENTRY *Entry) FinishExternalScreen(); } /* 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) +{ + BeginExternalScreen(TRUE, L"Booting Legacy OS (UEFI mode)"); + + BdsLibConnectDevicePath (Entry->BdsOption->DevicePath); + BdsLibDoLegacyBoot(Entry->BdsOption); + + // If we get here, it means that there was a failure.... + Print(L"Failure booting legacy (BIOS) OS."); + PauseForKey(); + FinishExternalScreen(); +} // static VOID StartLegacyUEFI() +#endif // __MAKEWITH_TIANO + static LEGACY_ENTRY * AddLegacyEntry(IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume) { LEGACY_ENTRY *Entry, *SubEntry; @@ -1193,6 +1336,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)); @@ -1209,6 +1358,140 @@ static LEGACY_ENTRY * AddLegacyEntry(IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Vo return Entry; } /* 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; + + switch (DiskType) { + case BBS_HARDDISK: + Badge = BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL); + break; + case BBS_USB: + Badge = BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL); + break; + case BBS_CDROM: + Badge = BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL); + break; + } // switch() + return Badge; +} // static EG_IMAGE * GetDiskBadge() + +/** + Create a rEFInd boot option from a Legacy BIOS protocol option. +*/ +static LEGACY_ENTRY * AddLegacyEntryUEFI(BDS_COMMON_OPTION *BdsOption, IN UINT16 DiskType) +{ + LEGACY_ENTRY *Entry, *SubEntry; + REFIT_MENU_SCREEN *SubScreen; + 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)); + SPrint(Entry->me.Title, 255, L"Boot legacy target %s", LegacyDescription); + Entry->me.Tag = TAG_LEGACY_UEFI; + Entry->me.Row = 0; + Entry->me.ShortcutLetter = ShortcutLetter; + Entry->me.Image = LoadOSIcon(L"legacy", L"legacy", TRUE); + 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; + + // create the submenu + SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN)); + 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)); + SubEntry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16)); + SPrint(SubEntry->me.Title, 255, L"Boot %s", LegacyDescription); + SubEntry->me.Tag = TAG_LEGACY_UEFI; + Entry->BdsOption = BdsOption; + AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry); + + AddMenuEntry(SubScreen, &MenuEntryReturn); + Entry->me.SubScreen = SubScreen; + AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry); + return Entry; +} /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */ + +/** + Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL. + In testing, protocol has not been implemented on Macs but has been + implemented on several Dell PCs and an ASUS motherboard. + Restricts output to disks of the specified DiskType. +*/ +static VOID ScanLegacyUEFI(IN UINTN DiskType) +{ + EFI_STATUS Status; + EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; + UINT16 *BootOrder = NULL; + 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; + + 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); + if (EFI_ERROR (Status)) + return; + + // Grab the boot order + BootOrder = BdsLibGetVariableAndSize(L"BootOrder", &gEfiGlobalVariableGuid, &BootOrderSize); + if (BootOrder == NULL) { + BootOrderSize = 0; + } + + Index = 0; + while (Index < BootOrderSize / sizeof (UINT16)) + { + // Grab each boot option variable from the boot order, and convert + // the variable into a BDS boot option + UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]); + BdsOption = BdsLibVariableToOption (&TempList, BootOption); + + 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); + } + } + Index++; + } +} /* static VOID ScanLegacyUEFI() */ +#endif // __MAKEWITH_GNUEFI + static VOID ScanLegacyVolume(REFIT_VOLUME *Volume, UINTN VolumeIndex) { UINTN VolumeIndex2; BOOLEAN ShowVolume, HideIfOthersFound; @@ -1246,11 +1529,15 @@ static VOID ScanLegacyDisc(VOID) UINTN VolumeIndex; REFIT_VOLUME *Volume; - for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) { - Volume = Volumes[VolumeIndex]; - if (Volume->DiskKind == DISK_KIND_OPTICAL) - ScanLegacyVolume(Volume, VolumeIndex); - } // for + if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) { + for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) { + Volume = Volumes[VolumeIndex]; + if (Volume->DiskKind == DISK_KIND_OPTICAL) + ScanLegacyVolume(Volume, VolumeIndex); + } // for + } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) { + ScanLegacyUEFI(BBS_CDROM); + } } /* static VOID ScanLegacyDisc() */ // Scan internal hard disks for legacy (BIOS) boot code @@ -1260,11 +1547,15 @@ static VOID ScanLegacyInternal(VOID) UINTN VolumeIndex; REFIT_VOLUME *Volume; - for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) { - Volume = Volumes[VolumeIndex]; - if (Volume->DiskKind == DISK_KIND_INTERNAL) - ScanLegacyVolume(Volume, VolumeIndex); - } // for + if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) { + for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) { + Volume = Volumes[VolumeIndex]; + if (Volume->DiskKind == DISK_KIND_INTERNAL) + ScanLegacyVolume(Volume, VolumeIndex); + } // for + } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) { + ScanLegacyUEFI(BBS_HARDDISK); + } } /* static VOID ScanLegacyInternal() */ // Scan external disks for legacy (BIOS) boot code @@ -1274,11 +1565,15 @@ static VOID ScanLegacyExternal(VOID) UINTN VolumeIndex; REFIT_VOLUME *Volume; - for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) { - Volume = Volumes[VolumeIndex]; - if (Volume->DiskKind == DISK_KIND_EXTERNAL) - ScanLegacyVolume(Volume, VolumeIndex); - } // for + if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) { + for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) { + Volume = Volumes[VolumeIndex]; + if (Volume->DiskKind == DISK_KIND_EXTERNAL) + ScanLegacyVolume(Volume, VolumeIndex); + } // for + } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) { + ScanLegacyUEFI(BBS_USB); + } } /* static VOID ScanLegacyExternal() */ // @@ -1289,11 +1584,11 @@ static VOID StartTool(IN LOADER_ENTRY *Entry) { BeginExternalScreen(Entry->UseGraphicsMode, Entry->me.Title + 6); // assumes "Start " as assigned below StartEFIImage(Entry->DevicePath, Entry->LoadOptions, Basename(Entry->LoaderPath), - Basename(Entry->LoaderPath), NULL, TRUE); + Basename(Entry->LoaderPath), Entry->OSType, NULL, TRUE); FinishExternalScreen(); } /* static VOID StartTool() */ -static LOADER_ENTRY * AddToolEntry(IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN EG_IMAGE *Image, +static LOADER_ENTRY * AddToolEntry(EFI_HANDLE DeviceHandle, IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN EG_IMAGE *Image, IN CHAR16 ShortcutLetter, IN BOOLEAN UseGraphicsMode) { LOADER_ENTRY *Entry; @@ -1308,8 +1603,8 @@ static LOADER_ENTRY * AddToolEntry(IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle Entry->me.Row = 1; Entry->me.ShortcutLetter = ShortcutLetter; Entry->me.Image = Image; - Entry->LoaderPath = StrDuplicate(LoaderPath); - Entry->DevicePath = FileDevicePath(SelfLoadedImage->DeviceHandle, Entry->LoaderPath); + Entry->LoaderPath = (LoaderPath) ? StrDuplicate(LoaderPath) : NULL; + Entry->DevicePath = FileDevicePath(DeviceHandle, Entry->LoaderPath); Entry->UseGraphicsMode = UseGraphicsMode; AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry); @@ -1338,7 +1633,7 @@ static UINTN ScanDriverDir(IN CHAR16 *Path) SPrint(FileName, 255, L"%s\\%s", Path, DirEntry->FileName); NumFound++; Status = StartEFIImage(FileDevicePath(SelfLoadedImage->DeviceHandle, FileName), - L"", DirEntry->FileName, DirEntry->FileName, NULL, FALSE); + L"", DirEntry->FileName, DirEntry->FileName, 0, NULL, FALSE); } Status = DirIterClose(&DirIter); if (Status != EFI_NOT_FOUND) { @@ -1408,12 +1703,12 @@ static EFI_STATUS ConnectAllDriversToAllControllers(VOID) } } - FreePool (HandleBuffer); - FreePool (HandleType); + MyFreePool (HandleBuffer); + MyFreePool (HandleType); } Done: - FreePool (AllHandleBuffer); + MyFreePool (AllHandleBuffer); return Status; } /* EFI_STATUS ConnectAllDriversToAllControllers() */ #else @@ -1434,12 +1729,12 @@ static VOID LoadDrivers(VOID) // load drivers from the subdirectories of rEFInd's home directory specified // in the DRIVER_DIRS constant. while ((Directory = FindCommaDelimited(DRIVER_DIRS, i++)) != NULL) { - SelfDirectory = StrDuplicate(SelfDirPath); + SelfDirectory = SelfDirPath ? StrDuplicate(SelfDirPath) : NULL; CleanUpPathNameSlashes(SelfDirectory); MergeStrings(&SelfDirectory, Directory, L'\\'); NumFound += ScanDriverDir(SelfDirectory); - FreePool(Directory); - FreePool(SelfDirectory); + MyFreePool(Directory); + MyFreePool(SelfDirectory); } // Scan additional user-specified driver directories.... @@ -1447,9 +1742,10 @@ static VOID LoadDrivers(VOID) while ((Directory = FindCommaDelimited(GlobalConfig.DriverDirs, i++)) != NULL) { CleanUpPathNameSlashes(Directory); Length = StrLen(Directory); - if (Length > 0) + if (Length > 0) { NumFound += ScanDriverDir(Directory); - FreePool(Directory); + } // if + MyFreePool(Directory); } // while // connect all devices @@ -1457,8 +1753,64 @@ static VOID LoadDrivers(VOID) ConnectAllDriversToAllControllers(); } /* static VOID LoadDrivers() */ +// Determine what (if any) type of legacy (BIOS) boot support is available +static VOID FindLegacyBootType(VOID) { +#ifdef __MAKEWITH_TIANO + EFI_STATUS Status; + EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; +#endif + + GlobalConfig.LegacyType = LEGACY_TYPE_NONE; + + // UEFI-style legacy BIOS support is available only with the TianoCore EDK2 + // build environment, and then only with some EFI implementations.... +#ifdef __MAKEWITH_TIANO + Status = gBS->LocateProtocol (&gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios); + if (!EFI_ERROR (Status)) + GlobalConfig.LegacyType = LEGACY_TYPE_UEFI; +#endif + + // Macs have their own system. If the firmware vendor code contains the + // string "Apple", assume it's available. Note that this overrides the + // UEFI type, and might yield false positives if the vendor string + // contains "Apple" as part of something bigger, so this isn't 100% + // perfect. + if (StriSubCmp(L"Apple", ST->FirmwareVendor)) + GlobalConfig.LegacyType = LEGACY_TYPE_MAC; +} // static VOID FindLegacyBootType + +// Warn the user if legacy OS scans are enabled but the firmware or this +// application can't support them.... +static VOID WarnIfLegacyProblems() { + BOOLEAN found = FALSE; + UINTN i = 0; + + if (GlobalConfig.LegacyType == LEGACY_TYPE_NONE) { + do { + if (GlobalConfig.ScanFor[i] == 'h' || GlobalConfig.ScanFor[i] == 'b' || GlobalConfig.ScanFor[i] == 'c') + found = TRUE; + i++; + } while ((i < NUM_SCAN_OPTIONS) && (!found)); + if (found) { + Print(L"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n"); + Print(L"(BIOS) boot options; however, this is not possible because "); +#ifdef __MAKEWITH_TIANO + Print(L"your computer lacks\n"); + Print(L"the necessary Compatibility Support Module (CSM) support.\n"); +#else + Print(L"this program was\n"); + Print(L"compiled without the necessary support. Please visit\n"); + Print(L"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n"); + Print(L"binary built with the TianoCore EDK2 to enable legacy boot support.\n"); +#endif + PauseForKey(); + } // if (found) + } // if no legacy support +} // static VOID WarnIfLegacyProblems() + +// Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly. static VOID ScanForBootloaders(VOID) { - UINTN i; + UINTN i; ScanVolumes(); @@ -1500,9 +1852,9 @@ 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; + CHAR16 *FileName = NULL, Description[256]; REFIT_MENU_ENTRY *TempMenuEntry; - UINTN i, j; + UINTN i, j, VolumeIndex; for (i = 0; i < NUM_TOOLS; i++) { switch(GlobalConfig.ShowTools[i]) { @@ -1528,23 +1880,56 @@ static VOID ScanForTools(VOID) { break; case TAG_SHELL: j = 0; + MyFreePool(FileName); while ((FileName = FindCommaDelimited(SHELL_NAMES, j++)) != NULL) { if (FileExists(SelfRootDir, FileName)) { - AddToolEntry(FileName, L"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL), 'S', FALSE); + AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL), + 'S', FALSE); } } // while break; case TAG_GPTSYNC: - MergeStrings(&FileName, L"\\efi\\tools\\gptsync.efi", 0); + MyFreePool(FileName); + FileName = StrDuplicate(L"\\efi\\tools\\gptsync.efi"); +// MergeStrings(&FileName, L"\\efi\\tools\\gptsync.efi", 0); if (FileExists(SelfRootDir, FileName)) { - AddToolEntry(FileName, L"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART), 'P', FALSE); + AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART), 'P', FALSE); + } + break; + 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); + AddToolEntry(Volumes[VolumeIndex]->DeviceHandle, FileName, Description, + BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE), 'R', TRUE); + } + } // for + 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")) { + 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); } break; } // switch() - if (FileName != NULL) { - FreePool(FileName); - FileName = NULL; - } + MyFreePool(FileName); + FileName = NULL; } // for } // static VOID ScanForTools @@ -1560,14 +1945,15 @@ VOID RescanAll(VOID) { FreeList((VOID ***) &(MainMenu.Entries), &MainMenu.EntryCount); MainMenu.Entries = NULL; MainMenu.EntryCount = 0; - ReadConfig(); + ReadConfig(CONFIG_FILE_NAME); ConnectAllDriversToAllControllers(); + ScanVolumes(); ScanForBootloaders(); ScanForTools(); SetupScreen(); } // VOID RescanAll() -#ifndef __MAKEWITH_GNUEFI +#ifdef __MAKEWITH_TIANO // Minimal initialization function static VOID InitializeLib(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) { @@ -1575,6 +1961,7 @@ static VOID InitializeLib(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *System // gImageHandle = ImageHandle; gBS = SystemTable->BootServices; // gRS = SystemTable->RuntimeServices; + gRT = SystemTable->RuntimeServices; // Some BDS functions need gRT to be set EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid, (VOID **) &gDS); InitializeConsoleSim(); @@ -1582,29 +1969,73 @@ 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; + UINTN MenuExit, i; CHAR16 *Selection; + EG_PIXEL BGColor; // bootstrap InitializeLib(ImageHandle, SystemTable); - InitScreen(); Status = InitRefitLib(ImageHandle); if (EFI_ERROR(Status)) return Status; // read configuration - CopyMem(GlobalConfig.ScanFor, "ieo ", NUM_SCAN_OPTIONS); - ReadConfig(); + CopyMem(GlobalConfig.ScanFor, "ieom ", NUM_SCAN_OPTIONS); + 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; // disable EFI watchdog timer @@ -1612,10 +2043,23 @@ efi_main (IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) // further bootstrap (now with config available) SetupScreen(); + MokProtocol = SecureBootSetup(); +// ScanVolumes(); LoadDrivers(); ScanForBootloaders(); ScanForTools(); + if (GlobalConfig.ScanDelay > 0) { + BGColor.b = 255; + BGColor.g = 175; + BGColor.r = 100; + BGColor.a = 0; + egDisplayMessage(L"Pausing before disk scan; please wait....", &BGColor); + for (i = 0; i < GlobalConfig.ScanDelay; i++) + refit_call1_wrapper(BS->Stall, 1000000); + RescanAll(); + } // if + Selection = StrDuplicate(GlobalConfig.DefaultSelection); while (MainLoopRunning) { MenuExit = RunMainMenu(&MainMenu, Selection, &ChosenEntry); @@ -1652,18 +2096,28 @@ efi_main (IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) StartLegacy((LEGACY_ENTRY *)ChosenEntry); break; +#ifdef __MAKEWITH_TIANO + case TAG_LEGACY_UEFI: // Boot a legacy OS on a non-Mac + StartLegacyUEFI((LEGACY_ENTRY *)ChosenEntry); + break; +#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() - FreePool(Selection); - Selection = StrDuplicate(ChosenEntry->Title); + MyFreePool(Selection); + Selection = (ChosenEntry->Title) ? StrDuplicate(ChosenEntry->Title) : NULL; } // while() // If we end up here, things have gone wrong. Try to reboot, and if that