From 6b837606b74deec086008deac66e1d249148f3b7 Mon Sep 17 00:00:00 2001 From: srs5694 Date: Sat, 6 Dec 2014 18:34:31 -0500 Subject: [PATCH] Support for iPXE (booting from a network server from rEFInd). --- BUILDING.txt | 22 ++++ CREDITS.txt | 4 + NEWS.txt | 9 ++ docs/refind/configfile.html | 12 +- docs/refind/installing.html | 34 ++++++ refind.conf-sample | 4 + refind/config.c | 15 ++- refind/global.h | 3 +- refind/icns.c | 2 + refind/icns.h | 10 +- refind/lib.c | 3 + refind/lib.h | 3 +- refind/main.c | 226 +++++++++++++++++++++++++++--------- 13 files changed, 279 insertions(+), 68 deletions(-) diff --git a/BUILDING.txt b/BUILDING.txt index df00b42..f9d889c 100644 --- a/BUILDING.txt +++ b/BUILDING.txt @@ -297,3 +297,25 @@ Christoph Pfisterer. Most of the drivers seem to have passed through Oracle's VirtualBox project (https://www.virtualbox.org) and the Clover boot loader project (https://sourceforge.net/projects/cloverefiboot/), which I used as the source for this build. + +Adding Support for Network Boot +=============================== + +rEFInd provides EXPERIMENTAL support for booting over the network using +iPXE (http://ipxe.org) as a means to receive the payload. In order to +enable this feature you'll want to follow these instructions: + +* cd net/ +* make source +* make netboot +* copy bin/ipxe.efi and bin/ipxe_discover.efi to the EFI volume at EFI/tools/ + +Note that you may need to install additional development packages, such as +libiberty-dev and binutils-dev, in addition to those needed to build rEFInd +itself. + +My own tests show this support to work under optimal conditions; however, +architecture (EFI vs. BIOS) detection may not work, and some computers will +hang or won't retrieve boot files from the network. For these reasons, this +support is disabled by default in rEFInd, and I do not provide iPXE +binaries. diff --git a/CREDITS.txt b/CREDITS.txt index 301cace..a04d1ab 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -46,6 +46,10 @@ Program (C source code) files: * Samuel Liao ported the GRUB 2 Btrfs code into an EFI driver and contributed it to this project. +* Emerson Barcelos (emerson_freitas@yahoo.com.br) wrote the code for + enabling Intel VMX support (the enable_and_lock_vmx token in + refind.conf). + * Matthew J. Garrett (mjg@redhat.com) wrote the shim boot loader upon which rEFInd relies for its Secure Boot functionality. I took a few shim functions to help out on the rEFInd side, too; see the mok/mok.c source diff --git a/NEWS.txt b/NEWS.txt index 5ef467f..0fb7031 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -12,6 +12,15 @@ work. Most EFIs enable setting this feature in their own setup utilities, but some (such as most Macs) don't. +- If rEFInd can't locate an icons directory (either the default or one + specified by the icons_dir token), the program switches to text-only + mode. + +- If a loader contains the string "grub" and no other clue to the loader's + OS association exists, search for os_grub.{png|icns} (which is not + provided with rEFInd) or os_linux.{png|icns}. (Previous versions provided + a generic loader icon for GRUB.) + 0.8.3 (7/6/2014): ----------------- diff --git a/docs/refind/configfile.html b/docs/refind/configfile.html index 5d47e93..a39174a 100644 --- a/docs/refind/configfile.html +++ b/docs/refind/configfile.html @@ -240,7 +240,7 @@ timeout 20 icons_dir directory name - Specifies a directory in which custom icons may be found. This directory should contain files with the same names as the files in the standard icons directory. The directory name is specified relative to the directory in which the rEFInd binary resides. The standard icons directory is searched if an icon can't be found in the one specified by icons_dir, so you can use this location to redefine just some icons. + Specifies a directory in which custom icons may be found. This directory should contain files with the same names as the files in the standard icons directory. The directory name is specified relative to the directory in which the rEFInd binary resides. The standard icons directory is searched if an icon can't be found in the one specified by icons_dir, so you can use this location to redefine just some icons. Note that if no icons directory is found (either icons or one specified by icons_dir), rEFInd switches to text-only mode, as if textonly had been specified. banner @@ -274,8 +274,8 @@ timeout 20 showtools - shell, memtest, gdisk, gptsync, apple_recovery, mok_tool, about, exit, shutdown, reboot, and firmware - Specifies which tool tags to display on the second row. shell launches an EFI shell, memtest (or memtest86) launches the Memtest86 program, gdisk launches the partitioning tool of the same name, gptsync launches a tool that creates a hybrid MBR, apple_recovery boots the OS X Recovery HD, windows_recovery boots a Windows recovery tool, mok_tool launches a tool to manage Machine Owner Keys (MOKs) on systems with Secure Boot active, about displays information about the program, exit terminates rEFInd, shutdown shuts down the computer (or reboots it, on some UEFI PCs), reboot reboots the computer, and firmware reboots the computer into the computer's own setup utility. The tags appear in the order in which you specify them. The default is shell, memtest, gdisk, apple_recovery, mok_tool, about, shutdown, reboot, firmware. Note that the shell, memtest, apple_recovery, and mok_tool options all require the presence of programs not included with rEFInd. The gptsync option requires use of a like-named program which, although it ships with rEFInd 0.6.9 and later, is not installed by default except under OS X. See the "Installing Additional Components" section of the Installing rEFInd page for pointers to the shell, Memtest86, and gptsync programs. The apple_recovery option will appear only if you've got an Apple Recovery HD partition (which has a boot loader called com.apple.recovery.boot/boot.efi). The firmware option works only on computers that support this option; on other computers, the option is quietly ignored. See the Secure Boot page for information on Secure Boot and MOK management. + shell, memtest, gdisk, gptsync, apple_recovery, mok_tool, netboot, about, exit, shutdown, reboot, and firmware + Specifies which tool tags to display on the second row. shell launches an EFI shell, memtest (or memtest86) launches the Memtest86 program, gdisk launches the partitioning tool of the same name, gptsync launches a tool that creates a hybrid MBR, apple_recovery boots the OS X Recovery HD, windows_recovery boots a Windows recovery tool, mok_tool launches a tool to manage Machine Owner Keys (MOKs) on systems with Secure Boot active, netboot launches the network boot tool (iPXE), about displays information about rEFInd, exit terminates rEFInd, shutdown shuts down the computer (or reboots it, on some UEFI PCs), reboot reboots the computer, and firmware reboots the computer into the computer's own setup utility. The tags appear in the order in which you specify them. The default is shell, memtest, gdisk, apple_recovery, mok_tool, about, shutdown, reboot, firmware. Note that the shell, memtest, apple_recovery, and mok_tool options all require the presence of programs not included with rEFInd. The gptsync option requires use of a like-named program which, although it ships with rEFInd 0.6.9 and later, is not installed by default except under OS X. See the "Installing Additional Components" section of the Installing rEFInd page for pointers to the shell, Memtest86, and gptsync programs. The apple_recovery option will appear only if you've got an Apple Recovery HD partition (which has a boot loader called com.apple.recovery.boot/boot.efi). The firmware option works only on computers that support this option; on other computers, the option is quietly ignored. See the Secure Boot page for information on Secure Boot and MOK management. font @@ -285,7 +285,7 @@ timeout 20 textonly none or one of true, on, 1, false, off, or 0 - rEFInd defaults to a graphical mode; however, if you prefer to do without the flashy graphics, you can run it in text mode by including this option (alone or with true, on, or 1). Passing false, off, or 0 causes graphics mode to be used. (This could be useful if you want to override a text-mode setting in an included secondary configuration file.) + rEFInd defaults to a graphical mode; however, if you prefer to do without the flashy graphics, you can run it in text mode by including this option (alone or with true, on, or 1). Passing false, off, or 0 causes graphics mode to be used. (This could be useful if you want to override a text-mode setting in an included secondary configuration file.) Text-only mode is implicitly set if rEFInd cannot find either a subdirectory called icons or a subdirectory named by icons_dir. textmode @@ -309,8 +309,8 @@ timeout 20 scanfor - internal, external, optical, hdbios, biosexternal, cd, and manual - Tells rEFInd what methods to use to locate boot loaders. The internal, external, and optical parameters tell rEFInd to scan for EFI boot loaders on internal, external, and optical (CD, DVD, and Blu-ray) devices, respectively. The hdbios, biosexternal, and cd parameters are similar, but scan for BIOS boot loaders. (Note that the BIOS options scan more thoroughly and actively on Macs than on UEFI-based PCs; for the latter, only options in the firmware's boot list are scanned, as described on the Using rEFInd page.) The manual parameter tells rEFInd to scan the configuration file for manual settings. You can specify multiple parameters to have the program scan for multiple boot loader types. When you do so, the order determines the order in which the boot loaders appear in the menu. The default is internal, external, optical, manual on most systems, but internal, hdbios, external, biosexternal, optical, cd, manual on Macs. + internal, external, optical, netboot, hdbios, biosexternal, cd, and manual + Tells rEFInd what methods to use to locate boot loaders. The internal, external, and optical parameters tell rEFInd to scan for EFI boot loaders on internal, external, and optical (CD, DVD, and Blu-ray) devices, respectively. The netboot option relies on the presence of the ipxe.efi and ipxe_discover.efi program files in the EFI/tools directory to assist with network (Preboot Execution Environment, or PXE) booting. Note that netboot is experimental. See the BUILDING.txt file for information on building the necessary binaries. The hdbios, biosexternal, and cd parameters are similar, but scan for BIOS boot loaders. (Note that the BIOS options scan more thoroughly and actively on Macs than on UEFI-based PCs; for the latter, only options in the firmware's boot list are scanned, as described on the Using rEFInd page.) The manual parameter tells rEFInd to scan the configuration file for manual settings. You can specify multiple parameters to have the program scan for multiple boot loader types. When you do so, the order determines the order in which the boot loaders appear in the menu. The default is internal, external, optical, manual on most systems, but internal, hdbios, external, biosexternal, optical, cd, manual on Macs. uefi_deep_legacy_scan diff --git a/docs/refind/installing.html b/docs/refind/installing.html index ec81622..f85f939 100644 --- a/docs/refind/installing.html +++ b/docs/refind/installing.html @@ -198,6 +198,8 @@ href="mailto:rodsmith@rodsbooks.com">rodsmith@rodsbooks.com

+
  • Fixing Windows Boot Problems
  • +
  • Uninstalling rEFInd
  • @@ -1003,6 +1005,38 @@ $ ioreg -l -p IODeviceTree | grep firmware-abi

    Unfortunately, I lack a recent Mac and so can't investigate these issues myself, so I'm dependent upon others (mostly non-programmers) to offer workarounds. This is the type of problem that really requires hands-on interactive debugging sessions with the code to stand any chance of finding a better solution.

    + +

    Fixing Windows Boot Problems

    +
    + +

    Most Windows boot problems are best addressed on Windows-specific sites, so I recommend you make the rounds of Windows forums to solve such problems. There is one that deserves mention here, though: If you accidentally erase the Windows boot loader file, EFI/Microsoft/Boot/bootmgfw.efi, you won't be able to boot Windows. The simplest solution is to restore this file from a backup you prepared ahead of time. If you don't have such a backup, though, you can restore it as follows:

    + +
      + +
    1. Boot from an emergency Windows recovery disk. If you don't have one, you can prepare one from a working Windows system as described here.
    2. + +
    3. Type diskpart to enter the Windows disk-partitioning tool.
    4. + +
    5. In diskpart, type sel disk 0 followed by list vol. You should see a set of partitions. This step is intended to help you identify your ESP, which will probably be the only FAT32 partition on the disk. (If you have multiple disks, you may need to try again with sel disk 1 or higher.) Note the volume number of your ESP.
    6. + +
    7. Type sel vol 1, changing 1 to whatever the ESP's volume number is.
    8. + +
    9. Type assign letter=S: to assign the ESP a Windows disk identifier of S:. (You can use another letter if you prefer.)
    10. + +
    11. Type exit to exit from diskutil.
    12. + +
    13. Type cd /d s:\EFI\Microsoft\Boot\ to change into the Windows boot loader directory. (If this directory doesn't exist, you may need to create it first with mkdir. If rEFInd or some other boot loader occupies this directory, back it up first.
    14. + +
    15. Type bootrec /fixboot.
    16. + +
    17. Type bcdboot c:\Windows /s s: /f ALL. Note that this command should set the Windows boot loader as the default. Omit /f ALL if you don't want to adjust the EFI's default boot program.
    18. + +
    19. Reboot and hope it works! If the computer boots straight to Windows and you want to use rEFInd, use bcdedit in Windows, as described in step 9 of the Installing rEFInd Manually Using Windows section of this page.
    20. + +
    + +

    For more information, see this SuperUser question and answer.

    +

    Uninstalling rEFInd

    diff --git a/refind.conf-sample b/refind.conf-sample index ab7cd66..193a75d 100644 --- a/refind.conf-sample +++ b/refind.conf-sample @@ -174,6 +174,7 @@ timeout 20 # reboot - a tag to reboot the computer # firmware - a tag to reboot the computer into the firmware's # user interface (ignored on older computers) +# netboot - launch the ipxe.efi tool for network (PXE) booting # Default is shell,memtest,gdisk,apple_recovery,windows_recovery,mok_tool,about,shutdown,reboot,firmware # #showtools shell, gdisk, memtest, mok_tool, about, reboot, exit, firmware @@ -200,12 +201,15 @@ timeout 20 # internal - internal EFI disk-based boot loaders # external - external EFI disk-based boot loaders # optical - EFI optical discs (CD, DVD, etc.) +# netboot - EFI network (PXE) boot options # hdbios - BIOS disk-based boot loaders # biosexternal - BIOS external boot loaders (USB, eSATA, etc.) # cd - BIOS optical-disc boot loaders # manual - use stanzas later in this configuration file # Note that the legacy BIOS options require firmware support, which is # not present on all computers. +# The netboot option is experimental and relies on the ipxe.efi and +# ipxe_discover.efi program files. # On UEFI PCs, default is internal,external,optical,manual # On Macs, default is internal,hdbios,external,biosexternal,optical,cd,manual # diff --git a/refind/config.c b/refind/config.c index ba0c06b..9e5016b 100644 --- a/refind/config.c +++ b/refind/config.c @@ -475,8 +475,12 @@ VOID ReadConfig(CHAR16 *FileName) } // if if (!FileExists(SelfDir, FileName)) { - Print(L"Configuration file '%s' missing!\n", FileName); - return; + Print(L"Configuration file '%s' missing!\n", FileName); + if (!FileExists(SelfDir, L"icons")) { + Print(L"Icons directory doesn't exist; setting textonly = TRUE!\n"); + GlobalConfig.TextOnly = TRUE; + } + return; } Status = ReadFile(SelfDir, FileName, &File, &i); @@ -585,6 +589,8 @@ VOID ReadConfig(CHAR16 *FileName) GlobalConfig.ShowTools[i - 1] = TAG_FIRMWARE; } else if ((StriCmp(FlagName, L"memtest86") == 0) || (StriCmp(FlagName, L"memtest") == 0)) { GlobalConfig.ShowTools[i - 1] = TAG_MEMTEST; + } else if (StriCmp(FlagName, L"netboot") == 0) { + GlobalConfig.ShowTools[i - 1] = TAG_NETBOOT; } else { Print(L" unknown showtools flag: '%s'\n", FlagName); } @@ -685,6 +691,11 @@ VOID ReadConfig(CHAR16 *FileName) if ((GlobalConfig.DontScanFiles) && (GlobalConfig.WindowsRecoveryFiles)) MergeStrings(&(GlobalConfig.DontScanFiles), GlobalConfig.WindowsRecoveryFiles, L','); MyFreePool(File.Buffer); + + if (!FileExists(SelfDir, L"icons") && !FileExists(SelfDir, GlobalConfig.IconsDir)) { + Print(L"Icons directory doesn't exist; setting textonly = TRUE!\n"); + GlobalConfig.TextOnly = TRUE; + } } /* VOID ReadConfig() */ // Finds a volume with the specified Identifier (a filesystem label, a diff --git a/refind/global.h b/refind/global.h index fc43038..ea1f71f 100644 --- a/refind/global.h +++ b/refind/global.h @@ -74,7 +74,8 @@ #define TAG_FIRMWARE (14) #define TAG_MEMTEST (15) #define TAG_GDISK (16) -#define NUM_TOOLS (17) +#define TAG_NETBOOT (17) +#define NUM_TOOLS (18) #define NUM_SCAN_OPTIONS 10 diff --git a/refind/icns.c b/refind/icns.c index 53a94ef..5438afd 100644 --- a/refind/icns.c +++ b/refind/icns.c @@ -63,9 +63,11 @@ BUILTIN_ICON BuiltinIconTable[BUILTIN_ICON_COUNT] = { { NULL, L"tool_windows_rescue", ICON_SIZE_SMALL }, { NULL, L"tool_mok_tool", ICON_SIZE_SMALL }, { NULL, L"tool_memtest", ICON_SIZE_SMALL }, + { NULL, L"tool_netboot", ICON_SIZE_SMALL }, { NULL, L"vol_internal", ICON_SIZE_BADGE }, { NULL, L"vol_external", ICON_SIZE_BADGE }, { NULL, L"vol_optical", ICON_SIZE_BADGE }, + { NULL, L"vol_net", ICON_SIZE_BADGE }, }; EG_IMAGE * BuiltinIcon(IN UINTN Id) diff --git a/refind/icns.h b/refind/icns.h index 25b545e..b1c8fdb 100644 --- a/refind/icns.h +++ b/refind/icns.h @@ -67,10 +67,12 @@ EG_IMAGE * BuiltinIcon(IN UINTN Id); #define BUILTIN_ICON_TOOL_WINDOWS_RESCUE (9) #define BUILTIN_ICON_TOOL_MOK_TOOL (10) #define BUILTIN_ICON_TOOL_MEMTEST (11) -#define BUILTIN_ICON_VOL_INTERNAL (12) -#define BUILTIN_ICON_VOL_EXTERNAL (13) -#define BUILTIN_ICON_VOL_OPTICAL (14) -#define BUILTIN_ICON_COUNT (15) +#define BUILTIN_ICON_TOOL_NETBOOT (12) +#define BUILTIN_ICON_VOL_INTERNAL (13) +#define BUILTIN_ICON_VOL_EXTERNAL (14) +#define BUILTIN_ICON_VOL_OPTICAL (15) +#define BUILTIN_ICON_VOL_NET (16) +#define BUILTIN_ICON_COUNT (17) #endif diff --git a/refind/lib.c b/refind/lib.c index 893f62c..1a7bb81 100644 --- a/refind/lib.c +++ b/refind/lib.c @@ -713,6 +713,9 @@ VOID SetVolumeBadgeIcon(REFIT_VOLUME *Volume) case DISK_KIND_OPTICAL: Volume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL); break; + case DISK_KIND_NET: + Volume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_NET); + break; } // switch() } } // VOID SetVolumeBadgeIcon() diff --git a/refind/lib.h b/refind/lib.h index e6a8c30..6a106ee 100644 --- a/refind/lib.h +++ b/refind/lib.h @@ -73,6 +73,7 @@ typedef struct { #define DISK_KIND_INTERNAL (0) #define DISK_KIND_EXTERNAL (1) #define DISK_KIND_OPTICAL (2) +#define DISK_KIND_NET (3) #define VOL_UNREADABLE 999 @@ -95,10 +96,10 @@ VOID FreeList(IN OUT VOID ***ListPtr, IN OUT UINTN *ElementCount); VOID ExtractLegacyLoaderPaths(EFI_DEVICE_PATH **PathList, UINTN MaxPaths, EFI_DEVICE_PATH **HardcodedPathList); +VOID SetVolumeBadgeIcon(REFIT_VOLUME *Volume); VOID ScanVolumes(VOID); BOOLEAN FileExists(IN EFI_FILE *BaseDir, IN CHAR16 *RelativePath); -BOOLEAN DirectoryExists(IN EFI_FILE *BaseDir, IN CHAR16 *RelativePath); EFI_STATUS DirNextEntry(IN EFI_FILE *Directory, IN OUT EFI_FILE_INFO **DirEntry, IN UINTN FilterMode); diff --git a/refind/main.c b/refind/main.c index e83940b..e3e009d 100644 --- a/refind/main.c +++ b/refind/main.c @@ -81,6 +81,7 @@ #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 NETBOOT_NAMES L"\\EFI\\tools\\ipxe.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" @@ -90,6 +91,7 @@ #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 NETBOOT_NAMES L"\\EFI\\tools\\ipxe.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" @@ -99,6 +101,7 @@ #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 NETBOOT_NAMES L"\\EFI\\tools\\ipxe.efi" #define MEMTEST_NAMES L"memtest86.efi" #define DRIVER_DIRS L"drivers" #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */ @@ -106,6 +109,9 @@ #endif #define FAT_ARCH 0x0ef1fab9 /* ID for Apple "fat" binary */ +#define IPXE_DISCOVER_NAME L"\\efi\\tools\\ipxe_discover.efi" +#define IPXE_NAME L"\\efi\\tools\\ipxe.efi" + // 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 @@ -143,7 +149,7 @@ REFIT_CONFIG GlobalConfig = { FALSE, TRUE, FALSE, FALSE, 0, 0, 0, DONT_CHANGE_TE 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 } + TAG_ABOUT, TAG_SHUTDOWN, TAG_REBOOT, TAG_FIRMWARE, 0, 0, 0, 0, 0, 0, 0, 0 } }; EFI_GUID GlobalGuid = EFI_GLOBAL_VARIABLE; @@ -169,7 +175,7 @@ static VOID AboutrEFInd(VOID) if (AboutMenu.EntryCount == 0) { AboutMenu.TitleImage = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT); - AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.8.3.2"); + AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.8.3.4"); AddMenuInfoLine(&AboutMenu, L""); AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2006-2010 Christoph Pfisterer"); AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2012-2014 Roderick W. Smith"); @@ -451,7 +457,7 @@ static VOID DoEnableAndLockVMX(VOID) msr = 0x3a; __asm__ volatile ("wrmsr" : : "c" (msr), "a" (low_bits), "d" (high_bits)); } -} // VOID DoEnableAndLockVMX +} // VOID DoEnableAndLockVMX() static VOID StartLoader(LOADER_ENTRY *Entry, CHAR16 *SelectionName) { @@ -476,7 +482,7 @@ static VOID StartLoader(LOADER_ENTRY *Entry, CHAR16 *SelectionName) // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match; // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it -// finds). Thus, care should be taken to avoid placing duplicate matching files in +// finds.) Thus, care should be taken to avoid placing duplicate matching files in // the kernel's directory. // If no matching init file can be found, returns NULL. static CHAR16 * FindInitrd(IN CHAR16 *LoaderPath, IN REFIT_VOLUME *Volume) { @@ -912,61 +918,67 @@ static VOID GuessLinuxDistribution(CHAR16 **OSIconName, REFIT_VOLUME *Volume, CH // 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 *FileName, *PathOnly, *NoExtension, *OSIconName = NULL, *Temp, *SubString; + CHAR16 *NameClues, *PathOnly, *NoExtension, *OSIconName = NULL, *Temp, *SubString; CHAR16 ShortcutLetter = 0; UINTN i = 0, Length; - FileName = Basename(LoaderPath); + NameClues = Basename(LoaderPath); PathOnly = FindPath(LoaderPath); - NoExtension = StripEfiExtension(FileName); + NoExtension = StripEfiExtension(NameClues); - // 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, GlobalConfig.IconSizes[ICON_SIZE_BIG]); - } - if (!Entry->me.Image) { - Entry->me.Image = egCopyImage(Volume->VolIconImage); - } + if (Volume->DiskKind == DISK_KIND_NET) { + MergeStrings(&NameClues, Entry->me.Title, L' '); + } else { + // 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, 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 - Temp = FindLastDirName(LoaderPath); - MergeStrings(&OSIconName, Temp, L','); - MyFreePool(Temp); - Temp = NULL; - if (OSIconName != NULL) { - ShortcutLetter = OSIconName[0]; - } + // Begin creating icon "hints" by using last part of directory path leading + // to the loader + Temp = FindLastDirName(LoaderPath); + MergeStrings(&OSIconName, Temp, L','); + 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); + // 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 - } // if + } // if/else network boot // detect specific loaders - if (StriSubCmp(L"bzImage", FileName) || StriSubCmp(L"vmlinuz", FileName)) { - GuessLinuxDistribution(&OSIconName, Volume, LoaderPath); + if (StriSubCmp(L"bzImage", NameClues) || StriSubCmp(L"vmlinuz", NameClues)) { + if (Volume->DiskKind != DISK_KIND_NET) { + GuessLinuxDistribution(&OSIconName, Volume, LoaderPath); + Entry->LoadOptions = GetMainLinuxOptions(LoaderPath, Volume); + } MergeStrings(&OSIconName, L"linux", L','); Entry->OSType = 'L'; if (ShortcutLetter == 0) ShortcutLetter = 'L'; - Entry->LoadOptions = GetMainLinuxOptions(LoaderPath, Volume); Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_LINUX; } else if (StriSubCmp(L"refit", LoaderPath)) { MergeStrings(&OSIconName, L"refit", L','); @@ -981,33 +993,39 @@ VOID SetLoaderDefaults(LOADER_ENTRY *Entry, CHAR16 *LoaderPath, REFIT_VOLUME *Vo Entry->OSType = 'M'; ShortcutLetter = 'M'; Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_OSX; - } else if (StriCmp(FileName, L"diags.efi") == 0) { + } else if (StriCmp(NameClues, L"diags.efi") == 0) { MergeStrings(&OSIconName, L"hwtest", L','); - } else if (StriCmp(FileName, L"e.efi") == 0 || StriCmp(FileName, L"elilo.efi") == 0 || StriSubCmp(L"elilo", FileName)) { + } else if (StriCmp(NameClues, L"e.efi") == 0 || StriCmp(NameClues, L"elilo.efi") == 0 || StriSubCmp(L"elilo", NameClues)) { MergeStrings(&OSIconName, L"elilo,linux", L','); Entry->OSType = 'E'; if (ShortcutLetter == 0) ShortcutLetter = 'L'; Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO; - } else if (StriSubCmp(L"grub", FileName)) { + } else if (StriSubCmp(L"grub", NameClues)) { + MergeStrings(&OSIconName, L"grub,linux", L','); Entry->OSType = 'G'; ShortcutLetter = 'G'; 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"bkpbootmgfw.efi") == 0) { + } else if (StriCmp(NameClues, L"cdboot.efi") == 0 || + StriCmp(NameClues, L"bootmgr.efi") == 0 || + StriCmp(NameClues, L"bootmgfw.efi") == 0 || + StriCmp(NameClues, L"bkpbootmgfw.efi") == 0) { MergeStrings(&OSIconName, L"win", L','); Entry->OSType = 'W'; ShortcutLetter = 'W'; Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS; - } else if (StriCmp(FileName, L"xom.efi") == 0) { + } else if (StriCmp(NameClues, L"xom.efi") == 0) { MergeStrings(&OSIconName, L"xom,win", L','); Entry->UseGraphicsMode = TRUE; Entry->OSType = 'X'; ShortcutLetter = 'W'; Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS; } + else if (StriSubCmp(L"ipxe", NameClues)) { + Entry->OSType = 'N'; + ShortcutLetter = 'N'; + MergeStrings(&OSIconName, L"network", L','); + } if ((ShortcutLetter >= 'a') && (ShortcutLetter <= 'z')) ShortcutLetter = ShortcutLetter - 'a' + 'A'; // convert lowercase to uppercase @@ -1017,6 +1035,38 @@ VOID SetLoaderDefaults(LOADER_ENTRY *Entry, CHAR16 *LoaderPath, REFIT_VOLUME *Vo MyFreePool(PathOnly); } // VOID SetLoaderDefaults() +// // Add a network (PXE) EFI boot loader to the list, using automatic settings +// // for icons, options, etc. +// LOADER_ENTRY * AddNetbootLoaderEntry(IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume) { +// LOADER_ENTRY *Entry; +// +// Print(L"Adding iPXE entry for '%s'\n", LoaderTitle); +// PauseForKey(); +// CleanUpPathNameSlashes(LoaderPath); +// Entry = InitializeLoaderEntry(NULL); +// if (Entry != NULL) { +// Entry->Title = StrDuplicate((LoaderTitle != NULL) ? LoaderTitle : LoaderPath); +// Entry->me.Title = AllocateZeroPool(sizeof(CHAR16) * 256); +// // Extra space at end of Entry->me.Title enables searching on Volume->VolName even if another volume +// // name is identical except for something added to the end (e.g., VolB1 vs. VolB12). +// SPrint(Entry->me.Title, 255, L"NetBoot %s", (LoaderTitle != NULL) ? LoaderTitle : LoaderPath); +// Entry->me.Row = 0; +// if ((LoaderPath != NULL) && (LoaderPath[0] != L'\\')) { +// Entry->LoaderPath = StrDuplicate(L"\\"); +// } else { +// Entry->LoaderPath = NULL; +// } +// MergeStrings(&(Entry->LoaderPath), LoaderPath, 0); +// Entry->VolName = Volume->VolName; +// Entry->DevicePath = FileDevicePath(Volume->DeviceHandle, Entry->LoaderPath); +// SetLoaderDefaults(Entry, LoaderPath, Volume); +// GenerateSubScreen(Entry, Volume); +// AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry); +// } +// +// return(Entry); +// } // LOADER_ENTRY * AddLoaderEntry() + // Add a specified EFI boot loader to the list, using automatic settings // for icons, options, etc. LOADER_ENTRY * AddLoaderEntry(IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume) { @@ -1029,7 +1079,11 @@ LOADER_ENTRY * AddLoaderEntry(IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN Entry->me.Title = AllocateZeroPool(sizeof(CHAR16) * 256); // Extra space at end of Entry->me.Title enables searching on Volume->VolName even if another volume // name is identical except for something added to the end (e.g., VolB1 vs. VolB12). - SPrint(Entry->me.Title, 255, L"Boot %s from %s ", (LoaderTitle != NULL) ? LoaderTitle : LoaderPath, Volume->VolName); + // Note: Volume->VolName will be NULL for network boot programs. + if (Volume->VolName) + SPrint(Entry->me.Title, 255, L"Boot %s from %s ", (LoaderTitle != NULL) ? LoaderTitle : LoaderPath, Volume->VolName); + else + SPrint(Entry->me.Title, 255, L"Boot %s ", (LoaderTitle != NULL) ? LoaderTitle : LoaderPath); Entry->me.Row = 0; Entry->me.BadgeImage = Volume->VolBadgeImage; if ((LoaderPath != NULL) && (LoaderPath[0] != L'\\')) { @@ -1345,6 +1399,55 @@ static BOOLEAN ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 return FoundFallbackDuplicate; } /* static VOID ScanLoaderDir() */ +// Run the IPXE_DISCOVER_NAME program, which obtains the IP address of the boot +// server and the name of the boot file it delivers. +CHAR16* RuniPXEDiscover(EFI_HANDLE Volume) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH *FilePath; + EFI_HANDLE iPXEHandle; + CHAR16 *boot_info; + UINTN boot_info_size = 256 * sizeof(CHAR16); + + FilePath = FileDevicePath (Volume, IPXE_DISCOVER_NAME); + Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, FilePath, + NULL, 0, &iPXEHandle); + if (Status != 0) { + return NULL; + } // if + + boot_info = AllocatePool(256 * sizeof(CHAR16)); + Status = refit_call3_wrapper(BS->StartImage, iPXEHandle, &boot_info_size, &boot_info); + + return boot_info; +} // RuniPXEDiscover() + +// Scan for network (PXE) boot servers. This function relies on the presence +// of the IPXE_DISCOVER_NAME and IPXE_NAME program files on the volume from +// which rEFInd launched. As of December 6, 2014, these tools aren't entirely +// reliable. See BUILDING.txt for information on building them. +static VOID ScanNetboot() { + CHAR16 *iPXEFileName = IPXE_NAME; + CHAR16 *Location; + REFIT_VOLUME *NetVolume; + + if (FileExists(SelfVolume->RootDir, IPXE_DISCOVER_NAME) && + FileExists(SelfVolume->RootDir, IPXE_NAME) && + IsValidLoader(SelfVolume->RootDir, IPXE_DISCOVER_NAME) && + IsValidLoader(SelfVolume->RootDir, IPXE_NAME)) { + Location = RuniPXEDiscover(SelfVolume->DeviceHandle); + if (Location != NULL && FileExists(SelfVolume->RootDir, iPXEFileName)) { + NetVolume = AllocatePool(sizeof(REFIT_VOLUME)); + NetVolume = CopyMem(NetVolume, SelfVolume, sizeof(REFIT_VOLUME)); + NetVolume->DiskKind = DISK_KIND_NET; + NetVolume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_NET); + NetVolume->PartName = NetVolume->VolName = NULL; + AddLoaderEntry(iPXEFileName, Location, NetVolume); + MyFreePool(NetVolume); + } // if support files exist and are valid + } +} // VOID ScanNetBoot() + static VOID ScanEfiFiles(REFIT_VOLUME *Volume) { EFI_STATUS Status; REFIT_DIR_ITER EfiDirIter; @@ -2256,6 +2359,9 @@ static VOID ScanForBootloaders(VOID) { case 'o': case 'O': ScanOptical(); break; + case 'n': case 'N': + ScanNetboot(); + break; } // switch() } // for @@ -2376,6 +2482,18 @@ static VOID ScanForTools(VOID) { } // while FileName = NULL; break; + + case TAG_NETBOOT: + j = 0; + while ((FileName = FindCommaDelimited(NETBOOT_NAMES, j++)) != NULL) { + if (FileExists(SelfRootDir, FileName)) { + AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"Netboot", + BuiltinIcon(BUILTIN_ICON_TOOL_NETBOOT), 'N', FALSE); + } // if + MyFreePool(FileName); + } // while + FileName = NULL; + break; case TAG_APPLE_RECOVERY: FileName = StrDuplicate(L"\\com.apple.recovery.boot\\boot.efi"); -- 2.39.2